From 099372d76c411c598285d637bd85c9b2dbc40948 Mon Sep 17 00:00:00 2001 From: David Schneiderbauer Date: Tue, 15 May 2018 12:07:32 +0200 Subject: [PATCH 1/2] Refactor User Settings (#3900) * moved avatar to profile page * combined password change, email and account deletion into account settings page * combined totp, access tokens, linked accounts and openid into security settings page * move access tokens to applications settings page * small change to restart drone build * fix change avatar url on profile page * redirect old settings urls to new ones * enforce only one autofocus attribute on settings pages * set correct redirect status code * fmt fix --- integrations/delete_user_test.go | 8 +- integrations/links_test.go | 7 +- options/locale/locale_en-US.ini | 3 +- routers/routes/routes.go | 66 +-- routers/user/setting.go | 379 +++++++++--------- routers/user/setting_openid.go | 47 +-- routers/user/setting_test.go | 2 +- templates/user/profile.tmpl | 2 +- templates/user/settings/account.tmpl | 135 +++++++ templates/user/settings/account_link.tmpl | 44 -- templates/user/settings/applications.tmpl | 33 +- templates/user/settings/avatar.tmpl | 46 --- templates/user/settings/delete.tmpl | 41 -- templates/user/settings/email.tmpl | 66 --- templates/user/settings/navbar.tmpl | 22 +- templates/user/settings/openid.tmpl | 71 ---- templates/user/settings/organization.tmpl | 2 +- templates/user/settings/profile.tmpl | 37 ++ templates/user/settings/repos.tmpl | 2 +- templates/user/settings/security.tmpl | 77 +--- .../user/settings/security_accountlinks.tmpl | 36 ++ templates/user/settings/security_openid.tmpl | 63 +++ templates/user/settings/security_twofa.tmpl | 35 ++ templates/user/settings/twofa.tmpl | 44 -- templates/user/settings/twofa_enroll.tmpl | 2 +- 25 files changed, 582 insertions(+), 688 deletions(-) create mode 100644 templates/user/settings/account.tmpl delete mode 100644 templates/user/settings/account_link.tmpl delete mode 100644 templates/user/settings/avatar.tmpl delete mode 100644 templates/user/settings/delete.tmpl delete mode 100644 templates/user/settings/email.tmpl delete mode 100644 templates/user/settings/openid.tmpl create mode 100644 templates/user/settings/security_accountlinks.tmpl create mode 100644 templates/user/settings/security_openid.tmpl create mode 100644 templates/user/settings/security_twofa.tmpl delete mode 100644 templates/user/settings/twofa.tmpl diff --git a/integrations/delete_user_test.go b/integrations/delete_user_test.go index a6e44c15c0..2a5ca89860 100644 --- a/integrations/delete_user_test.go +++ b/integrations/delete_user_test.go @@ -43,8 +43,8 @@ func TestUserDeleteAccount(t *testing.T) { prepareTestEnv(t) session := loginUser(t, "user8") - csrf := GetCSRF(t, session, "/user/settings/delete") - urlStr := fmt.Sprintf("/user/settings/delete?password=%s", userPassword) + csrf := GetCSRF(t, session, "/user/settings/account") + urlStr := fmt.Sprintf("/user/settings/account/delete?password=%s", userPassword) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "_csrf": csrf, }) @@ -58,8 +58,8 @@ func TestUserDeleteAccountStillOwnRepos(t *testing.T) { prepareTestEnv(t) session := loginUser(t, "user2") - csrf := GetCSRF(t, session, "/user/settings/delete") - urlStr := fmt.Sprintf("/user/settings/delete?password=%s", userPassword) + csrf := GetCSRF(t, session, "/user/settings/account") + urlStr := fmt.Sprintf("/user/settings/account/delete?password=%s", userPassword) req := NewRequestWithValues(t, "POST", urlStr, map[string]string{ "_csrf": csrf, }) diff --git a/integrations/links_test.go b/integrations/links_test.go index b0abbd7089..803d992055 100644 --- a/integrations/links_test.go +++ b/integrations/links_test.go @@ -93,15 +93,12 @@ func testLinksAsUser(userName string, t *testing.T) { "/user2?tab=stars", "/user2?tab=activity", "/user/settings", - "/user/settings/avatar", + "/user/settings/account", "/user/settings/security", "/user/settings/security/two_factor/enroll", - "/user/settings/email", "/user/settings/keys", - "/user/settings/applications", - "/user/settings/account_link", "/user/settings/organization", - "/user/settings/delete", + "/user/settings/repos", } session := loginUser(t, userName) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3082569bf5..8ef0056608 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -306,12 +306,13 @@ form.name_pattern_not_allowed = The pattern '%s' is not allowed in a username. [settings] profile = Profile +account = Account password = Password security = Security avatar = Avatar ssh_gpg_keys = SSH / GPG Keys social = Social Accounts -applications = Access Tokens +applications = Applications orgs = Manage Organizations repos = Repositories delete = Delete Account diff --git a/routers/routes/routes.go b/routers/routes/routes.go index 9618d25268..40b5f4bfb3 100644 --- a/routers/routes/routes.go +++ b/routers/routes/routes.go @@ -37,6 +37,7 @@ import ( "github.com/go-macaron/session" "github.com/go-macaron/toolbox" "gopkg.in/macaron.v1" + "net/http" ) // NewMacaron initializes Macaron instance. @@ -217,35 +218,54 @@ func RegisterRoutes(m *macaron.Macaron) { m.Group("/user/settings", func() { m.Get("", user.Settings) m.Post("", bindIgnErr(auth.UpdateProfileForm{}), user.SettingsPost) - m.Combo("/avatar").Get(user.SettingsAvatar). - Post(binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost) + m.Post("/avatar", binding.MultipartForm(auth.AvatarForm{}), user.SettingsAvatarPost) m.Post("/avatar/delete", user.SettingsDeleteAvatar) - m.Combo("/email").Get(user.SettingsEmails). - Post(bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) - m.Post("/email/delete", user.DeleteEmail) - m.Get("/security", user.SettingsSecurity) - m.Post("/security", bindIgnErr(auth.ChangePasswordForm{}), user.SettingsSecurityPost) - m.Group("/openid", func() { - m.Combo("").Get(user.SettingsOpenID). - Post(bindIgnErr(auth.AddOpenIDForm{}), user.SettingsOpenIDPost) - m.Post("/delete", user.DeleteOpenID) - m.Post("/toggle_visibility", user.ToggleOpenIDVisibility) - }, openIDSignInEnabled) - m.Combo("/keys").Get(user.SettingsKeys). - Post(bindIgnErr(auth.AddKeyForm{}), user.SettingsKeysPost) - m.Post("/keys/delete", user.DeleteKey) + m.Group("/account", func() { + m.Combo("").Get(user.SettingsAccount).Post(bindIgnErr(auth.ChangePasswordForm{}), user.SettingsAccountPost) + m.Post("/email", bindIgnErr(auth.AddEmailForm{}), user.SettingsEmailPost) + m.Post("/email/delete", user.DeleteEmail) + m.Post("/delete", user.SettingsDelete) + }) + m.Group("/security", func() { + m.Get("", user.SettingsSecurity) + m.Group("/two_factor", func() { + m.Post("/regenerate_scratch", user.SettingsTwoFactorRegenerateScratch) + m.Post("/disable", user.SettingsTwoFactorDisable) + m.Get("/enroll", user.SettingsTwoFactorEnroll) + m.Post("/enroll", bindIgnErr(auth.TwoFactorAuthForm{}), user.SettingsTwoFactorEnrollPost) + }) + m.Group("/openid", func() { + m.Post("", bindIgnErr(auth.AddOpenIDForm{}), user.SettingsOpenIDPost) + m.Post("/delete", user.DeleteOpenID) + m.Post("/toggle_visibility", user.ToggleOpenIDVisibility) + }, openIDSignInEnabled) + m.Post("/account_link", user.SettingsDeleteAccountLink) + }) m.Combo("/applications").Get(user.SettingsApplications). Post(bindIgnErr(auth.NewAccessTokenForm{}), user.SettingsApplicationsPost) m.Post("/applications/delete", user.SettingsDeleteApplication) - m.Route("/delete", "GET,POST", user.SettingsDelete) - m.Combo("/account_link").Get(user.SettingsAccountLinks).Post(user.SettingsDeleteAccountLink) + m.Combo("/keys").Get(user.SettingsKeys). + Post(bindIgnErr(auth.AddKeyForm{}), user.SettingsKeysPost) + m.Post("/keys/delete", user.DeleteKey) m.Get("/organization", user.SettingsOrganization) m.Get("/repos", user.SettingsRepos) - m.Group("/security/two_factor", func() { - m.Post("/regenerate_scratch", user.SettingsTwoFactorRegenerateScratch) - m.Post("/disable", user.SettingsTwoFactorDisable) - m.Get("/enroll", user.SettingsTwoFactorEnroll) - m.Post("/enroll", bindIgnErr(auth.TwoFactorAuthForm{}), user.SettingsTwoFactorEnrollPost) + + // redirects from old settings urls to new ones + // TODO: can be removed on next major version + m.Get("/avatar", func(ctx *context.Context) { + ctx.Redirect(setting.AppSubURL+"/user/settings", http.StatusMovedPermanently) + }) + m.Get("/email", func(ctx *context.Context) { + ctx.Redirect(setting.AppSubURL+"/user/settings/account", http.StatusMovedPermanently) + }) + m.Get("/delete", func(ctx *context.Context) { + ctx.Redirect(setting.AppSubURL+"/user/settings/account", http.StatusMovedPermanently) + }) + m.Get("/openid", func(ctx *context.Context) { + ctx.Redirect(setting.AppSubURL+"/user/settings/security", http.StatusMovedPermanently) + }) + m.Get("/account_link", func(ctx *context.Context) { + ctx.Redirect(setting.AppSubURL+"/user/settings/security", http.StatusMovedPermanently) }) }, reqSignIn, func(ctx *context.Context) { ctx.Data["PageIsUserSettings"] = true diff --git a/routers/user/setting.go b/routers/user/setting.go index f4326bf0f5..1c760e210c 100644 --- a/routers/user/setting.go +++ b/routers/user/setting.go @@ -30,18 +30,13 @@ import ( const ( tplSettingsProfile base.TplName = "user/settings/profile" - tplSettingsAvatar base.TplName = "user/settings/avatar" - tplSettingsEmails base.TplName = "user/settings/email" - tplSettingsKeys base.TplName = "user/settings/keys" - tplSettingsSocial base.TplName = "user/settings/social" - tplSettingsApplications base.TplName = "user/settings/applications" - tplSettingsTwofa base.TplName = "user/settings/twofa" + tplSettingsAccount base.TplName = "user/settings/account" + tplSettingsSecurity base.TplName = "user/settings/security" tplSettingsTwofaEnroll base.TplName = "user/settings/twofa_enroll" - tplSettingsAccountLink base.TplName = "user/settings/account_link" + tplSettingsApplications base.TplName = "user/settings/applications" + tplSettingsKeys base.TplName = "user/settings/keys" tplSettingsOrganization base.TplName = "user/settings/organization" tplSettingsRepositories base.TplName = "user/settings/repos" - tplSettingsDelete base.TplName = "user/settings/delete" - tplSettingsSecurity base.TplName = "user/settings/security" ) // Settings render user's profile page @@ -168,13 +163,6 @@ func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *mo return nil } -// SettingsAvatar render user avatar page -func SettingsAvatar(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsAvatar"] = true - ctx.HTML(200, tplSettingsAvatar) -} - // SettingsAvatarPost response for change user's avatar request func SettingsAvatarPost(ctx *context.Context, form auth.AvatarForm) { if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil { @@ -183,7 +171,7 @@ func SettingsAvatarPost(ctx *context.Context, form auth.AvatarForm) { ctx.Flash.Success(ctx.Tr("settings.update_avatar_success")) } - ctx.Redirect(setting.AppSubURL + "/user/settings/avatar") + ctx.Redirect(setting.AppSubURL + "/user/settings") } // SettingsDeleteAvatar render delete avatar page @@ -192,38 +180,32 @@ func SettingsDeleteAvatar(ctx *context.Context) { ctx.Flash.Error(err.Error()) } - ctx.Redirect(setting.AppSubURL + "/user/settings/avatar") + ctx.Redirect(setting.AppSubURL + "/user/settings") } -// SettingsSecurity render change user's password page and 2FA -func SettingsSecurity(ctx *context.Context) { +// SettingsAccount renders change user's password, user's email and user suicide page +func SettingsAccount(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsSecurity"] = true + ctx.Data["PageIsSettingsAccount"] = true ctx.Data["Email"] = ctx.User.Email - enrolled := true - _, err := models.GetTwoFactorByUID(ctx.User.ID) + emails, err := models.GetEmailAddresses(ctx.User.ID) if err != nil { - if models.IsErrTwoFactorNotEnrolled(err) { - enrolled = false - } else { - ctx.ServerError("SettingsTwoFactor", err) - return - } + ctx.ServerError("GetEmailAddresses", err) + return } + ctx.Data["Emails"] = emails - ctx.Data["TwofaEnrolled"] = enrolled - ctx.HTML(200, tplSettingsSecurity) + ctx.HTML(200, tplSettingsAccount) } -// SettingsSecurityPost response for change user's password -func SettingsSecurityPost(ctx *context.Context, form auth.ChangePasswordForm) { +// SettingsAccountPost response for change user's password +func SettingsAccountPost(ctx *context.Context, form auth.ChangePasswordForm) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsSecurity"] = true - ctx.Data["PageIsSettingsDelete"] = true + ctx.Data["PageIsSettingsAccount"] = true if ctx.HasError() { - ctx.HTML(200, tplSettingsSecurity) + ctx.HTML(200, tplSettingsAccount) return } @@ -248,28 +230,13 @@ func SettingsSecurityPost(ctx *context.Context, form auth.ChangePasswordForm) { ctx.Flash.Success(ctx.Tr("settings.change_password_success")) } - ctx.Redirect(setting.AppSubURL + "/user/settings/security") -} - -// SettingsEmails render user's emails page -func SettingsEmails(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsEmails"] = true - - emails, err := models.GetEmailAddresses(ctx.User.ID) - if err != nil { - ctx.ServerError("GetEmailAddresses", err) - return - } - ctx.Data["Emails"] = emails - - ctx.HTML(200, tplSettingsEmails) + ctx.Redirect(setting.AppSubURL + "/user/settings/account") } // SettingsEmailPost response for change user's email func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsEmails"] = true + ctx.Data["PageIsSettingsAccount"] = true // Make emailaddress primary. if ctx.Query("_method") == "PRIMARY" { @@ -279,7 +246,7 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) { } log.Trace("Email made primary: %s", ctx.User.Name) - ctx.Redirect(setting.AppSubURL + "/user/settings/email") + ctx.Redirect(setting.AppSubURL + "/user/settings/account") return } @@ -292,7 +259,7 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) { ctx.Data["Emails"] = emails if ctx.HasError() { - ctx.HTML(200, tplSettingsEmails) + ctx.HTML(200, tplSettingsAccount) return } @@ -303,7 +270,7 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) { } if err := models.AddEmailAddress(email); err != nil { if models.IsErrEmailAlreadyUsed(err) { - ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsEmails, &form) + ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsAccount, &form) return } ctx.ServerError("AddEmailAddress", err) @@ -323,7 +290,7 @@ func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) { } log.Trace("Email address added: %s", email.Email) - ctx.Redirect(setting.AppSubURL + "/user/settings/email") + ctx.Redirect(setting.AppSubURL + "/user/settings/account") } // DeleteEmail response for delete user's email @@ -336,7 +303,164 @@ func DeleteEmail(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.email_deletion_success")) ctx.JSON(200, map[string]interface{}{ - "redirect": setting.AppSubURL + "/user/settings/email", + "redirect": setting.AppSubURL + "/user/settings/account", + }) +} + +// SettingsDelete render user suicide page and response for delete user himself +func SettingsDelete(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsSettingsAccount"] = true + + if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil { + if models.IsErrUserNotExist(err) { + ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsAccount, nil) + } else { + ctx.ServerError("UserSignIn", err) + } + return + } + + if err := models.DeleteUser(ctx.User); err != nil { + switch { + case models.IsErrUserOwnRepos(err): + ctx.Flash.Error(ctx.Tr("form.still_own_repo")) + ctx.Redirect(setting.AppSubURL + "/user/settings/account") + case models.IsErrUserHasOrgs(err): + ctx.Flash.Error(ctx.Tr("form.still_has_org")) + ctx.Redirect(setting.AppSubURL + "/user/settings/account") + default: + ctx.ServerError("DeleteUser", err) + } + } else { + log.Trace("Account deleted: %s", ctx.User.Name) + ctx.Redirect(setting.AppSubURL + "/") + } +} + +// SettingsSecurity render change user's password page and 2FA +func SettingsSecurity(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsSettingsSecurity"] = true + + enrolled := true + _, err := models.GetTwoFactorByUID(ctx.User.ID) + if err != nil { + if models.IsErrTwoFactorNotEnrolled(err) { + enrolled = false + } else { + ctx.ServerError("SettingsTwoFactor", err) + return + } + } + ctx.Data["TwofaEnrolled"] = enrolled + + accountLinks, err := models.ListAccountLinks(ctx.User) + if err != nil { + ctx.ServerError("ListAccountLinks", err) + return + } + + // map the provider display name with the LoginSource + sources := make(map[*models.LoginSource]string) + for _, externalAccount := range accountLinks { + if loginSource, err := models.GetLoginSourceByID(externalAccount.LoginSourceID); err == nil { + var providerDisplayName string + if loginSource.IsOAuth2() { + providerTechnicalName := loginSource.OAuth2().Provider + providerDisplayName = models.OAuth2Providers[providerTechnicalName].DisplayName + } else { + providerDisplayName = loginSource.Name + } + sources[loginSource] = providerDisplayName + } + } + ctx.Data["AccountLinks"] = sources + + if ctx.Query("openid.return_to") != "" { + settingsOpenIDVerify(ctx) + return + } + + openid, err := models.GetUserOpenIDs(ctx.User.ID) + if err != nil { + ctx.ServerError("GetUserOpenIDs", err) + return + } + ctx.Data["OpenIDs"] = openid + + ctx.HTML(200, tplSettingsSecurity) +} + +// SettingsDeleteAccountLink delete a single account link +func SettingsDeleteAccountLink(ctx *context.Context) { + if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil { + ctx.Flash.Error("RemoveAccountLink: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success")) + } + + ctx.JSON(200, map[string]interface{}{ + "redirect": setting.AppSubURL + "/user/settings/security", + }) +} + +// SettingsApplications render manage access token page +func SettingsApplications(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsSettingsApplications"] = true + + tokens, err := models.ListAccessTokens(ctx.User.ID) + if err != nil { + ctx.ServerError("ListAccessTokens", err) + return + } + ctx.Data["Tokens"] = tokens + + ctx.HTML(200, tplSettingsApplications) +} + +// SettingsApplicationsPost response for add user's access token +func SettingsApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) { + ctx.Data["Title"] = ctx.Tr("settings") + ctx.Data["PageIsSettingsApplications"] = true + + if ctx.HasError() { + tokens, err := models.ListAccessTokens(ctx.User.ID) + if err != nil { + ctx.ServerError("ListAccessTokens", err) + return + } + ctx.Data["Tokens"] = tokens + ctx.HTML(200, tplSettingsApplications) + return + } + + t := &models.AccessToken{ + UID: ctx.User.ID, + Name: form.Name, + } + if err := models.NewAccessToken(t); err != nil { + ctx.ServerError("NewAccessToken", err) + return + } + + ctx.Flash.Success(ctx.Tr("settings.generate_token_success")) + ctx.Flash.Info(t.Sha1) + + ctx.Redirect(setting.AppSubURL + "/user/settings/applications") +} + +// SettingsDeleteApplication response for delete user access token +func SettingsDeleteApplication(ctx *context.Context) { + if err := models.DeleteAccessTokenByID(ctx.QueryInt64("id"), ctx.User.ID); err != nil { + ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error()) + } else { + ctx.Flash.Success(ctx.Tr("settings.delete_token_success")) + } + + ctx.JSON(200, map[string]interface{}{ + "redirect": setting.AppSubURL + "/user/settings/applications", }) } @@ -471,65 +595,6 @@ func DeleteKey(ctx *context.Context) { }) } -// SettingsApplications render user's access tokens page -func SettingsApplications(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsApplications"] = true - - tokens, err := models.ListAccessTokens(ctx.User.ID) - if err != nil { - ctx.ServerError("ListAccessTokens", err) - return - } - ctx.Data["Tokens"] = tokens - - ctx.HTML(200, tplSettingsApplications) -} - -// SettingsApplicationsPost response for add user's access token -func SettingsApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsApplications"] = true - - if ctx.HasError() { - tokens, err := models.ListAccessTokens(ctx.User.ID) - if err != nil { - ctx.ServerError("ListAccessTokens", err) - return - } - ctx.Data["Tokens"] = tokens - ctx.HTML(200, tplSettingsApplications) - return - } - - t := &models.AccessToken{ - UID: ctx.User.ID, - Name: form.Name, - } - if err := models.NewAccessToken(t); err != nil { - ctx.ServerError("NewAccessToken", err) - return - } - - ctx.Flash.Success(ctx.Tr("settings.generate_token_success")) - ctx.Flash.Info(t.Sha1) - - ctx.Redirect(setting.AppSubURL + "/user/settings/applications") -} - -// SettingsDeleteApplication response for delete user access token -func SettingsDeleteApplication(ctx *context.Context) { - if err := models.DeleteAccessTokenByID(ctx.QueryInt64("id"), ctx.User.ID); err != nil { - ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("settings.delete_token_success")) - } - - ctx.JSON(200, map[string]interface{}{ - "redirect": setting.AppSubURL + "/user/settings/applications", - }) -} - // SettingsTwoFactorRegenerateScratch regenerates the user's 2FA scratch code. func SettingsTwoFactorRegenerateScratch(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") @@ -695,86 +760,6 @@ func SettingsTwoFactorEnrollPost(ctx *context.Context, form auth.TwoFactorAuthFo ctx.Redirect(setting.AppSubURL + "/user/settings/security") } -// SettingsAccountLinks render the account links settings page -func SettingsAccountLinks(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsAccountLink"] = true - - accountLinks, err := models.ListAccountLinks(ctx.User) - if err != nil { - ctx.ServerError("ListAccountLinks", err) - return - } - - // map the provider display name with the LoginSource - sources := make(map[*models.LoginSource]string) - for _, externalAccount := range accountLinks { - if loginSource, err := models.GetLoginSourceByID(externalAccount.LoginSourceID); err == nil { - var providerDisplayName string - if loginSource.IsOAuth2() { - providerTechnicalName := loginSource.OAuth2().Provider - providerDisplayName = models.OAuth2Providers[providerTechnicalName].DisplayName - } else { - providerDisplayName = loginSource.Name - } - sources[loginSource] = providerDisplayName - } - } - ctx.Data["AccountLinks"] = sources - - ctx.HTML(200, tplSettingsAccountLink) -} - -// SettingsDeleteAccountLink delete a single account link -func SettingsDeleteAccountLink(ctx *context.Context) { - if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil { - ctx.Flash.Error("RemoveAccountLink: " + err.Error()) - } else { - ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success")) - } - - ctx.JSON(200, map[string]interface{}{ - "redirect": setting.AppSubURL + "/user/settings/account_link", - }) -} - -// SettingsDelete render user suicide page and response for delete user himself -func SettingsDelete(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsDelete"] = true - ctx.Data["Email"] = ctx.User.Email - - if ctx.Req.Method == "POST" { - if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil { - if models.IsErrUserNotExist(err) { - ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsDelete, nil) - } else { - ctx.ServerError("UserSignIn", err) - } - return - } - - if err := models.DeleteUser(ctx.User); err != nil { - switch { - case models.IsErrUserOwnRepos(err): - ctx.Flash.Error(ctx.Tr("form.still_own_repo")) - ctx.Redirect(setting.AppSubURL + "/user/settings/delete") - case models.IsErrUserHasOrgs(err): - ctx.Flash.Error(ctx.Tr("form.still_has_org")) - ctx.Redirect(setting.AppSubURL + "/user/settings/delete") - default: - ctx.ServerError("DeleteUser", err) - } - } else { - log.Trace("Account deleted: %s", ctx.User.Name) - ctx.Redirect(setting.AppSubURL + "/") - } - return - } - - ctx.HTML(200, tplSettingsDelete) -} - // SettingsOrganization render all the organization of the user func SettingsOrganization(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("settings") diff --git a/routers/user/setting_openid.go b/routers/user/setting_openid.go index 92eb636e29..7716466120 100644 --- a/routers/user/setting_openid.go +++ b/routers/user/setting_openid.go @@ -8,40 +8,15 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/auth/openid" - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" ) -const ( - tplSettingsOpenID base.TplName = "user/settings/openid" -) - -// SettingsOpenID renders change user's openid page -func SettingsOpenID(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsOpenID"] = true - - if ctx.Query("openid.return_to") != "" { - settingsOpenIDVerify(ctx) - return - } - - openid, err := models.GetUserOpenIDs(ctx.User.ID) - if err != nil { - ctx.ServerError("GetUserOpenIDs", err) - return - } - ctx.Data["OpenIDs"] = openid - - ctx.HTML(200, tplSettingsOpenID) -} - // SettingsOpenIDPost response for change user's openid func SettingsOpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { ctx.Data["Title"] = ctx.Tr("settings") - ctx.Data["PageIsSettingsOpenID"] = true + ctx.Data["PageIsSettingsSecurity"] = true if ctx.HasError() { openid, err := models.GetUserOpenIDs(ctx.User.ID) @@ -50,7 +25,7 @@ func SettingsOpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { return } ctx.Data["OpenIDs"] = openid - ctx.HTML(200, tplSettingsOpenID) + ctx.HTML(200, tplSettingsSecurity) return } @@ -62,7 +37,7 @@ func SettingsOpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { id, err := openid.Normalize(form.Openid) if err != nil { - ctx.RenderWithErr(err.Error(), tplSettingsOpenID, &form) + ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form) return } form.Openid = id @@ -78,15 +53,15 @@ func SettingsOpenIDPost(ctx *context.Context, form auth.AddOpenIDForm) { // Check that the OpenID is not already used for _, obj := range oids { if obj.URI == id { - ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsOpenID, &form) + ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsSecurity, &form) return } } - redirectTo := setting.AppURL + "user/settings/openid" + redirectTo := setting.AppURL + "user/settings/security" url, err := openid.RedirectURL(id, redirectTo, setting.AppURL) if err != nil { - ctx.RenderWithErr(err.Error(), tplSettingsOpenID, &form) + ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &form) return } ctx.Redirect(url) @@ -107,7 +82,7 @@ func settingsOpenIDVerify(ctx *context.Context) { id, err := openid.Verify(fullURL) if err != nil { - ctx.RenderWithErr(err.Error(), tplSettingsOpenID, &auth.AddOpenIDForm{ + ctx.RenderWithErr(err.Error(), tplSettingsSecurity, &auth.AddOpenIDForm{ Openid: id, }) return @@ -118,7 +93,7 @@ func settingsOpenIDVerify(ctx *context.Context) { oid := &models.UserOpenID{UID: ctx.User.ID, URI: id} if err = models.AddUserOpenID(oid); err != nil { if models.IsErrOpenIDAlreadyUsed(err) { - ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsOpenID, &auth.AddOpenIDForm{Openid: id}) + ctx.RenderWithErr(ctx.Tr("form.openid_been_used", id), tplSettingsSecurity, &auth.AddOpenIDForm{Openid: id}) return } ctx.ServerError("AddUserOpenID", err) @@ -127,7 +102,7 @@ func settingsOpenIDVerify(ctx *context.Context) { log.Trace("Associated OpenID %s to user %s", id, ctx.User.Name) ctx.Flash.Success(ctx.Tr("settings.add_openid_success")) - ctx.Redirect(setting.AppSubURL + "/user/settings/openid") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") } // DeleteOpenID response for delete user's openid @@ -140,7 +115,7 @@ func DeleteOpenID(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("settings.openid_deletion_success")) ctx.JSON(200, map[string]interface{}{ - "redirect": setting.AppSubURL + "/user/settings/openid", + "redirect": setting.AppSubURL + "/user/settings/security", }) } @@ -151,5 +126,5 @@ func ToggleOpenIDVisibility(ctx *context.Context) { return } - ctx.Redirect(setting.AppSubURL + "/user/settings/openid") + ctx.Redirect(setting.AppSubURL + "/user/settings/security") } diff --git a/routers/user/setting_test.go b/routers/user/setting_test.go index 72b1b83143..6aa9a07439 100644 --- a/routers/user/setting_test.go +++ b/routers/user/setting_test.go @@ -56,7 +56,7 @@ func TestChangePassword(t *testing.T) { test.LoadUser(t, ctx, 2) test.LoadRepo(t, ctx, 1) - SettingsSecurityPost(ctx, auth.ChangePasswordForm{ + SettingsAccountPost(ctx, auth.ChangePasswordForm{ OldPassword: req.OldPassword, Password: req.NewPassword, Retype: req.Retype, diff --git a/templates/user/profile.tmpl b/templates/user/profile.tmpl index 669ee3264b..837f8bd949 100644 --- a/templates/user/profile.tmpl +++ b/templates/user/profile.tmpl @@ -5,7 +5,7 @@
{{if eq .SignedUserName .Owner.Name}} - + {{else}} diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl new file mode 100644 index 0000000000..290745222d --- /dev/null +++ b/templates/user/settings/account.tmpl @@ -0,0 +1,135 @@ +{{template "base/head" .}} + + + + + + +{{template "base/footer" .}} diff --git a/templates/user/settings/account_link.tmpl b/templates/user/settings/account_link.tmpl deleted file mode 100644 index 81ddf626e1..0000000000 --- a/templates/user/settings/account_link.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -{{template "base/head" .}} - - - -{{template "base/footer" .}} diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index 4ad5eaa714..f1a3e48115 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -1,8 +1,7 @@ {{template "base/head" .}} -
+
{{template "user/settings/navbar" .}}
- {{template "base/alert" .}}

{{.i18n.Tr "settings.manage_access_token"}}

@@ -13,26 +12,26 @@
{{range .Tokens}}
-
- -
- -
- {{.Name}} -
- {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}} -
+
+ +
+ {{.Name}} +
+ {{$.i18n.Tr "settings.add_on"}} {{.CreatedUnix.FormatShort}} {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{.UpdatedUnix.FormatShort}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}
+
{{end}}
-

