You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

setting.go 24 kB

11 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
8 years ago
11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
8 years ago
Add support for federated avatars (#3320) * Add support for federated avatars Fixes #3105 Removes avatar fetching duplication code Adds an "Enable Federated Avatar" checkbox in user settings (defaults to unchecked) Moves avatar settings all in the same form, making local and remote avatars mutually exclusive Renames UploadAvatarForm to AvatarForm as it's not anymore only for uploading * Run gofmt on all modified files * Move Avatar form in its own page * Add go-libravatar dependency to vendor/ dir Hopefully helps with accepting the contribution. See also #3214 * Revert "Add go-libravatar dependency to vendor/ dir" This reverts commit a8cb93ae640bbb90f7d25012fc257bda9fae9b82. * Make federated avatar setting a global configuration Removes the per-user setting * Move avatar handling back to base tool, disable federated avatar in offline mode * Format, handle error * Properly set fallback host * Use unsupported github.com mirror for importing go-libravatar * Remove comment showing life exists outside of github.com ... pity, but contribution would not be accepted otherwise * Use Combo for Get and Post methods over /avatar * FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR * Fix persistance of federated avatar lookup checkbox at install time * Federated Avatars -> Enable Federated Avatars * Use len(string) == 0 instead of string == "" * Move import line where it belong See https://github.com/Unknwon/go-code-convention/blob/master/en-US/import_packages.md Pity the import url is still the unofficial one, but oh well... * Save a line (and waste much more expensive time) * Remove redundant parens * Remove an empty line * Remove empty lines * Reorder lines to make diff smaller * Remove another newline Unknwon review got me start a fight against newlines * Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE On re-reading the diff I figured what Unknwon meant here: https://github.com/gogits/gogs/pull/3320/files#r73741106 * Remove newlines that weren't there before my intervention
9 years ago
8 years ago
Add support for federated avatars (#3320) * Add support for federated avatars Fixes #3105 Removes avatar fetching duplication code Adds an "Enable Federated Avatar" checkbox in user settings (defaults to unchecked) Moves avatar settings all in the same form, making local and remote avatars mutually exclusive Renames UploadAvatarForm to AvatarForm as it's not anymore only for uploading * Run gofmt on all modified files * Move Avatar form in its own page * Add go-libravatar dependency to vendor/ dir Hopefully helps with accepting the contribution. See also #3214 * Revert "Add go-libravatar dependency to vendor/ dir" This reverts commit a8cb93ae640bbb90f7d25012fc257bda9fae9b82. * Make federated avatar setting a global configuration Removes the per-user setting * Move avatar handling back to base tool, disable federated avatar in offline mode * Format, handle error * Properly set fallback host * Use unsupported github.com mirror for importing go-libravatar * Remove comment showing life exists outside of github.com ... pity, but contribution would not be accepted otherwise * Use Combo for Get and Post methods over /avatar * FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR * Fix persistance of federated avatar lookup checkbox at install time * Federated Avatars -> Enable Federated Avatars * Use len(string) == 0 instead of string == "" * Move import line where it belong See https://github.com/Unknwon/go-code-convention/blob/master/en-US/import_packages.md Pity the import url is still the unofficial one, but oh well... * Save a line (and waste much more expensive time) * Remove redundant parens * Remove an empty line * Remove empty lines * Reorder lines to make diff smaller * Remove another newline Unknwon review got me start a fight against newlines * Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE On re-reading the diff I figured what Unknwon meant here: https://github.com/gogits/gogs/pull/3320/files#r73741106 * Remove newlines that weren't there before my intervention
9 years ago
10 years ago
Add support for federated avatars (#3320) * Add support for federated avatars Fixes #3105 Removes avatar fetching duplication code Adds an "Enable Federated Avatar" checkbox in user settings (defaults to unchecked) Moves avatar settings all in the same form, making local and remote avatars mutually exclusive Renames UploadAvatarForm to AvatarForm as it's not anymore only for uploading * Run gofmt on all modified files * Move Avatar form in its own page * Add go-libravatar dependency to vendor/ dir Hopefully helps with accepting the contribution. See also #3214 * Revert "Add go-libravatar dependency to vendor/ dir" This reverts commit a8cb93ae640bbb90f7d25012fc257bda9fae9b82. * Make federated avatar setting a global configuration Removes the per-user setting * Move avatar handling back to base tool, disable federated avatar in offline mode * Format, handle error * Properly set fallback host * Use unsupported github.com mirror for importing go-libravatar * Remove comment showing life exists outside of github.com ... pity, but contribution would not be accepted otherwise * Use Combo for Get and Post methods over /avatar * FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR * Fix persistance of federated avatar lookup checkbox at install time * Federated Avatars -> Enable Federated Avatars * Use len(string) == 0 instead of string == "" * Move import line where it belong See https://github.com/Unknwon/go-code-convention/blob/master/en-US/import_packages.md Pity the import url is still the unofficial one, but oh well... * Save a line (and waste much more expensive time) * Remove redundant parens * Remove an empty line * Remove empty lines * Reorder lines to make diff smaller * Remove another newline Unknwon review got me start a fight against newlines * Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE On re-reading the diff I figured what Unknwon meant here: https://github.com/gogits/gogs/pull/3320/files#r73741106 * Remove newlines that weren't there before my intervention
9 years ago
10 years ago
10 years ago
Add support for federated avatars (#3320) * Add support for federated avatars Fixes #3105 Removes avatar fetching duplication code Adds an "Enable Federated Avatar" checkbox in user settings (defaults to unchecked) Moves avatar settings all in the same form, making local and remote avatars mutually exclusive Renames UploadAvatarForm to AvatarForm as it's not anymore only for uploading * Run gofmt on all modified files * Move Avatar form in its own page * Add go-libravatar dependency to vendor/ dir Hopefully helps with accepting the contribution. See also #3214 * Revert "Add go-libravatar dependency to vendor/ dir" This reverts commit a8cb93ae640bbb90f7d25012fc257bda9fae9b82. * Make federated avatar setting a global configuration Removes the per-user setting * Move avatar handling back to base tool, disable federated avatar in offline mode * Format, handle error * Properly set fallback host * Use unsupported github.com mirror for importing go-libravatar * Remove comment showing life exists outside of github.com ... pity, but contribution would not be accepted otherwise * Use Combo for Get and Post methods over /avatar * FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR * Fix persistance of federated avatar lookup checkbox at install time * Federated Avatars -> Enable Federated Avatars * Use len(string) == 0 instead of string == "" * Move import line where it belong See https://github.com/Unknwon/go-code-convention/blob/master/en-US/import_packages.md Pity the import url is still the unofficial one, but oh well... * Save a line (and waste much more expensive time) * Remove redundant parens * Remove an empty line * Remove empty lines * Reorder lines to make diff smaller * Remove another newline Unknwon review got me start a fight against newlines * Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE On re-reading the diff I figured what Unknwon meant here: https://github.com/gogits/gogs/pull/3320/files#r73741106 * Remove newlines that weren't there before my intervention
9 years ago
Add support for federated avatars (#3320) * Add support for federated avatars Fixes #3105 Removes avatar fetching duplication code Adds an "Enable Federated Avatar" checkbox in user settings (defaults to unchecked) Moves avatar settings all in the same form, making local and remote avatars mutually exclusive Renames UploadAvatarForm to AvatarForm as it's not anymore only for uploading * Run gofmt on all modified files * Move Avatar form in its own page * Add go-libravatar dependency to vendor/ dir Hopefully helps with accepting the contribution. See also #3214 * Revert "Add go-libravatar dependency to vendor/ dir" This reverts commit a8cb93ae640bbb90f7d25012fc257bda9fae9b82. * Make federated avatar setting a global configuration Removes the per-user setting * Move avatar handling back to base tool, disable federated avatar in offline mode * Format, handle error * Properly set fallback host * Use unsupported github.com mirror for importing go-libravatar * Remove comment showing life exists outside of github.com ... pity, but contribution would not be accepted otherwise * Use Combo for Get and Post methods over /avatar * FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR * Fix persistance of federated avatar lookup checkbox at install time * Federated Avatars -> Enable Federated Avatars * Use len(string) == 0 instead of string == "" * Move import line where it belong See https://github.com/Unknwon/go-code-convention/blob/master/en-US/import_packages.md Pity the import url is still the unofficial one, but oh well... * Save a line (and waste much more expensive time) * Remove redundant parens * Remove an empty line * Remove empty lines * Reorder lines to make diff smaller * Remove another newline Unknwon review got me start a fight against newlines * Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE On re-reading the diff I figured what Unknwon meant here: https://github.com/gogits/gogs/pull/3320/files#r73741106 * Remove newlines that weren't there before my intervention
9 years ago
Add support for federated avatars (#3320) * Add support for federated avatars Fixes #3105 Removes avatar fetching duplication code Adds an "Enable Federated Avatar" checkbox in user settings (defaults to unchecked) Moves avatar settings all in the same form, making local and remote avatars mutually exclusive Renames UploadAvatarForm to AvatarForm as it's not anymore only for uploading * Run gofmt on all modified files * Move Avatar form in its own page * Add go-libravatar dependency to vendor/ dir Hopefully helps with accepting the contribution. See also #3214 * Revert "Add go-libravatar dependency to vendor/ dir" This reverts commit a8cb93ae640bbb90f7d25012fc257bda9fae9b82. * Make federated avatar setting a global configuration Removes the per-user setting * Move avatar handling back to base tool, disable federated avatar in offline mode * Format, handle error * Properly set fallback host * Use unsupported github.com mirror for importing go-libravatar * Remove comment showing life exists outside of github.com ... pity, but contribution would not be accepted otherwise * Use Combo for Get and Post methods over /avatar * FEDERATED_AVATAR -> ENABLE_FEDERATED_AVATAR * Fix persistance of federated avatar lookup checkbox at install time * Federated Avatars -> Enable Federated Avatars * Use len(string) == 0 instead of string == "" * Move import line where it belong See https://github.com/Unknwon/go-code-convention/blob/master/en-US/import_packages.md Pity the import url is still the unofficial one, but oh well... * Save a line (and waste much more expensive time) * Remove redundant parens * Remove an empty line * Remove empty lines * Reorder lines to make diff smaller * Remove another newline Unknwon review got me start a fight against newlines * Move DISABLE_GRAVATAR and ENABLE_FEDERATED_AVATAR after OFFLINE_MODE On re-reading the diff I figured what Unknwon meant here: https://github.com/gogits/gogs/pull/3320/files#r73741106 * Remove newlines that weren't there before my intervention
9 years ago
10 years ago
10 years ago
11 years ago
8 years ago
11 years ago
11 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
8 years ago
Oauth2 consumer (#679) * initial stuff for oauth2 login, fails on: * login button on the signIn page to start the OAuth2 flow and a callback for each provider Only GitHub is implemented for now * show login button only when the OAuth2 consumer is configured (and activated) * create macaron group for oauth2 urls * prevent net/http in modules (other then oauth2) * use a new data sessions oauth2 folder for storing the oauth2 session data * add missing 2FA when this is enabled on the user * add password option for OAuth2 user , for use with git over http and login to the GUI * add tip for registering a GitHub OAuth application * at startup of Gitea register all configured providers and also on adding/deleting of new providers * custom handling of errors in oauth2 request init + show better tip * add ExternalLoginUser model and migration script to add it to database * link a external account to an existing account (still need to handle wrong login and signup) and remove if user is removed * remove the linked external account from the user his settings * if user is unknown we allow him to register a new account or link it to some existing account * sign up with button on signin page (als change OAuth2Provider structure so we can store basic stuff about providers) * from gorilla/sessions docs: "Important Note: If you aren't using gorilla/mux, you need to wrap your handlers with context.ClearHandler as or else you will leak memory!" (we're using gorilla/sessions for storing oauth2 sessions) * use updated goth lib that now supports getting the OAuth2 user if the AccessToken is still valid instead of re-authenticating (prevent flooding the OAuth2 provider)
8 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package user
  5. import (
  6. "bytes"
  7. "errors"
  8. "fmt"
  9. "io/ioutil"
  10. "strings"
  11. "github.com/Unknwon/com"
  12. "github.com/Unknwon/i18n"
  13. "github.com/pquerna/otp"
  14. "github.com/pquerna/otp/totp"
  15. "encoding/base64"
  16. "html/template"
  17. "image/png"
  18. "code.gitea.io/gitea/models"
  19. "code.gitea.io/gitea/modules/auth"
  20. "code.gitea.io/gitea/modules/base"
  21. "code.gitea.io/gitea/modules/context"
  22. "code.gitea.io/gitea/modules/log"
  23. "code.gitea.io/gitea/modules/setting"
  24. )
  25. const (
  26. tplSettingsProfile base.TplName = "user/settings/profile"
  27. tplSettingsAvatar base.TplName = "user/settings/avatar"
  28. tplSettingsEmails base.TplName = "user/settings/email"
  29. tplSettingsKeys base.TplName = "user/settings/keys"
  30. tplSettingsSocial base.TplName = "user/settings/social"
  31. tplSettingsApplications base.TplName = "user/settings/applications"
  32. tplSettingsTwofa base.TplName = "user/settings/twofa"
  33. tplSettingsTwofaEnroll base.TplName = "user/settings/twofa_enroll"
  34. tplSettingsAccountLink base.TplName = "user/settings/account_link"
  35. tplSettingsOrganization base.TplName = "user/settings/organization"
  36. tplSettingsRepositories base.TplName = "user/settings/repos"
  37. tplSettingsDelete base.TplName = "user/settings/delete"
  38. tplSettingsSecurity base.TplName = "user/settings/security"
  39. )
  40. // Settings render user's profile page
  41. func Settings(ctx *context.Context) {
  42. ctx.Data["Title"] = ctx.Tr("settings")
  43. ctx.Data["PageIsSettingsProfile"] = true
  44. ctx.HTML(200, tplSettingsProfile)
  45. }
  46. func handleUsernameChange(ctx *context.Context, newName string) {
  47. // Non-local users are not allowed to change their username.
  48. if len(newName) == 0 || !ctx.User.IsLocal() {
  49. return
  50. }
  51. // Check if user name has been changed
  52. if ctx.User.LowerName != strings.ToLower(newName) {
  53. if err := models.ChangeUserName(ctx.User, newName); err != nil {
  54. switch {
  55. case models.IsErrUserAlreadyExist(err):
  56. ctx.Flash.Error(ctx.Tr("form.username_been_taken"))
  57. ctx.Redirect(setting.AppSubURL + "/user/settings")
  58. case models.IsErrEmailAlreadyUsed(err):
  59. ctx.Flash.Error(ctx.Tr("form.email_been_used"))
  60. ctx.Redirect(setting.AppSubURL + "/user/settings")
  61. case models.IsErrNameReserved(err):
  62. ctx.Flash.Error(ctx.Tr("user.form.name_reserved", newName))
  63. ctx.Redirect(setting.AppSubURL + "/user/settings")
  64. case models.IsErrNamePatternNotAllowed(err):
  65. ctx.Flash.Error(ctx.Tr("user.form.name_pattern_not_allowed", newName))
  66. ctx.Redirect(setting.AppSubURL + "/user/settings")
  67. default:
  68. ctx.ServerError("ChangeUserName", err)
  69. }
  70. return
  71. }
  72. log.Trace("User name changed: %s -> %s", ctx.User.Name, newName)
  73. }
  74. // In case it's just a case change
  75. ctx.User.Name = newName
  76. ctx.User.LowerName = strings.ToLower(newName)
  77. }
  78. // SettingsPost response for change user's profile
  79. func SettingsPost(ctx *context.Context, form auth.UpdateProfileForm) {
  80. ctx.Data["Title"] = ctx.Tr("settings")
  81. ctx.Data["PageIsSettingsProfile"] = true
  82. if ctx.HasError() {
  83. ctx.HTML(200, tplSettingsProfile)
  84. return
  85. }
  86. handleUsernameChange(ctx, form.Name)
  87. if ctx.Written() {
  88. return
  89. }
  90. ctx.User.FullName = form.FullName
  91. ctx.User.Email = form.Email
  92. ctx.User.KeepEmailPrivate = form.KeepEmailPrivate
  93. ctx.User.Website = form.Website
  94. ctx.User.Location = form.Location
  95. ctx.User.Language = form.Language
  96. if err := models.UpdateUserSetting(ctx.User); err != nil {
  97. if _, ok := err.(models.ErrEmailAlreadyUsed); ok {
  98. ctx.Flash.Error(ctx.Tr("form.email_been_used"))
  99. ctx.Redirect(setting.AppSubURL + "/user/settings")
  100. return
  101. }
  102. ctx.ServerError("UpdateUser", err)
  103. return
  104. }
  105. // Update the language to the one we just set
  106. ctx.SetCookie("lang", ctx.User.Language, nil, setting.AppSubURL)
  107. log.Trace("User settings updated: %s", ctx.User.Name)
  108. ctx.Flash.Success(i18n.Tr(ctx.User.Language, "settings.update_profile_success"))
  109. ctx.Redirect(setting.AppSubURL + "/user/settings")
  110. }
  111. // UpdateAvatarSetting update user's avatar
  112. // FIXME: limit size.
  113. func UpdateAvatarSetting(ctx *context.Context, form auth.AvatarForm, ctxUser *models.User) error {
  114. ctxUser.UseCustomAvatar = form.Source == auth.AvatarLocal
  115. if len(form.Gravatar) > 0 {
  116. ctxUser.Avatar = base.EncodeMD5(form.Gravatar)
  117. ctxUser.AvatarEmail = form.Gravatar
  118. }
  119. if form.Avatar != nil {
  120. fr, err := form.Avatar.Open()
  121. if err != nil {
  122. return fmt.Errorf("Avatar.Open: %v", err)
  123. }
  124. defer fr.Close()
  125. data, err := ioutil.ReadAll(fr)
  126. if err != nil {
  127. return fmt.Errorf("ioutil.ReadAll: %v", err)
  128. }
  129. if !base.IsImageFile(data) {
  130. return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image"))
  131. }
  132. if err = ctxUser.UploadAvatar(data); err != nil {
  133. return fmt.Errorf("UploadAvatar: %v", err)
  134. }
  135. } else {
  136. // No avatar is uploaded but setting has been changed to enable,
  137. // generate a random one when needed.
  138. if ctxUser.UseCustomAvatar && !com.IsFile(ctxUser.CustomAvatarPath()) {
  139. if err := ctxUser.GenerateRandomAvatar(); err != nil {
  140. log.Error(4, "GenerateRandomAvatar[%d]: %v", ctxUser.ID, err)
  141. }
  142. }
  143. }
  144. if err := models.UpdateUserCols(ctxUser, "avatar", "avatar_email", "use_custom_avatar"); err != nil {
  145. return fmt.Errorf("UpdateUser: %v", err)
  146. }
  147. return nil
  148. }
  149. // SettingsAvatar render user avatar page
  150. func SettingsAvatar(ctx *context.Context) {
  151. ctx.Data["Title"] = ctx.Tr("settings")
  152. ctx.Data["PageIsSettingsAvatar"] = true
  153. ctx.HTML(200, tplSettingsAvatar)
  154. }
  155. // SettingsAvatarPost response for change user's avatar request
  156. func SettingsAvatarPost(ctx *context.Context, form auth.AvatarForm) {
  157. if err := UpdateAvatarSetting(ctx, form, ctx.User); err != nil {
  158. ctx.Flash.Error(err.Error())
  159. } else {
  160. ctx.Flash.Success(ctx.Tr("settings.update_avatar_success"))
  161. }
  162. ctx.Redirect(setting.AppSubURL + "/user/settings/avatar")
  163. }
  164. // SettingsDeleteAvatar render delete avatar page
  165. func SettingsDeleteAvatar(ctx *context.Context) {
  166. if err := ctx.User.DeleteAvatar(); err != nil {
  167. ctx.Flash.Error(err.Error())
  168. }
  169. ctx.Redirect(setting.AppSubURL + "/user/settings/avatar")
  170. }
  171. // SettingsSecurity render change user's password page and 2FA
  172. func SettingsSecurity(ctx *context.Context) {
  173. ctx.Data["Title"] = ctx.Tr("settings")
  174. ctx.Data["PageIsSettingsSecurity"] = true
  175. ctx.Data["Email"] = ctx.User.Email
  176. enrolled := true
  177. _, err := models.GetTwoFactorByUID(ctx.User.ID)
  178. if err != nil {
  179. if models.IsErrTwoFactorNotEnrolled(err) {
  180. enrolled = false
  181. } else {
  182. ctx.ServerError("SettingsTwoFactor", err)
  183. return
  184. }
  185. }
  186. ctx.Data["TwofaEnrolled"] = enrolled
  187. ctx.HTML(200, tplSettingsSecurity)
  188. }
  189. // SettingsSecurityPost response for change user's password
  190. func SettingsSecurityPost(ctx *context.Context, form auth.ChangePasswordForm) {
  191. ctx.Data["Title"] = ctx.Tr("settings")
  192. ctx.Data["PageIsSettingsSecurity"] = true
  193. ctx.Data["PageIsSettingsDelete"] = true
  194. if ctx.HasError() {
  195. ctx.HTML(200, tplSettingsSecurity)
  196. return
  197. }
  198. if len(form.Password) < setting.MinPasswordLength {
  199. ctx.Flash.Error(ctx.Tr("auth.password_too_short", setting.MinPasswordLength))
  200. } else if ctx.User.IsPasswordSet() && !ctx.User.ValidatePassword(form.OldPassword) {
  201. ctx.Flash.Error(ctx.Tr("settings.password_incorrect"))
  202. } else if form.Password != form.Retype {
  203. ctx.Flash.Error(ctx.Tr("form.password_not_match"))
  204. } else {
  205. var err error
  206. if ctx.User.Salt, err = models.GetUserSalt(); err != nil {
  207. ctx.ServerError("UpdateUser", err)
  208. return
  209. }
  210. ctx.User.HashPassword(form.Password)
  211. if err := models.UpdateUserCols(ctx.User, "salt", "passwd"); err != nil {
  212. ctx.ServerError("UpdateUser", err)
  213. return
  214. }
  215. log.Trace("User password updated: %s", ctx.User.Name)
  216. ctx.Flash.Success(ctx.Tr("settings.change_password_success"))
  217. }
  218. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  219. }
  220. // SettingsEmails render user's emails page
  221. func SettingsEmails(ctx *context.Context) {
  222. ctx.Data["Title"] = ctx.Tr("settings")
  223. ctx.Data["PageIsSettingsEmails"] = true
  224. emails, err := models.GetEmailAddresses(ctx.User.ID)
  225. if err != nil {
  226. ctx.ServerError("GetEmailAddresses", err)
  227. return
  228. }
  229. ctx.Data["Emails"] = emails
  230. ctx.HTML(200, tplSettingsEmails)
  231. }
  232. // SettingsEmailPost response for change user's email
  233. func SettingsEmailPost(ctx *context.Context, form auth.AddEmailForm) {
  234. ctx.Data["Title"] = ctx.Tr("settings")
  235. ctx.Data["PageIsSettingsEmails"] = true
  236. // Make emailaddress primary.
  237. if ctx.Query("_method") == "PRIMARY" {
  238. if err := models.MakeEmailPrimary(&models.EmailAddress{ID: ctx.QueryInt64("id")}); err != nil {
  239. ctx.ServerError("MakeEmailPrimary", err)
  240. return
  241. }
  242. log.Trace("Email made primary: %s", ctx.User.Name)
  243. ctx.Redirect(setting.AppSubURL + "/user/settings/email")
  244. return
  245. }
  246. // Add Email address.
  247. emails, err := models.GetEmailAddresses(ctx.User.ID)
  248. if err != nil {
  249. ctx.ServerError("GetEmailAddresses", err)
  250. return
  251. }
  252. ctx.Data["Emails"] = emails
  253. if ctx.HasError() {
  254. ctx.HTML(200, tplSettingsEmails)
  255. return
  256. }
  257. email := &models.EmailAddress{
  258. UID: ctx.User.ID,
  259. Email: form.Email,
  260. IsActivated: !setting.Service.RegisterEmailConfirm,
  261. }
  262. if err := models.AddEmailAddress(email); err != nil {
  263. if models.IsErrEmailAlreadyUsed(err) {
  264. ctx.RenderWithErr(ctx.Tr("form.email_been_used"), tplSettingsEmails, &form)
  265. return
  266. }
  267. ctx.ServerError("AddEmailAddress", err)
  268. return
  269. }
  270. // Send confirmation email
  271. if setting.Service.RegisterEmailConfirm {
  272. models.SendActivateEmailMail(ctx.Context, ctx.User, email)
  273. if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
  274. log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
  275. }
  276. ctx.Flash.Info(ctx.Tr("settings.add_email_confirmation_sent", email.Email, base.MinutesToFriendly(setting.Service.ActiveCodeLives, ctx.Locale.Language())))
  277. } else {
  278. ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
  279. }
  280. log.Trace("Email address added: %s", email.Email)
  281. ctx.Redirect(setting.AppSubURL + "/user/settings/email")
  282. }
  283. // DeleteEmail response for delete user's email
  284. func DeleteEmail(ctx *context.Context) {
  285. if err := models.DeleteEmailAddress(&models.EmailAddress{ID: ctx.QueryInt64("id"), UID: ctx.User.ID}); err != nil {
  286. ctx.ServerError("DeleteEmail", err)
  287. return
  288. }
  289. log.Trace("Email address deleted: %s", ctx.User.Name)
  290. ctx.Flash.Success(ctx.Tr("settings.email_deletion_success"))
  291. ctx.JSON(200, map[string]interface{}{
  292. "redirect": setting.AppSubURL + "/user/settings/email",
  293. })
  294. }
  295. // SettingsKeys render user's SSH/GPG public keys page
  296. func SettingsKeys(ctx *context.Context) {
  297. ctx.Data["Title"] = ctx.Tr("settings")
  298. ctx.Data["PageIsSettingsKeys"] = true
  299. ctx.Data["DisableSSH"] = setting.SSH.Disabled
  300. keys, err := models.ListPublicKeys(ctx.User.ID)
  301. if err != nil {
  302. ctx.ServerError("ListPublicKeys", err)
  303. return
  304. }
  305. ctx.Data["Keys"] = keys
  306. gpgkeys, err := models.ListGPGKeys(ctx.User.ID)
  307. if err != nil {
  308. ctx.ServerError("ListGPGKeys", err)
  309. return
  310. }
  311. ctx.Data["GPGKeys"] = gpgkeys
  312. ctx.HTML(200, tplSettingsKeys)
  313. }
  314. // SettingsKeysPost response for change user's SSH/GPG keys
  315. func SettingsKeysPost(ctx *context.Context, form auth.AddKeyForm) {
  316. ctx.Data["Title"] = ctx.Tr("settings")
  317. ctx.Data["PageIsSettingsKeys"] = true
  318. keys, err := models.ListPublicKeys(ctx.User.ID)
  319. if err != nil {
  320. ctx.ServerError("ListPublicKeys", err)
  321. return
  322. }
  323. ctx.Data["Keys"] = keys
  324. gpgkeys, err := models.ListGPGKeys(ctx.User.ID)
  325. if err != nil {
  326. ctx.ServerError("ListGPGKeys", err)
  327. return
  328. }
  329. ctx.Data["GPGKeys"] = gpgkeys
  330. if ctx.HasError() {
  331. ctx.HTML(200, tplSettingsKeys)
  332. return
  333. }
  334. switch form.Type {
  335. case "gpg":
  336. key, err := models.AddGPGKey(ctx.User.ID, form.Content)
  337. if err != nil {
  338. ctx.Data["HasGPGError"] = true
  339. switch {
  340. case models.IsErrGPGKeyParsing(err):
  341. ctx.Flash.Error(ctx.Tr("form.invalid_gpg_key", err.Error()))
  342. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  343. case models.IsErrGPGKeyIDAlreadyUsed(err):
  344. ctx.Data["Err_Content"] = true
  345. ctx.RenderWithErr(ctx.Tr("settings.gpg_key_id_used"), tplSettingsKeys, &form)
  346. case models.IsErrGPGNoEmailFound(err):
  347. ctx.Data["Err_Content"] = true
  348. ctx.RenderWithErr(ctx.Tr("settings.gpg_no_key_email_found"), tplSettingsKeys, &form)
  349. default:
  350. ctx.ServerError("AddPublicKey", err)
  351. }
  352. return
  353. }
  354. ctx.Flash.Success(ctx.Tr("settings.add_gpg_key_success", key.KeyID))
  355. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  356. case "ssh":
  357. content, err := models.CheckPublicKeyString(form.Content)
  358. if err != nil {
  359. if models.IsErrSSHDisabled(err) {
  360. ctx.Flash.Info(ctx.Tr("settings.ssh_disabled"))
  361. } else if models.IsErrKeyUnableVerify(err) {
  362. ctx.Flash.Info(ctx.Tr("form.unable_verify_ssh_key"))
  363. } else {
  364. ctx.Flash.Error(ctx.Tr("form.invalid_ssh_key", err.Error()))
  365. }
  366. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  367. return
  368. }
  369. if _, err = models.AddPublicKey(ctx.User.ID, form.Title, content); err != nil {
  370. ctx.Data["HasSSHError"] = true
  371. switch {
  372. case models.IsErrKeyAlreadyExist(err):
  373. ctx.Data["Err_Content"] = true
  374. ctx.RenderWithErr(ctx.Tr("settings.ssh_key_been_used"), tplSettingsKeys, &form)
  375. case models.IsErrKeyNameAlreadyUsed(err):
  376. ctx.Data["Err_Title"] = true
  377. ctx.RenderWithErr(ctx.Tr("settings.ssh_key_name_used"), tplSettingsKeys, &form)
  378. default:
  379. ctx.ServerError("AddPublicKey", err)
  380. }
  381. return
  382. }
  383. ctx.Flash.Success(ctx.Tr("settings.add_key_success", form.Title))
  384. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  385. default:
  386. ctx.Flash.Warning("Function not implemented")
  387. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  388. }
  389. }
  390. // DeleteKey response for delete user's SSH/GPG key
  391. func DeleteKey(ctx *context.Context) {
  392. switch ctx.Query("type") {
  393. case "gpg":
  394. if err := models.DeleteGPGKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  395. ctx.Flash.Error("DeleteGPGKey: " + err.Error())
  396. } else {
  397. ctx.Flash.Success(ctx.Tr("settings.gpg_key_deletion_success"))
  398. }
  399. case "ssh":
  400. if err := models.DeletePublicKey(ctx.User, ctx.QueryInt64("id")); err != nil {
  401. ctx.Flash.Error("DeletePublicKey: " + err.Error())
  402. } else {
  403. ctx.Flash.Success(ctx.Tr("settings.ssh_key_deletion_success"))
  404. }
  405. default:
  406. ctx.Flash.Warning("Function not implemented")
  407. ctx.Redirect(setting.AppSubURL + "/user/settings/keys")
  408. }
  409. ctx.JSON(200, map[string]interface{}{
  410. "redirect": setting.AppSubURL + "/user/settings/keys",
  411. })
  412. }
  413. // SettingsApplications render user's access tokens page
  414. func SettingsApplications(ctx *context.Context) {
  415. ctx.Data["Title"] = ctx.Tr("settings")
  416. ctx.Data["PageIsSettingsApplications"] = true
  417. tokens, err := models.ListAccessTokens(ctx.User.ID)
  418. if err != nil {
  419. ctx.ServerError("ListAccessTokens", err)
  420. return
  421. }
  422. ctx.Data["Tokens"] = tokens
  423. ctx.HTML(200, tplSettingsApplications)
  424. }
  425. // SettingsApplicationsPost response for add user's access token
  426. func SettingsApplicationsPost(ctx *context.Context, form auth.NewAccessTokenForm) {
  427. ctx.Data["Title"] = ctx.Tr("settings")
  428. ctx.Data["PageIsSettingsApplications"] = true
  429. if ctx.HasError() {
  430. tokens, err := models.ListAccessTokens(ctx.User.ID)
  431. if err != nil {
  432. ctx.ServerError("ListAccessTokens", err)
  433. return
  434. }
  435. ctx.Data["Tokens"] = tokens
  436. ctx.HTML(200, tplSettingsApplications)
  437. return
  438. }
  439. t := &models.AccessToken{
  440. UID: ctx.User.ID,
  441. Name: form.Name,
  442. }
  443. if err := models.NewAccessToken(t); err != nil {
  444. ctx.ServerError("NewAccessToken", err)
  445. return
  446. }
  447. ctx.Flash.Success(ctx.Tr("settings.generate_token_success"))
  448. ctx.Flash.Info(t.Sha1)
  449. ctx.Redirect(setting.AppSubURL + "/user/settings/applications")
  450. }
  451. // SettingsDeleteApplication response for delete user access token
  452. func SettingsDeleteApplication(ctx *context.Context) {
  453. if err := models.DeleteAccessTokenByID(ctx.QueryInt64("id"), ctx.User.ID); err != nil {
  454. ctx.Flash.Error("DeleteAccessTokenByID: " + err.Error())
  455. } else {
  456. ctx.Flash.Success(ctx.Tr("settings.delete_token_success"))
  457. }
  458. ctx.JSON(200, map[string]interface{}{
  459. "redirect": setting.AppSubURL + "/user/settings/applications",
  460. })
  461. }
  462. // SettingsTwoFactorRegenerateScratch regenerates the user's 2FA scratch code.
  463. func SettingsTwoFactorRegenerateScratch(ctx *context.Context) {
  464. ctx.Data["Title"] = ctx.Tr("settings")
  465. ctx.Data["PageIsSettingsSecurity"] = true
  466. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  467. if err != nil {
  468. ctx.ServerError("SettingsTwoFactor", err)
  469. return
  470. }
  471. if err = t.GenerateScratchToken(); err != nil {
  472. ctx.ServerError("SettingsTwoFactor", err)
  473. return
  474. }
  475. if err = models.UpdateTwoFactor(t); err != nil {
  476. ctx.ServerError("SettingsTwoFactor", err)
  477. return
  478. }
  479. ctx.Flash.Success(ctx.Tr("settings.twofa_scratch_token_regenerated", t.ScratchToken))
  480. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  481. }
  482. // SettingsTwoFactorDisable deletes the user's 2FA settings.
  483. func SettingsTwoFactorDisable(ctx *context.Context) {
  484. ctx.Data["Title"] = ctx.Tr("settings")
  485. ctx.Data["PageIsSettingsSecurity"] = true
  486. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  487. if err != nil {
  488. ctx.ServerError("SettingsTwoFactor", err)
  489. return
  490. }
  491. if err = models.DeleteTwoFactorByID(t.ID, ctx.User.ID); err != nil {
  492. ctx.ServerError("SettingsTwoFactor", err)
  493. return
  494. }
  495. ctx.Flash.Success(ctx.Tr("settings.twofa_disabled"))
  496. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  497. }
  498. func twofaGenerateSecretAndQr(ctx *context.Context) bool {
  499. var otpKey *otp.Key
  500. var err error
  501. uri := ctx.Session.Get("twofaUri")
  502. if uri != nil {
  503. otpKey, err = otp.NewKeyFromURL(uri.(string))
  504. }
  505. if otpKey == nil {
  506. err = nil // clear the error, in case the URL was invalid
  507. otpKey, err = totp.Generate(totp.GenerateOpts{
  508. Issuer: setting.AppName + " (" + strings.TrimRight(setting.AppURL, "/") + ")",
  509. AccountName: ctx.User.Name,
  510. })
  511. if err != nil {
  512. ctx.ServerError("SettingsTwoFactor", err)
  513. return false
  514. }
  515. }
  516. ctx.Data["TwofaSecret"] = otpKey.Secret()
  517. img, err := otpKey.Image(320, 240)
  518. if err != nil {
  519. ctx.ServerError("SettingsTwoFactor", err)
  520. return false
  521. }
  522. var imgBytes bytes.Buffer
  523. if err = png.Encode(&imgBytes, img); err != nil {
  524. ctx.ServerError("SettingsTwoFactor", err)
  525. return false
  526. }
  527. ctx.Data["QrUri"] = template.URL("data:image/png;base64," + base64.StdEncoding.EncodeToString(imgBytes.Bytes()))
  528. ctx.Session.Set("twofaSecret", otpKey.Secret())
  529. ctx.Session.Set("twofaUri", otpKey.String())
  530. return true
  531. }
  532. // SettingsTwoFactorEnroll shows the page where the user can enroll into 2FA.
  533. func SettingsTwoFactorEnroll(ctx *context.Context) {
  534. ctx.Data["Title"] = ctx.Tr("settings")
  535. ctx.Data["PageIsSettingsSecurity"] = true
  536. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  537. if t != nil {
  538. // already enrolled
  539. ctx.ServerError("SettingsTwoFactor", err)
  540. return
  541. }
  542. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  543. ctx.ServerError("SettingsTwoFactor", err)
  544. return
  545. }
  546. if !twofaGenerateSecretAndQr(ctx) {
  547. return
  548. }
  549. ctx.HTML(200, tplSettingsTwofaEnroll)
  550. }
  551. // SettingsTwoFactorEnrollPost handles enrolling the user into 2FA.
  552. func SettingsTwoFactorEnrollPost(ctx *context.Context, form auth.TwoFactorAuthForm) {
  553. ctx.Data["Title"] = ctx.Tr("settings")
  554. ctx.Data["PageIsSettingsSecurity"] = true
  555. t, err := models.GetTwoFactorByUID(ctx.User.ID)
  556. if t != nil {
  557. // already enrolled
  558. ctx.ServerError("SettingsTwoFactor", err)
  559. return
  560. }
  561. if err != nil && !models.IsErrTwoFactorNotEnrolled(err) {
  562. ctx.ServerError("SettingsTwoFactor", err)
  563. return
  564. }
  565. if ctx.HasError() {
  566. if !twofaGenerateSecretAndQr(ctx) {
  567. return
  568. }
  569. ctx.HTML(200, tplSettingsTwofaEnroll)
  570. return
  571. }
  572. secret := ctx.Session.Get("twofaSecret").(string)
  573. if !totp.Validate(form.Passcode, secret) {
  574. if !twofaGenerateSecretAndQr(ctx) {
  575. return
  576. }
  577. ctx.Flash.Error(ctx.Tr("settings.passcode_invalid"))
  578. ctx.HTML(200, tplSettingsTwofaEnroll)
  579. return
  580. }
  581. t = &models.TwoFactor{
  582. UID: ctx.User.ID,
  583. }
  584. err = t.SetSecret(secret)
  585. if err != nil {
  586. ctx.ServerError("SettingsTwoFactor", err)
  587. return
  588. }
  589. err = t.GenerateScratchToken()
  590. if err != nil {
  591. ctx.ServerError("SettingsTwoFactor", err)
  592. return
  593. }
  594. if err = models.NewTwoFactor(t); err != nil {
  595. ctx.ServerError("SettingsTwoFactor", err)
  596. return
  597. }
  598. ctx.Session.Delete("twofaSecret")
  599. ctx.Session.Delete("twofaUri")
  600. ctx.Flash.Success(ctx.Tr("settings.twofa_enrolled", t.ScratchToken))
  601. ctx.Redirect(setting.AppSubURL + "/user/settings/security")
  602. }
  603. // SettingsAccountLinks render the account links settings page
  604. func SettingsAccountLinks(ctx *context.Context) {
  605. ctx.Data["Title"] = ctx.Tr("settings")
  606. ctx.Data["PageIsSettingsAccountLink"] = true
  607. accountLinks, err := models.ListAccountLinks(ctx.User)
  608. if err != nil {
  609. ctx.ServerError("ListAccountLinks", err)
  610. return
  611. }
  612. // map the provider display name with the LoginSource
  613. sources := make(map[*models.LoginSource]string)
  614. for _, externalAccount := range accountLinks {
  615. if loginSource, err := models.GetLoginSourceByID(externalAccount.LoginSourceID); err == nil {
  616. var providerDisplayName string
  617. if loginSource.IsOAuth2() {
  618. providerTechnicalName := loginSource.OAuth2().Provider
  619. providerDisplayName = models.OAuth2Providers[providerTechnicalName].DisplayName
  620. } else {
  621. providerDisplayName = loginSource.Name
  622. }
  623. sources[loginSource] = providerDisplayName
  624. }
  625. }
  626. ctx.Data["AccountLinks"] = sources
  627. ctx.HTML(200, tplSettingsAccountLink)
  628. }
  629. // SettingsDeleteAccountLink delete a single account link
  630. func SettingsDeleteAccountLink(ctx *context.Context) {
  631. if _, err := models.RemoveAccountLink(ctx.User, ctx.QueryInt64("loginSourceID")); err != nil {
  632. ctx.Flash.Error("RemoveAccountLink: " + err.Error())
  633. } else {
  634. ctx.Flash.Success(ctx.Tr("settings.remove_account_link_success"))
  635. }
  636. ctx.JSON(200, map[string]interface{}{
  637. "redirect": setting.AppSubURL + "/user/settings/account_link",
  638. })
  639. }
  640. // SettingsDelete render user suicide page and response for delete user himself
  641. func SettingsDelete(ctx *context.Context) {
  642. ctx.Data["Title"] = ctx.Tr("settings")
  643. ctx.Data["PageIsSettingsDelete"] = true
  644. ctx.Data["Email"] = ctx.User.Email
  645. if ctx.Req.Method == "POST" {
  646. if _, err := models.UserSignIn(ctx.User.Name, ctx.Query("password")); err != nil {
  647. if models.IsErrUserNotExist(err) {
  648. ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_password"), tplSettingsDelete, nil)
  649. } else {
  650. ctx.ServerError("UserSignIn", err)
  651. }
  652. return
  653. }
  654. if err := models.DeleteUser(ctx.User); err != nil {
  655. switch {
  656. case models.IsErrUserOwnRepos(err):
  657. ctx.Flash.Error(ctx.Tr("form.still_own_repo"))
  658. ctx.Redirect(setting.AppSubURL + "/user/settings/delete")
  659. case models.IsErrUserHasOrgs(err):
  660. ctx.Flash.Error(ctx.Tr("form.still_has_org"))
  661. ctx.Redirect(setting.AppSubURL + "/user/settings/delete")
  662. default:
  663. ctx.ServerError("DeleteUser", err)
  664. }
  665. } else {
  666. log.Trace("Account deleted: %s", ctx.User.Name)
  667. ctx.Redirect(setting.AppSubURL + "/")
  668. }
  669. return
  670. }
  671. ctx.HTML(200, tplSettingsDelete)
  672. }
  673. // SettingsOrganization render all the organization of the user
  674. func SettingsOrganization(ctx *context.Context) {
  675. ctx.Data["Title"] = ctx.Tr("settings")
  676. ctx.Data["PageIsSettingsOrganization"] = true
  677. orgs, err := models.GetOrgsByUserID(ctx.User.ID, ctx.IsSigned)
  678. if err != nil {
  679. ctx.ServerError("GetOrgsByUserID", err)
  680. return
  681. }
  682. ctx.Data["Orgs"] = orgs
  683. ctx.HTML(200, tplSettingsOrganization)
  684. }
  685. // SettingsRepos display a list of all repositories of the user
  686. func SettingsRepos(ctx *context.Context) {
  687. ctx.Data["Title"] = ctx.Tr("settings")
  688. ctx.Data["PageIsSettingsRepos"] = true
  689. ctxUser := ctx.User
  690. var err error
  691. if err = ctxUser.GetRepositories(1, setting.UI.User.RepoPagingNum); err != nil {
  692. ctx.ServerError("GetRepositories", err)
  693. return
  694. }
  695. repos := ctxUser.Repos
  696. for i := range repos {
  697. if repos[i].IsFork {
  698. err := repos[i].GetBaseRepo()
  699. if err != nil {
  700. ctx.ServerError("GetBaseRepo", err)
  701. return
  702. }
  703. err = repos[i].BaseRepo.GetOwner()
  704. if err != nil {
  705. ctx.ServerError("GetOwner", err)
  706. return
  707. }
  708. }
  709. }
  710. ctx.Data["Owner"] = ctxUser
  711. ctx.Data["Repos"] = repos
  712. ctx.HTML(200, tplSettingsRepositories)
  713. }