- {{.i18n.Tr "settings.generate_new_token"}} -

-
+
+
+ {{.i18n.Tr "settings.generate_new_token"}} +

{{.i18n.Tr "settings.new_token_desc"}}

{{.CsrfTokenHtml}} @@ -48,7 +47,7 @@
- + + {{template "base/footer" .}} diff --git a/templates/user/settings/avatar.tmpl b/templates/user/settings/avatar.tmpl deleted file mode 100644 index 72d2eb6ad4..0000000000 --- a/templates/user/settings/avatar.tmpl +++ /dev/null @@ -1,46 +0,0 @@ -{{template "base/head" .}} -
- {{template "user/settings/navbar" .}} -
- {{template "base/alert" .}} -

- {{.i18n.Tr "settings.avatar"}} -

-
- - - {{.CsrfTokenHtml}} - {{if not DisableGravatar}} -
-
- - -
-
-
- - -
- {{end}} - -
-
- - -
-
- -
- - -
- -
- - {{$.i18n.Tr "settings.delete_current_avatar"}} -
- -
-
-
-{{template "base/footer" .}} diff --git a/templates/user/settings/delete.tmpl b/templates/user/settings/delete.tmpl deleted file mode 100644 index 76ac7233bd..0000000000 --- a/templates/user/settings/delete.tmpl +++ /dev/null @@ -1,41 +0,0 @@ -{{template "base/head" .}} -
- {{template "user/settings/navbar" .}} -
- {{template "base/alert" .}} -

- {{.i18n.Tr "settings.delete_account"}} -

-
-
-

{{.i18n.Tr "settings.delete_prompt" | Str2html}}

-
-
- {{.CsrfTokenHtml}} - -
- - -
-
-
- {{.i18n.Tr "settings.confirm_delete_account"}} -
- {{.i18n.Tr "auth.forgot_password"}} -
-
-
-
-
- - -{{template "base/footer" .}} diff --git a/templates/user/settings/email.tmpl b/templates/user/settings/email.tmpl deleted file mode 100644 index 62dc1d5e59..0000000000 --- a/templates/user/settings/email.tmpl +++ /dev/null @@ -1,66 +0,0 @@ -{{template "base/head" .}} -
- {{template "user/settings/navbar" .}} -
- {{template "base/alert" .}} -

- {{.i18n.Tr "settings.manage_emails"}} -

-
- -
-
-
- {{.CsrfTokenHtml}} -
- - -
- -
-
-
-
- - -{{template "base/footer" .}} diff --git a/templates/user/settings/navbar.tmpl b/templates/user/settings/navbar.tmpl index 4e0c7048ee..18bd9a4f8d 100644 --- a/templates/user/settings/navbar.tmpl +++ b/templates/user/settings/navbar.tmpl @@ -2,28 +2,17 @@ {{.i18n.Tr "settings.profile"}} - - {{.i18n.Tr "settings.avatar"}} + + {{.i18n.Tr "settings.account"}} {{.i18n.Tr "settings.security"}} - - {{.i18n.Tr "settings.emails"}} - - {{if .EnableOpenIDSignIn}} - - OpenID - - {{end}} - - {{.i18n.Tr "settings.ssh_gpg_keys"}} - {{.i18n.Tr "settings.applications"}} - - {{.i18n.Tr "settings.account_link"}} + + {{.i18n.Tr "settings.ssh_gpg_keys"}} {{.i18n.Tr "settings.organization"}} @@ -31,7 +20,4 @@ {{.i18n.Tr "settings.repos"}} - - {{.i18n.Tr "settings.delete"}} -
diff --git a/templates/user/settings/openid.tmpl b/templates/user/settings/openid.tmpl deleted file mode 100644 index 6ae4a8dee8..0000000000 --- a/templates/user/settings/openid.tmpl +++ /dev/null @@ -1,71 +0,0 @@ -{{template "base/head" .}} -
- {{template "user/settings/navbar" .}} -
- {{template "base/alert" .}} -

- {{.i18n.Tr "settings.manage_openid"}} -

-
-
-
- {{.i18n.Tr "settings.openid_desc"}} -
- {{range .OpenIDs}} -
-
- -
-
-
- {{$.CsrfTokenHtml}} - - {{if .Show}} - - {{else}} - - {{end}} - -
-
-
- {{.URI}} -
-
- {{end}} -
-
-
-
- {{.CsrfTokenHtml}} -
- - -
- -
-
-
-
- - -{{template "base/footer" .}} diff --git a/templates/user/settings/organization.tmpl b/templates/user/settings/organization.tmpl index 2d357cb3b5..de541dcd17 100644 --- a/templates/user/settings/organization.tmpl +++ b/templates/user/settings/organization.tmpl @@ -1,5 +1,5 @@ {{template "base/head" .}} -
diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl index 39d98c6d1e..efb2c41c5b 100644 --- a/templates/user/settings/repos.tmpl +++ b/templates/user/settings/repos.tmpl @@ -1,5 +1,5 @@ {{template "base/head" .}} -
+
{{template "user/settings/navbar" .}}
{{template "base/alert" .}} diff --git a/templates/user/settings/security.tmpl b/templates/user/settings/security.tmpl index b7cd222b30..8e7044f7df 100644 --- a/templates/user/settings/security.tmpl +++ b/templates/user/settings/security.tmpl @@ -1,79 +1,14 @@ {{template "base/head" .}} -
+
{{template "user/settings/navbar" .}}
{{template "base/alert" .}} -

- {{.i18n.Tr "settings.password"}} -

-
- {{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}} -
- {{.CsrfTokenHtml}} - {{if .SignedUser.IsPasswordSet}} -
- - -
- {{end}} -
- - -
-
- - -
- -
- - {{.i18n.Tr "auth.forgot_password"}} -
-
- {{else}} -
-

{{$.i18n.Tr "settings.password_change_disabled"}}

-
- {{end}} -
-
- -

- {{.i18n.Tr "settings.twofa"}} -

-
-

{{.i18n.Tr "settings.twofa_desc"}}

- {{if .TwofaEnrolled}} -

{{$.i18n.Tr "settings.twofa_is_enrolled" | Str2html }}

-
- {{.CsrfTokenHtml}} -

{{.i18n.Tr "settings.regenerate_scratch_token_desc"}}

- -
-
- {{.CsrfTokenHtml}} -

{{.i18n.Tr "settings.twofa_disable_note"}}

-
{{$.i18n.Tr "settings.twofa_disable"}}
-
- {{else}} -

{{.i18n.Tr "settings.twofa_not_enrolled"}}

- - {{end}} -
+ {{template "user/settings/security_twofa" .}} + {{template "user/settings/security_accountlinks" .}} + {{if .EnableOpenIDSignIn}} + {{template "user/settings/security_openid" .}} + {{end}}
- - {{template "base/footer" .}} diff --git a/templates/user/settings/security_accountlinks.tmpl b/templates/user/settings/security_accountlinks.tmpl new file mode 100644 index 0000000000..93cc508a54 --- /dev/null +++ b/templates/user/settings/security_accountlinks.tmpl @@ -0,0 +1,36 @@ +

+ {{.i18n.Tr "settings.manage_account_links"}} +

+
+
+
+ {{.i18n.Tr "settings.manage_account_links_desc"}} +
+ {{if .AccountLinks}} + {{range $loginSource, $provider := .AccountLinks}} +
+
+ +
+
+ {{$provider}} + {{if $loginSource.IsActived}}{{$.i18n.Tr "settings.active"}}{{end}} +
+
+ {{end}} + {{end}} +
+
+ + diff --git a/templates/user/settings/security_openid.tmpl b/templates/user/settings/security_openid.tmpl new file mode 100644 index 0000000000..12f4aab419 --- /dev/null +++ b/templates/user/settings/security_openid.tmpl @@ -0,0 +1,63 @@ +

+ {{.i18n.Tr "settings.manage_openid"}} +

+
+
+
+ {{.i18n.Tr "settings.openid_desc"}} +
+ {{range .OpenIDs}} +
+
+ +
+
+
+ {{$.CsrfTokenHtml}} + + {{if .Show}} + + {{else}} + + {{end}} + +
+
+
+ {{.URI}} +
+
+ {{end}} +
+
+
+
+ {{.CsrfTokenHtml}} +
+ + +
+ +
+
+ + diff --git a/templates/user/settings/security_twofa.tmpl b/templates/user/settings/security_twofa.tmpl new file mode 100644 index 0000000000..05112a21bb --- /dev/null +++ b/templates/user/settings/security_twofa.tmpl @@ -0,0 +1,35 @@ +

+ {{.i18n.Tr "settings.twofa"}} +

+
+

{{.i18n.Tr "settings.twofa_desc"}}

+ {{if .TwofaEnrolled}} +

{{$.i18n.Tr "settings.twofa_is_enrolled" | Str2html }}

+
+ {{.CsrfTokenHtml}} +

{{.i18n.Tr "settings.regenerate_scratch_token_desc"}}

+ +
+
+ {{.CsrfTokenHtml}} +

{{.i18n.Tr "settings.twofa_disable_note"}}

+
{{$.i18n.Tr "settings.twofa_disable"}}
+
+ {{else}} +

{{.i18n.Tr "settings.twofa_not_enrolled"}}

+ + {{end}} +
+ + diff --git a/templates/user/settings/twofa.tmpl b/templates/user/settings/twofa.tmpl deleted file mode 100644 index c6a7a7cba0..0000000000 --- a/templates/user/settings/twofa.tmpl +++ /dev/null @@ -1,44 +0,0 @@ -{{template "base/head" .}} -
- {{template "user/settings/navbar" .}} -
- {{template "base/alert" .}} -

- {{.i18n.Tr "settings.twofa"}} -

-
-

{{.i18n.Tr "settings.twofa_desc"}}

- {{if .TwofaEnrolled}} -

{{$.i18n.Tr "settings.twofa_is_enrolled" | Str2html }}

-
- {{.CsrfTokenHtml}} -

{{.i18n.Tr "settings.regenerate_scratch_token_desc"}}

- -
-
- {{.CsrfTokenHtml}} -

{{.i18n.Tr "settings.twofa_disable_note"}}

-
{{$.i18n.Tr "settings.twofa_disable"}}
-
- {{else}} -

{{.i18n.Tr "settings.twofa_not_enrolled"}}

- - {{end}} -
-
-
- - - -{{template "base/footer" .}} diff --git a/templates/user/settings/twofa_enroll.tmpl b/templates/user/settings/twofa_enroll.tmpl index 9238fb61a5..0205532ac8 100644 --- a/templates/user/settings/twofa_enroll.tmpl +++ b/templates/user/settings/twofa_enroll.tmpl @@ -1,5 +1,5 @@ {{template "base/head" .}} -
+
{{template "user/settings/navbar" .}}
{{template "base/alert" .}} From d3dce01cf78befe0b7c589957f27c1626f2069d7 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 15 May 2018 10:08:59 +0000 Subject: [PATCH 2/2] [skip ci] Updated translations via Crowdin --- options/locale/locale_de-DE.ini | 143 ++++++++++++++++++++++++++++++++ options/locale/locale_pt-BR.ini | 2 + options/locale/locale_uk-UA.ini | 15 +++- options/locale/locale_zh-CN.ini | 2 + 4 files changed, 161 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index ec7a8634db..b5a6d537bd 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -117,6 +117,7 @@ federated_avatar_lookup=Föderierte Profilbilder einschalten federated_avatar_lookup_popup=Föderierte Profilbilder via Libravatar aktivieren. disable_registration=Registrierung deaktivieren disable_registration_popup=Registrierung neuer Benutzer deaktivieren. Nur Administratoren werden neue Benutzerkonten anlegen können. +allow_only_external_registration_popup=Registrierung nur über externe Services aktiveren. openid_signin=OpenID Anmeldung aktivieren openid_signin_popup=Benutzeranmeldung via OpenID aktivieren. openid_signup=OpenID Selbstregistrierung aktivieren @@ -147,6 +148,7 @@ default_allow_create_organization=Erstellen von Organisationen standarmäßig er default_allow_create_organization_popup=Neuen Nutzern das Erstellen von Organisationen standardmäßig erlauben. default_enable_timetracking=Zeiterfassung standardmäßig aktivieren default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren. +no_reply_address=Versteckte E-Mail-Domain no_reply_address_helper=Domain-Namen 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-Domäne "noreply.example.org" festgelegt ist. [home] @@ -330,6 +332,7 @@ change_username=Dein Benutzername wurde geändert. change_username_prompt=Hinweis: Wenn du deinen Benutzernamen änderst, wird auch deine Konto-URL geändert. continue=Weiter cancel=Abbrechen +language=Sprache lookup_avatar_by_mail=Avatar anhand der E-Mail-Addresse suchen federated_avatar_lookup=Suche nach föderierten Profilbildern @@ -500,6 +503,7 @@ form.reach_limit_of_creation=Du hast bereits dein Limit von %d Repositories erre form.name_reserved=Der Repository-Name '%s' ist reserviert. form.name_pattern_not_allowed='%s' ist nicht erlaubt für Repository-Namen. +need_auth=Authentifizierung zum Klonen benötigt migrate_type=Migrationstyp migrate_type_helper=Dieses Repository wird ein Mirror sein migrate_repo=Repository migrieren @@ -533,6 +537,7 @@ push_exist_repo=Bestehendes Repository via Kommandozeile pushen bare_message=Dieses Repository hat noch keinen Inhalt. code=Code +code.desc=Zugriff auf Quellcode, Dateien, Commits und Branches. branch=Branch tree=Struktur filter_branch_and_tag=Branch oder Tag filtern @@ -560,11 +565,13 @@ editor.edit_file=Datei bearbeiten editor.preview_changes=Vorschau der Änderungen editor.cannot_edit_non_text_files=Binärdateien können nicht im Webinterface bearbeitet werden. editor.edit_this_file=Datei bearbeiten +editor.must_be_on_a_branch=Du musst dich in einer Branch befinden, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.fork_before_edit=Du musst dieses Repository forken, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.delete_this_file=Datei löschen editor.must_have_write_access=Du benötigst Schreibzugriff, um Änderungen an dieser Datei vorzuschlagen oder vorzunehmen. editor.file_delete_success=Datei '%s' wurde gelöscht. editor.name_your_file=Dateinamen eingeben… +editor.filename_help=Füge einen Ordner hinzu, indem du seinen Namen und anschließend '/' eingibst. Entferne einen Ordner indem du die Zurücktaste am Anfang des Feldes drückst. editor.or=oder editor.cancel_lower=Abbrechen editor.commit_changes=Änderungen committen @@ -723,25 +730,41 @@ issues.tracking_already_started=`Du hast die Zeiterfassung bereits in %[2]s#%[3]d` pulls.create=Pull-Request erstellen pulls.title_desc=möchte %[1]d Commits von %[2]s nach %[3]s zusammenführen pulls.merged_title_desc=hat %[1]d Commits von %[2]s nach %[3]s %[4]s zusammengeführt @@ -751,10 +774,18 @@ pulls.tab_files=Geänderte Dateien pulls.reopen_to_merge=Bitte diesen Pull-Request wieder öffnen, um die Merge-Operation auszuführen. pulls.merged=Zusammengeführt pulls.has_merged=Der Pull-Request wurde zusammengeführt. +pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden. +pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. pulls.can_auto_merge_desc=Dieser Pull-Request kann automatisch zusammengeführt werden. +pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch zusammengeführt werden, da es Konflikte gibt. +pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte zu lösen. +pulls.no_merge_desc=Dieser Pull-Request kann nicht gemerged werden, da keine Mergeoptionen aktiviert sind. +pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder merge den Pull-Request manuell. pulls.merge_pull_request=Pull-Request zusammenführen pulls.rebase_merge_pull_request=Rebase und Mergen pulls.squash_merge_pull_request=Zusammenfassen und Mergen +pulls.invalid_merge_option=Du kannst diese Mergeoption auf diesen Pull-Request nicht anwenden. +pulls.open_unmerged_pull_exists=`Du kannst diesen Pull-Request nicht erneut öffnen, da noch ein anderer (#%d) mit identischen Eigenschaften offen ist.` milestones.new=Neuer Meilenstein milestones.open_tab=%d offen @@ -763,6 +794,7 @@ milestones.closed=Geschlossen %s milestones.no_due_date=Kein Fälligkeitsdatum milestones.open=Öffnen milestones.close=Schließen +milestones.new_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. milestones.create=Meilenstein erstellen milestones.title=Titel milestones.desc=Beschreibung @@ -771,6 +803,7 @@ milestones.clear=Feld leeren milestones.invalid_due_date_format=Das Fälligkeitsdatum muss das Format 'JJJJ-MM-TT' haben. milestones.create_success=Der Meilenstein '%s' wurde erstellt. milestones.edit=Meilenstein bearbeiten +milestones.edit_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. milestones.cancel=Abbrechen milestones.modify=Meilenstein bearbeiten milestones.edit_success=Die Änderungen am Meilenstein "%s" wurden gespeichert. @@ -789,6 +822,7 @@ ext_wiki.desc=Verweis auf externes Wiki. wiki=Wiki wiki.welcome=Willkommen im Wiki. +wiki.welcome_desc=Im Wiki kannst Dokumentation schreiben und mit Mitarbeitern teilen. wiki.desc=Schreibe und teile Dokumentation mit Mitarbeitern. wiki.create_first_page=Erstelle die erste Seite wiki.page=Seite @@ -800,6 +834,7 @@ wiki.last_commit_info=%s hat diese Seite bearbeitet %s wiki.edit_page_button=Bearbeiten wiki.new_page_button=Neue Seite wiki.delete_page_button=Seite löschen +wiki.delete_page_notice_1=Das Löschen der Wiki-Seite '%s' kann nicht Rückgängig gemacht werden. Fortfahren? wiki.page_already_exists=Eine Wiki-Seite mit dem gleichen Namen existiert bereits. wiki.reserved_page=Der Wiki-Seitenname "%s" ist reserviert. wiki.pages=Seiten @@ -838,6 +873,9 @@ activity.closed_issue_label=Geschlossen activity.new_issues_count_1=Neuer Issue activity.new_issues_count_n=Neue Issues activity.new_issue_label=Geöffnet +activity.title.unresolved_conv_1=%d offene Konversation +activity.title.unresolved_conv_n=%d offene Konversationen +activity.unresolved_conv_desc=Diese kürzlich geänderten Issues und Pull-Requests wurden noch nicht gelöst. activity.unresolved_conv_label=Offen activity.title.releases_1=%d Release activity.title.releases_n=%d Releases @@ -861,6 +899,7 @@ settings.githooks=Git-Hooks settings.basic_settings=Grundeinstellungen settings.mirror_settings=Mirror Einstellungen settings.sync_mirror=Jetzt Synchronisieren +settings.mirror_sync_in_progress=Mirror-Synchronisierung wird zurzeit ausgeführt. Komm in ein paar Minuten zurück. settings.site=Webseite settings.update_settings=Einstellungen speichern settings.advanced_settings=Erweiterte Einstellungen @@ -869,16 +908,27 @@ settings.use_internal_wiki=Eingebautes Wiki verwenden settings.use_external_wiki=Externes Wiki verwenden settings.external_wiki_url=Externe Wiki URL settings.external_wiki_url_error=Die externe Wiki-URL ist ungültig. +settings.external_wiki_url_desc=Besucher werden auf die externe Wiki-URL weitergeleitet wenn sie auf das Wiki-Tab klicken. settings.issues_desc=Repository Issue-Tracker aktivieren settings.use_internal_issue_tracker=Integrierten Issue-Tracker verwenden settings.use_external_issue_tracker=Externen Issue-Tracker verwenden settings.external_tracker_url=URL eines externen Issue Trackers settings.external_tracker_url_error=Die URL des externen Issue-Trackers ist ungültig. +settings.external_tracker_url_desc=Besucher werden auf die externe Issue-Tracker-URL weitergeleitet wenn sie auf das Issues-Tab klicken. settings.tracker_url_format=URL-Format des externen Issue-Systems +settings.tracker_issue_style=Namenskonvention des externen Issue-Trackers settings.tracker_issue_style.numeric=Numerisch settings.tracker_issue_style.alphanumeric=Alphanumerisch +settings.tracker_url_format_desc=Du kannst die Platzhalter {user}, {repo}, {index} für den Benutzernamen, den Namen des Repositories und die Issue-Nummer verwenden. settings.enable_timetracker=Zeiterfassung aktivieren +settings.allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen +settings.pulls_desc=Repository-Pull-Requests aktivieren +settings.pulls.ignore_whitespace=Bei Konflikten Leerzeichen ignorieren +settings.pulls.allow_merge_commits=Mergecommits aktivieren +settings.pulls.allow_rebase_merge=Mergen von Commits durch Rebasen aktivieren +settings.pulls.allow_squash_commits=Mergen von Commits durch Squash aktivieren settings.admin_settings=Administratoreinstellungen +settings.admin_enable_health_check=Repository-Health-Checks aktivieren (git fsck) settings.danger_zone=Gefahrenzone settings.new_owner_has_same_repo=Der neue Eigentümer hat bereits ein Repository mit dem gleichen Namen. Bitte wähle einen anderen Namen. settings.convert=In ein normales Repository umwandeln @@ -899,6 +949,8 @@ settings.wiki_deletion_success=Repository Wiki-Daten wurden gelöscht. settings.delete=Dieses Repository löschen settings.delete_desc=Wenn dieses Repository gelöscht wurde, gibt es keinen Weg zurück. Bitte sei vorsichtig. settings.delete_notices_1=- Diese Operation kann NICHT rückgängig gemacht werden. +settings.delete_notices_2=- Die Operation wird das %s-Repository dauerhaft löschen, inklusive der Dateien, Issues, Kommentare und Zugriffseinstellungen. +settings.delete_notices_fork_1=- Nach dem Löschen werden alle Forks unabhängig. settings.deletion_success=Das Repository wurde gelöscht. settings.update_settings_success=Repository Einstellungen wurden aktualisiert. settings.transfer_owner=Neuer Besitzer @@ -909,30 +961,40 @@ settings.add_collaborator=Mitarbeiter hinzufügen settings.add_collaborator_success=Der Mitarbeiter wurde hinzugefügt. settings.delete_collaborator=Entfernen settings.collaborator_deletion=Mitarbeiter entfernen +settings.collaborator_deletion_desc=Nach dem Löschen wird dieser Mitarbeiter keinen Zugriff mehr auf dieses Repository haben. Fortfahren? settings.remove_collaborator_success=Der Mitarbeiter wurde entfernt. settings.search_user_placeholder=Benutzer suchen… settings.org_not_allowed_to_be_collaborator=Organisationen können nicht als Mitarbeiter hinzugefügt werden. settings.user_is_org_member=Der Benutzer ist ein Organisationsmitglied und kann nicht als Mitarbeiter hinzugefügt werden. settings.add_webhook=Webhook hinzufügen +settings.hooks_desc=Webhooks senden bei bestimmten Gitea-Events automatisch HTTP POST-Requets an einen Server. Lies mehr in unserer Anleitung zu Webhooks (Englisch). settings.webhook_deletion=Webhook löschen +settings.webhook_deletion_desc=Das Entfernen eines Webhooks löscht seine Einstellungen und Zustellungsverlauf. Fortfahren? settings.webhook_deletion_success=Webhook wurde entfernt. settings.webhook.test_delivery=Senden testen +settings.webhook.test_delivery_desc=Teste diesen Webhook mit einem Fake-Event. +settings.webhook.test_delivery_success=Ein Fake-Event wurde zur Auslieferungswarteschlange hinzugefügt. Es kann einige Sekunden dauern, bevor es im Zustellungsverlauf erscheint. settings.webhook.request=Anfrage settings.webhook.response=Antwort settings.webhook.headers=Kopfzeilen settings.webhook.payload=Inhalt settings.webhook.body=Inhalt +settings.githooks_desc=Git-Hooks werden von Git selbst bereitgestellt. Du kannst die Dateien der unterstützten Hooks in der Liste unten bearbeiten, um eigene Operationen einzubinden. settings.githook_edit_desc=Wenn ein Hook nicht aktiv ist, wird der Standardinhalt benutzt. Lasse den Inhalt leer, um den Hook zu deaktivieren. settings.githook_name=Hook-Name settings.githook_content=Hook-Inhalt settings.update_githook=Hook aktualisieren +settings.add_webhook_desc=Gitea sendet einen POST-Request mit festgelegtem Content-Type an die Ziel-URL. Mehr Informationen findest du in der Anleitung zu Webhooks (Englisch). settings.payload_url=Ziel-URL +settings.content_type=POST-Content-Type settings.secret=Secret settings.slack_username=Benutzername settings.slack_icon_url=Icon-URL settings.discord_username=Benutzername settings.discord_icon_url=Icon-URL settings.slack_color=Farbe +settings.event_desc=Auslösen bei: +settings.event_push_only=Push-Events settings.event_send_everything=Alle Events settings.event_choose=Benutzerdefinierte Events… settings.event_create=Erstellen @@ -943,20 +1005,34 @@ settings.event_push=Push settings.event_push_desc=Git push in ein Repository. settings.event_repository=Repository settings.event_repository_desc=Repository erstellt oder gelöscht. +settings.active=Event-Details mitversenden +settings.active_helper=Informationen über das auslösende Event mitversenden. settings.add_hook_success=Webhook wurde hinzugefügt. settings.update_webhook=Webhook aktualisieren settings.update_hook_success=Webhook wurde aktualisiert. settings.delete_webhook=Webhook entfernen settings.recent_deliveries=Letzte Zustellungen settings.hook_type=Hook Typ +settings.add_slack_hook_desc=Slack-Integration zu deinem Repository hinzufügen. settings.slack_token=Token settings.slack_domain=Domain settings.slack_channel=Kanal +settings.add_discord_hook_desc=Discord-Integration zu deinem Repository hinzufügen. +settings.add_dingtalk_hook_desc=Dingtalk-Integration zu deinem Repository hinzufügen. settings.deploy_keys=Deploy-Schlüssel settings.add_deploy_key=Deploy-Schlüssel hinzufügen +settings.deploy_key_desc=Deploy-Keys haben nur Lesezugriff auf das Repository. settings.is_writable=Erlaube Schreibzugriff +settings.is_writable_info=Erlaube diesem Deploy-Key auf das Repository zu pushen. +settings.no_deploy_keys=Noch keine Deploy-Keys vorhanden. settings.title=Titel settings.deploy_key_content=Inhalt +settings.key_been_used=Ein Deploy-Key mit identischem Inhalt wird bereits verwendet. +settings.key_name_used=Ein Deploy-Key mit diesem Namen existiert bereits. +settings.add_key_success=Der Deploy-Key '%s' wurde erfolgreich hinzugefügt. +settings.deploy_key_deletion=Deploy-Key löschen +settings.deploy_key_deletion_desc=Nach dem Löschen wird dieser Deploy-Key keinen Zugriff mehr auf dieses Repository haben. Fortfahren? +settings.deploy_key_deletion_success=Der Deploy-Key wurde entfernt. settings.branches=Branches settings.protected_branch=Branch-Protection settings.protected_branch_can_push=Push erlauben? @@ -964,11 +1040,24 @@ settings.protected_branch_can_push_yes=Du kannst pushen settings.protected_branch_can_push_no=Du kannst nicht pushen settings.branch_protection=Branch-Schutz" für Branch '%s' settings.protect_this_branch=Brach-Schutz aktivieren +settings.protect_this_branch_desc=Verhindere Löschen und deaktiviere Git force push auf diese Branch. +settings.protect_whitelist_committers=Push-Whitelist aktivieren +settings.protect_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Push-Beschränkungen zu umgehen. +settings.protect_whitelist_users=Nutzer, die pushen dürfen: settings.protect_whitelist_search_users=Benutzer suchen… +settings.protect_whitelist_teams=Teams, die pushen dürfen: settings.protect_whitelist_search_teams=Suche nach Teams… +settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren +settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diese Branch zu mergen. +settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen: +settings.protect_merge_whitelist_teams=Teams, die mergen dürfen: settings.add_protected_branch=Schutz aktivieren settings.delete_protected_branch=Schutz deaktivieren +settings.update_protect_branch_success=Branch-protection für die Branch '%s' wurde geändert. +settings.remove_protected_branch_success=Branch-protection für die Branch '%s' wurde deaktiviert. settings.protected_branch_deletion=Brach-Schutz deaktivieren +settings.protected_branch_deletion_desc=Wenn du die Branch-Protection deaktivierst, können alle Nutzer mit Schreibrechten auf die Branch pushen. Fortfahren? +settings.default_branch_desc=Wähle eine Standardbranch für Pull-Requests und Code-Commits: settings.choose_branch=Wähle eine Branch… settings.no_protected_branch=Es gibt keine geschützten Branches. @@ -985,6 +1074,7 @@ diff.view_file=Datei anzeigen diff.file_suppressed=Datei-Diff unterdrückt, da er zu groß ist diff.too_many_files=Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden. +releases.desc=Behalte den Überblick über Versionen und Downloads. release.releases=Releases release.new_release=Neues Release release.draft=Entwurf @@ -993,8 +1083,11 @@ release.stable=Stabil release.edit=bearbeiten release.ahead=%d Commits zu %s seit diesem Release release.source_code=Quelltext +release.new_subheader=In Releases werden Projektversionen verwaltet. +release.edit_subheader=In Releases werden Projektversionen verwaltet. release.tag_name=Tag-Name release.target=Ziel +release.tag_helper=Wähle einen existierenden oder erstelle einen neuen Tag. release.title=Titel release.content=Inhalt release.write=Schreiben @@ -1008,6 +1101,7 @@ release.save_draft=Entwurf speichern release.edit_release=Release aktualisieren release.delete_release=Release löschen release.deletion=Release löschen +release.deletion_desc=Wenn du ein Release löschst, wird das den dazugehörigen Git-Tag aus dem Repository löschen. Repository-Inhalte und History bleiben unverändert. Fortfahren? release.deletion_success=Das Release wurde gelöscht. release.tag_name_already_exist=Ein Release mit diesem Tag existiert bereits. release.tag_name_invalid=Der Tag-Name ist ungültig. @@ -1019,12 +1113,16 @@ branch.already_exists=Eine Branch mit dem Namen '%s' existiert bereits. branch.delete_head=Löschen branch.delete=Branch '%s' löschen branch.delete_html=Branch löschen +branch.delete_desc=Das Löschen einer Branch ist permanent. Es KANN NICHT Rückgängig gemacht werden. Fortfahren? branch.deletion_success=Branch '%s' wurde gelöscht. branch.deletion_failed=Branch '%s' konnte nicht gelöscht werden. +branch.delete_branch_has_new_commits=Die Branch '%s' kann nicht gelöscht weden, da seit dem letzten Merge neue Commits hinzugefügt wurden. branch.create_branch=Erstelle Branch %s branch.create_from=von '%s' branch.create_success=Branch '%s' wurde erstellt. branch.branch_already_exists=Branch '%s' existiert bereits in diesem Repository. +branch.branch_name_conflict=Der Branch-Name '%s' steht in Konflikt mit der bestehendem Branch '%s'. +branch.tag_collision=Branch '%s' kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit dem selben Namen existiert. branch.deleted_by=Von %s gelöscht branch.restore_success=Branch '%s' wurde wiederhergestellt. branch.restore_failed=Wiederherstellung der Branch '%s' fehlgeschlagen. @@ -1049,10 +1147,12 @@ org_desc=Beschreibung team_name=Teamname team_desc=Beschreibung team_name_helper=Teamnamen sollten kurz und einprägsam sein. +team_desc_helper=Beschreibe den Zweck oder die Rolle des Teams. team_permission_desc=Berechtigungen team_unit_desc=Zugriff auf Repositorybereiche erlauben form.name_reserved=Der Organisationsname '%s' ist reserviert. +form.name_pattern_not_allowed=Das Muster '%s' ist in Organisationsnamen nicht erlaubt. form.create_org_not_allowed=Du bist nicht berechtigt eine Organisation zu erstellen. settings=Einstellungen @@ -1062,9 +1162,11 @@ settings.website=Webseite settings.location=Standort settings.update_settings=Einstellungen speichern settings.update_setting_success=Organisationseinstellungen wurden aktualisiert. +settings.change_orgname_prompt=Hinweis: Die Änderung des Organisationsnamens ändert auch ihre URL. settings.update_avatar_success=Der Organisationsavatar wurde aktualisiert. settings.delete=Organisation löschen settings.delete_account=Diese Organisation löschen +settings.delete_prompt=Die Organisation wird dauerhaft gelöscht. Dies KANN NICHT rückgängig gemacht werden! settings.confirm_delete_account=Löschen settings.delete_org_title=Organisation löschen settings.delete_org_desc=Diese Organisation wird dauerhaft gelöscht. Fortfahren? @@ -1072,6 +1174,8 @@ settings.hooks_desc=Webhooks hinzufügen, die für alle Reposit members.membership_visibility=Sichtbarkeit der Mitgliedschaft: members.public=Sichtbar +members.public_helper=verstecken +members.private=Versteckt members.private_helper=sichtbar machen members.member_role=Mitgliedsrolle: members.owner=Besitzer @@ -1086,16 +1190,22 @@ teams.leave=Verlassen teams.read_access=Lesezugriff teams.read_access_helper=Mitglieder können Teamrepositories ansehen und klonen. teams.write_access=Schreibzugriff +teams.write_access_helper=Mitglieder können Teamrepositories ansehen und auf sie pushen. teams.admin_access=Administratorzugang teams.admin_access_helper=Mitglieder können auf Team Repositories "pushen", von ihnen "pullen" und Mitarbeiter hinzufügen. teams.no_desc=Dieses Team hat keine Beschreibung teams.settings=Einstellungen +teams.owners_permission_desc=Besitzer haben vollen Zugriff auf alle Repositories und Admin-Rechte für diese Organisation. teams.members=Teammitglieder teams.update_settings=Einstellungen aktualisieren teams.delete_team=Team löschen teams.add_team_member=Teammitglied hinzufügen teams.delete_team_title=Team löschen +teams.delete_team_desc=Das Löschen eines Teams wiederruft den Repository-Zugriff für seine Mitglieder. Fortfahren? teams.delete_team_success=Das Team wurde gelöscht. +teams.read_permission_desc=Dieses Team hat Lesezugriff: Mitglieder können Team-Repositories einsehen und klonen. +teams.write_permission_desc=Dieses Team hat Schreibzugriff: Mitglieder können Team-Repositories einsehen und darauf pushen. +teams.admin_permission_desc=Dieses Team hat Adminzugriff: Mitglieder dieses Teams können Team-Repositories ansehen, auf sie pushen und Mitarbeiter hinzufügen. teams.repositories=Team-Repositories teams.search_repo_placeholder=Repository durchsuchen… teams.add_team_repository=Team-Repository hinzufügen @@ -1116,7 +1226,9 @@ last_page=Letzte total=Gesamt: %d dashboard.statistic=Übersicht +dashboard.operations=Wartungsoperationen dashboard.system_status=System-Status +dashboard.statistic_info=Gitea's Datenbank hat %d Benutzer, %d Organisationen, %d öffentliche Schlüssel, %d Repositories, %d Beobachtungen, %d Favoriten, %d Aktionen, %d Zugriffe, %d Issues, %d Kommentare, %d Konten sozialer Netzwerke, %d Gefolgte, %d Mirrors, %d Releases, %d Login-Quellen, %d Webhooks, %d Meilensteine, %d Label, %d Hook-Tasks, %d Teams, %d Aktualisierungs-Tasks, %d Anhänge. dashboard.operation_name=Name der Operation dashboard.operation_switch=Wechseln dashboard.operation_run=Ausführen @@ -1130,11 +1242,16 @@ dashboard.delete_missing_repos=Alle Repository-Datensätze mit verlorenen gegang dashboard.delete_missing_repos_success=Alle Repository-Datensätze mit verlorenen Git-Dateien wurden gelöscht. dashboard.git_gc_repos=Garbage Collection auf Repositories ausführen dashboard.git_gc_repos_success=Alle Repositories haben Garbage Collection beendet. +dashboard.resync_all_sshkeys='.ssh/authorized_keys'-Datei mit Gitea SSH-Keys neu schreiben. (Wenn Du den eingebauten SSH Server nutzt, musst du das nicht ausführen.) +dashboard.resync_all_sshkeys_success=Alle von Gitea verwalteten öffentlichen Schlüssel wurden neu geschrieben. +dashboard.resync_all_hooks=Synchronisiere pre-receive, update und post-receive Hooks für alle Repositories. +dashboard.resync_all_hooks_success=Alle pre-receive, update und post-receive Repository-Hooks wurden synchronisiert. dashboard.reinit_missing_repos=Alle Git-Repositories mit Einträgen neu einlesen dashboard.reinit_missing_repos_success=Alle verlorenen Git-Repositories mit existierenden Einträgen wurden erfolgreich aktualisiert. dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.sync_external_users_started=Externe Benutzersynchronisation gestartet. dashboard.git_fsck=Healthchecks auf alle Repositories ausführen +dashboard.git_fsck_started=Repository-Healthchecks gestartet. dashboard.server_uptime=Server-Uptime dashboard.current_goroutine=Aktuelle Goroutinen dashboard.current_memory_usage=Aktuelle Speichernutzung @@ -1173,21 +1290,28 @@ users.admin=Administrator users.repos=Repositories users.created=Registriert am users.last_login=Letzte Anmeldung +users.never_login=Hat sich noch nie eingeloggt +users.send_register_notify=Benutzer-Registrierungsbenachrichtigung senden users.new_success=Der Account '%s' wurde erstellt. users.edit=Bearbeiten users.auth_source=Authentifizierungsquelle users.local=Lokal +users.auth_login_name=Anmeldename zur Authentifizierung +users.password_helper=Passwort leerlassen, um es nicht zu verändern. users.update_profile_success=Der Account '%s' wurde aktualisiert. users.edit_account=Benutzerkonto bearbeiten users.max_repo_creation=Maximale Anzahl Repositories users.max_repo_creation_desc=(Gib -1 ein, um das globale Standardlimit zu verwenden.) users.is_activated=Account ist aktiviert +users.prohibit_login=Anmelden deaktivieren users.is_admin=Ist Administrator users.allow_git_hook=Darf "Git Hooks" erstellen users.allow_import_local=Darf lokale Repositories importieren users.allow_create_organization=Darf Organisationen erstellen users.update_profile=Benutzerkonto aktualisieren users.delete_account=Benutzerkonto löschen +users.still_own_repo=Dieser Benutzer besitzt noch mindestens ein Repository. Bitte lösche oder übertrage diese zuerst. +users.still_has_org=Dieser Nutzer ist Mitglied einer Organisation. Du musst ihn zuerst aus allen Organisationen entfernen. users.deletion_success=Der Account wurde gelöscht. orgs.org_manage_panel=Organisationsverwaltung @@ -1220,12 +1344,17 @@ auths.host=Host auths.port=Port auths.bind_dn=DN binden auths.bind_password=Passwort binden +auths.bind_password_helper=Achtung: Das Passwort wird im Klartext gespeichert. Benutze wenn möglich einen Account mit nur Lesezugriff. auths.user_base=Basis für Benutzersuche auths.user_dn=Benutzer DN auths.attribute_username=Benutzername Attribut +auths.attribute_username_placeholder=Leerlassen, um den in Gitea eingegebenen Benutzernamen zu verwenden. auths.attribute_name=Vornamensattribut auths.attribute_surname=Nachnamensattribut auths.attribute_mail=E-Mail Attribut +auths.attributes_in_bind=Hole Attribute im Bind-Kontext +auths.use_paged_search=Seitensuche verwenden +auths.search_page_size=Seitengröße auths.filter=Benutzerfilter auths.admin_filter=Admin Filter auths.ms_ad_sa=MS AD Suchattribute @@ -1233,6 +1362,7 @@ auths.smtp_auth=SMTP-Authentifizierungstyp auths.smtphost=SMTP-Host auths.smtpport=SMTP-Port auths.allowed_domains=Erlaubte Domains +auths.allowed_domains_helper=Leerlassen, um alle Domains zuzulassen. Trenne mehrere Domänen mit einem Komma (','). auths.enable_tls=TLS-Verschlüsselung aktivieren auths.skip_tls_verify=TLS Verifikation überspringen auths.pam_service_name=PAM Dienstname @@ -1240,6 +1370,7 @@ auths.oauth2_provider=OAuth2 Anbieter auths.oauth2_clientID=Client-ID (Schlüssel) auths.oauth2_clientSecret=Client-Secret auths.openIdConnectAutoDiscoveryURL=OpenID Connect Auto Discovery URL +auths.oauth2_use_custom_url=Benutzerdefinierte URLs anstelle von Standard-URLs verwenden auths.oauth2_tokenURL=Token-URL auths.oauth2_authURL=Authorisierungs-URL auths.oauth2_profileURL=Profil-URL @@ -1249,11 +1380,14 @@ auths.tips=Tipps auths.tips.oauth2.general=OAuth2 Authentifizierung auths.tips.oauth2.general.tip=Beim Registrieren einer neuen OAuth2 Authentifizierung sollte die Callback/Weiterleitungs-URL /user/oauth2//callback sein. auths.tip.oauth2_provider=OAuth2 Anbieter +auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user//oauth-consumers/new und füge die Berechtigung "Account"-"Read" hinzu. auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps. auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt "Facebook Login" hinzu. auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth Anwendung. auths.tip.gitlab=Erstelle unter https://gitlab.com/profile/applications eine neue Anwendung. +auths.tip.google_plus=Du erhältst die OAuth2 Client Zugangsdaten in der Google API Console unter https://console.developers.google.com/ auths.tip.openid_connect=Benutze die OpenID Connect Discovery URL (/.well-known/openid-configuration) als Endpunkt. +auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option “Allow this application to be used to Sign in with Twitter” aktiviert ist auths.edit=Authentifikationsquelle bearbeiten auths.activated=Diese Authentifikationsquelle ist aktiviert auths.new_success=Die Authentifizierung "%s" wurde hinzugefügt. @@ -1309,6 +1443,7 @@ config.db_path_helper=(für "sqlite3" und "tidb") config.service_config=Service-Konfiguration config.register_email_confirm=E-Mail-Bestätigung benötigt zum Registrieren config.disable_register=Selbstegistrierung deaktivieren +config.allow_only_external_registration=Registrierung nur über externe Services aktiveren config.enable_openid_signup=OpenID Selbstregistrierung aktivieren config.enable_openid_signin=OpenID Anmeldung aktivieren config.show_registration_button=Schaltfläche zum Registrieren anzeigen @@ -1319,7 +1454,11 @@ config.enable_captcha=CAPTCHA aktivieren config.active_code_lives=Aktivierungscode Lebensdauer config.reset_password_code_lives=Ablaufdatum des Passworts zurücksetzen config.default_keep_email_private=E-Mail-Adressen standardmäßig verbergen +config.default_allow_create_organization=Erstellen von Organisationen standarmäßig erlauben config.enable_timetracking=Zeiterfassung aktivieren +config.default_enable_timetracking=Zeiterfassung standardmäßig aktivieren +config.default_allow_only_contributors_to_track_time=Nur Mitarbeitern erlauben, die Zeiterfassung zu nutzen +config.no_reply_address=Versteckte E-Mail-Domain config.webhook_config=Webhook-Konfiguration config.queue_length=Warteschlangenlänge @@ -1357,6 +1496,7 @@ config.session_life_time=Session-Lebensdauer config.https_only=Nur HTTPS config.cookie_life_time=Cookie-Lebensdauer +config.picture_config=Avatar-Konfiguration config.picture_service=Bilderservice config.disable_gravatar=Gravatar deaktivieren config.enable_federated_avatar=Föderierte Profilbilder einschalten @@ -1381,6 +1521,7 @@ monitor.name=Name monitor.schedule=Zeitplan monitor.next=Nächste Ausführung monitor.previous=Letzte Ausführung +monitor.execute_times=Ausführungen monitor.process=Laufende Prozesse monitor.desc=Beschreibung monitor.start=Startzeit @@ -1460,8 +1601,10 @@ mark_all_as_read=Alle als gelesen markieren [gpg] error.extract_sign=Die Signatur konnte nicht extrahiert werden error.generate_hash=Es konnte kein Hash vom Commit generiert werden +error.no_committer_account=Es ist kein Benutzerkonto mit dieser Commiter-Email verbunden error.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden error.not_signed_commit=Kein signierter Commit +error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Keys des Commiter-Kontos [units] error.no_unit_allowed_repo=Du hast keine Berechtigung auf einen Bereich dieses Repositories zuzugreifen. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index a6c11cee9c..7aed99a37c 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -117,6 +117,7 @@ federated_avatar_lookup=Habilitar avatares federativos federated_avatar_lookup_popup=Habilitar a busca federativa de avatares a usar o serviço federativo de código aberto baseado no libravatar. disable_registration=Desabilitar auto-cadastro disable_registration_popup=Desabilitar auto-cadastro de usuário. Somente os administradores serão capazes de criar novas contas de usuário. +allow_only_external_registration_popup=Habilitar o cadastro apenas por meio de serviços externos. openid_signin=Habilitar acesso via OpenID openid_signin_popup=Habilitar o acesso de usuários via OpenID. openid_signup=Habilitar o auto-cadastro via OpenID @@ -1442,6 +1443,7 @@ config.db_path_helper=(para "sqlite3" e "tidb") config.service_config=Configuração do serviço config.register_email_confirm=Exigir confirmação de e-mail para se cadastrar config.disable_register=Desabilitar auto-cadastro +config.allow_only_external_registration=Habilitar o cadastro apenas por meio de serviços externos config.enable_openid_signup=Habilitar o auto-cadastro via OpenID config.enable_openid_signin=Habilitar acesso via OpenID config.show_registration_button=Mostrar botão de cadastro diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index a4281beda0..c6487d7c4e 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -150,6 +150,8 @@ organizations=Організації search=Пошук code=Код repo_no_results=Відповідних репозиторіїв не знайдено. +user_no_results=Відповідних користувачів не знайдено. +org_no_results=Відповідних організацій не знайдено. code_search_results=Результати пошуку '%s' [auth] @@ -422,7 +424,7 @@ milestones=Етап commits=Коміти commit=Змина releases=Релізи -file_raw=Raw +file_raw=Неформатований file_history=Історія file_view_raw=Перегляд Raw file_permalink=Постійне посилання @@ -558,8 +560,13 @@ issues.num_participants=%d учасників issues.attachment.open_tab=`Натисніть щоб побачити "%s" у новій вкладці` issues.subscribe=Підписатися issues.unsubscribe=Відписатися +issues.tracker=Відстеження часу issues.start_tracking_short=Запустити +issues.start_tracking=Почати відстеження часу +issues.start_tracking_history=`почав працювати %s` +issues.tracking_already_started=`Ви вже почали відстежувати час для цієї проблеми!` issues.stop_tracking=Стоп +issues.add_time=Вручну додати час issues.add_time_short=Додати час issues.add_time_cancel=Відміна issues.add_time_hours=Години @@ -569,6 +576,7 @@ issues.cancel_tracking=Відміна issues.due_date=Термін дії issues.due_date_form_add=Додати дату завершення issues.due_date_form_remove=Видалити дату завершення +issues.due_date_not_set=Термін виконання не встановлений. pulls.new=Новий запит на злиття pulls.compare_changes=Новий запит на злиття @@ -578,6 +586,7 @@ pulls.filter_branch=Фільтр по гілці pulls.no_results=Результатів не знайдено. pulls.create=Створити запит на злиття pulls.title_desc=хоче злити %[1]d комітів з %[2]s до %[3]s +pulls.merged_title_desc=злито %[1]d комітів з %[2]s до %[3]s %[4]s pulls.tab_conversation=Обговорення pulls.tab_commits=Коміти pulls.tab_files=Змінені файли @@ -682,6 +691,7 @@ settings.external_tracker_url=URL зовнішньої системи відст settings.tracker_url_format=Формат URL зовнішнього трекера задач settings.tracker_issue_style.numeric=Цифровий settings.tracker_issue_style.alphanumeric=Буквено-цифровий +settings.enable_timetracker=Увімкнути відстеження часу settings.admin_settings=Налаштування адміністратора settings.danger_zone=Небезпечна зона settings.new_owner_has_same_repo=Новий власник вже має репозиторій з такою назвою. Будь ласка, виберіть інше ім'я. @@ -721,6 +731,7 @@ settings.event_pull_request=Запити до злиття settings.event_push=Push settings.event_repository=Репозиторій settings.event_repository_desc=Репозиторій створений або видалено. +settings.active=Додавати інформацію про події settings.update_webhook=Оновити веб-хук settings.hook_type=Тип хука settings.slack_token=Токен @@ -833,6 +844,7 @@ teams.settings=Налаштування teams.members=Учасники команди teams.update_settings=Оновити налаштування teams.add_team_member=Додати учасника команди +teams.write_permission_desc=Ця команда надає доступ на запис: учасники можуть отримувати й виконувати push команди до репозитрію. teams.add_team_repository=Додати репозиторій команди teams.remove_repo=Видалити @@ -994,6 +1006,7 @@ config.enable_captcha=Увімкнути CAPTCHA config.active_code_lives=Час актуальності кода підтвердження config.default_keep_email_private=Приховати адресу електронної пошти за замовчуванням config.default_allow_create_organization=Дозволити створення організацій за замовчуванням +config.enable_timetracking=Увімкнути відстеження часу config.default_enable_timetracking=Увімкнути відстеження часу за замовчуванням config.webhook_config=Конфігурація web-хуків diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index c7e94726f4..e82892f08b 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -117,6 +117,7 @@ federated_avatar_lookup=启用 Federated 头像 federated_avatar_lookup_popup=启用 Federated Avatars 查找以使用开源的 Libravatar 服务。 disable_registration=禁止用户自助注册 disable_registration_popup=禁用用户自助注册。只有管理员才能创建新的用户帐户。 +allow_only_external_registration_popup=仅允许通过外部服务注册。 openid_signin=启用 OpenID 登录 openid_signin_popup=启用通过 OpenID 登录 openid_signup=启用 OpenID 自助注册 @@ -1442,6 +1443,7 @@ config.db_path_helper=(用于 "sqlite3" 和 "tidb") config.service_config=服务配置 config.register_email_confirm=需要电子邮件确认注册 config.disable_register=禁止用户注册 +config.allow_only_external_registration=仅允许通过外部服务注册 config.enable_openid_signup=启用 OpenID 自注册 config.enable_openid_signin=启用 OpenID 登录 config.show_registration_button=显示注册按钮