Quellcode durchsuchen

Merged conflicts.

Sergio Benitez vor 9 Jahren
Ursprung
Commit
964d0262ff
100 geänderte Dateien mit 758 neuen und 674 gelöschten Zeilen
  1. 1 2
      .bra.toml
  2. 1 1
      Dockerfile
  3. 2 1
      cmd/web.go
  4. 284 284
      conf/locale/locale_bg-BG.ini
  5. 5 5
      conf/locale/locale_de-DE.ini
  6. 17 18
      conf/locale/locale_en-US.ini
  7. 127 127
      conf/locale/locale_es-ES.ini
  8. 2 2
      conf/locale/locale_pt-BR.ini
  9. 8 8
      conf/locale/locale_zh-HK.ini
  10. 0 4
      docker/start.sh
  11. 1 1
      gogs.go
  12. 1 1
      models/issue.go
  13. 9 0
      models/models.go
  14. 16 0
      models/models_tidb.go
  15. 45 48
      models/org.go
  16. 21 2
      models/repo.go
  17. 102 76
      models/user.go
  18. 6 9
      modules/auth/org.go
  19. 8 8
      modules/auth/user_form.go
  20. 1 2
      modules/base/template.go
  21. 0 0
      modules/bindata/bindata.go
  22. 4 2
      modules/mailer/mailer.go
  23. 5 0
      modules/setting/setting.go
  24. 92 73
      public/config.codekit
  25. 0 0
      public/css/gogs.min.css
  26. BIN
      public/img/emoji/+1.png
  27. BIN
      public/img/emoji/-1.png
  28. BIN
      public/img/emoji/100.png
  29. BIN
      public/img/emoji/1234.png
  30. BIN
      public/img/emoji/8ball.png
  31. BIN
      public/img/emoji/a.png
  32. BIN
      public/img/emoji/ab.png
  33. BIN
      public/img/emoji/abc.png
  34. BIN
      public/img/emoji/abcd.png
  35. BIN
      public/img/emoji/accept.png
  36. BIN
      public/img/emoji/aerial_tramway.png
  37. BIN
      public/img/emoji/airplane.png
  38. BIN
      public/img/emoji/alarm_clock.png
  39. BIN
      public/img/emoji/alien.png
  40. BIN
      public/img/emoji/ambulance.png
  41. BIN
      public/img/emoji/anchor.png
  42. BIN
      public/img/emoji/angel.png
  43. BIN
      public/img/emoji/anger.png
  44. BIN
      public/img/emoji/angry.png
  45. BIN
      public/img/emoji/anguished.png
  46. BIN
      public/img/emoji/ant.png
  47. BIN
      public/img/emoji/apple.png
  48. BIN
      public/img/emoji/aquarius.png
  49. BIN
      public/img/emoji/aries.png
  50. BIN
      public/img/emoji/arrow_backward.png
  51. BIN
      public/img/emoji/arrow_double_down.png
  52. BIN
      public/img/emoji/arrow_double_up.png
  53. BIN
      public/img/emoji/arrow_down.png
  54. BIN
      public/img/emoji/arrow_down_small.png
  55. BIN
      public/img/emoji/arrow_forward.png
  56. BIN
      public/img/emoji/arrow_heading_down.png
  57. BIN
      public/img/emoji/arrow_heading_up.png
  58. BIN
      public/img/emoji/arrow_left.png
  59. BIN
      public/img/emoji/arrow_lower_left.png
  60. BIN
      public/img/emoji/arrow_lower_right.png
  61. BIN
      public/img/emoji/arrow_right.png
  62. BIN
      public/img/emoji/arrow_right_hook.png
  63. BIN
      public/img/emoji/arrow_up.png
  64. BIN
      public/img/emoji/arrow_up_down.png
  65. BIN
      public/img/emoji/arrow_up_small.png
  66. BIN
      public/img/emoji/arrow_upper_left.png
  67. BIN
      public/img/emoji/arrow_upper_right.png
  68. BIN
      public/img/emoji/arrows_clockwise.png
  69. BIN
      public/img/emoji/arrows_counterclockwise.png
  70. BIN
      public/img/emoji/art.png
  71. BIN
      public/img/emoji/articulated_lorry.png
  72. BIN
      public/img/emoji/astonished.png
  73. BIN
      public/img/emoji/atm.png
  74. BIN
      public/img/emoji/b.png
  75. BIN
      public/img/emoji/baby.png
  76. BIN
      public/img/emoji/baby_bottle.png
  77. BIN
      public/img/emoji/baby_chick.png
  78. BIN
      public/img/emoji/baby_symbol.png
  79. BIN
      public/img/emoji/back.png
  80. BIN
      public/img/emoji/baggage_claim.png
  81. BIN
      public/img/emoji/balloon.png
  82. BIN
      public/img/emoji/ballot_box_with_check.png
  83. BIN
      public/img/emoji/bamboo.png
  84. BIN
      public/img/emoji/banana.png
  85. BIN
      public/img/emoji/bangbang.png
  86. BIN
      public/img/emoji/bank.png
  87. BIN
      public/img/emoji/bar_chart.png
  88. BIN
      public/img/emoji/barber.png
  89. BIN
      public/img/emoji/baseball.png
  90. BIN
      public/img/emoji/basketball.png
  91. BIN
      public/img/emoji/bath.png
  92. BIN
      public/img/emoji/bathtub.png
  93. BIN
      public/img/emoji/battery.png
  94. BIN
      public/img/emoji/bear.png
  95. BIN
      public/img/emoji/bee.png
  96. BIN
      public/img/emoji/beer.png
  97. BIN
      public/img/emoji/beers.png
  98. BIN
      public/img/emoji/beetle.png
  99. BIN
      public/img/emoji/beginner.png
  100. BIN
      public/img/emoji/bell.png

+ 1 - 2
.bra.toml

@@ -13,8 +13,7 @@ watch_dirs = [
 watch_exts = [".go"]
 build_delay = 1500
 cmds = [
-	#["go-bindata", "-o=modules/bindata/bindata.go", "-ignore=\\.DS_Store|README", "-pkg=bindata", "conf/..."],
-	["go", "install", "-tags", "sqlite"],# redis memcache cert pam
+	["go", "install", "-tags", "sqlite"],# redis memcache cert pam tidb
 	["go", "build", "-tags", "sqlite"],
 	["./gogs", "web"]
 ]

+ 1 - 1
Dockerfile

@@ -5,7 +5,7 @@ RUN echo "deb http://ftp.debian.org/debian/ wheezy-backports main" >> /etc/apt/s
 	apt-get update -qqy && \
 	apt-get install --no-install-recommends -qqy \
 	curl build-essential ca-certificates git \ 
-	openssh-server rsync libpam-dev && \
+	openssh-server libpam-dev && \
 	apt-get autoclean && \
     apt-get autoremove && \
     rm -rf /var/lib/apt/lists/*

+ 2 - 1
cmd/web.go

@@ -406,6 +406,7 @@ func runWeb(ctx *cli.Context) {
 			m.Group("/settings", func() {
 				m.Combo("").Get(org.Settings).
 					Post(bindIgnErr(auth.UpdateOrgSettingForm{}), org.SettingsPost)
+				m.Post("/avatar", binding.MultipartForm(auth.UploadAvatarForm{}), org.SettingsAvatar)
 
 				m.Group("/hooks", func() {
 					m.Get("", org.Webhooks)
@@ -544,7 +545,7 @@ func runWeb(ctx *cli.Context) {
 	m.Group("/:username", func() {
 		m.Group("/:reponame", func() {
 			m.Get("", repo.Home)
-			m.Get(".git", repo.Home)
+			m.Get("\\.git$", repo.Home)
 		}, ignSignIn, middleware.RepoAssignment(true, true), middleware.RepoRef())
 
 		m.Group("/:reponame", func() {

Datei-Diff unterdrückt, da er zu groß ist
+ 284 - 284
conf/locale/locale_bg-BG.ini


+ 5 - 5
conf/locale/locale_de-DE.ini

@@ -463,26 +463,26 @@ issues.label_deletion_success=Label wurde erfolgreich gelöscht!
 
 pulls.compare_changes=Änderungen vergleichen
 pulls.compare_changes_desc=Vergleiche zwei Branches und erstelle einen Pull Request für die Änderungen.
-pulls.compare_base=base
+pulls.compare_base=Base
 pulls.compare_compare=vergleichen
 pulls.filter_branch=Filter Branch
 pulls.no_results=Keine Ergebnisse verfügbar.
 pulls.nothing_to_compare=Es ist nichts zu vergleichen, da Base- und Head-Branch gleich sind.
 pulls.has_pull_request=`Es existiert bereits eine Pull-Anforderung zwischen diesen beiden Zielen: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
 pulls.create=Pull Request erstellen
-pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
+pulls.title_desc=möchte %[1]d Commits von <code>%[2]s</code> nach <code>%[3]s</code> zusammenführen
 pulls.merged_title_desc=%[1]d Commits von <code>%[2]s</code> nach <code>%[3]s</code> %[4]s zusammengeführt
 pulls.tab_conversation=Unterhaltung
 pulls.tab_commits=Commits
 pulls.tab_files=Dateien geändert
 pulls.reopen_to_merge=Bitte diese Pull-Anforderung wiedereröffnen, um die Merge-Operation auszuführen.
 pulls.merged=Zusammengeführt
-pulls.has_merged=This pull request has been merged successfully!
+pulls.has_merged=Dieser Pull-Request wurde erfolgreich zusammengeführt!
 pulls.data_broken=Die Daten dieser Pull-Anforderung sind defekt aufgrund des Löschens von Fork-Informationen.
 pulls.can_auto_merge_desc=Du kannst eine Auto-Merge Operation auf diese Pull-Anforderung durchführen.
 pulls.cannot_auto_merge_desc=Es kann keine Auto-Merge Operation durchgeführt werden, da es Konflikte zwischen den Commits gibt.
 pulls.cannot_auto_merge_helper=Bitte benutze ein Kommandozeilentool, um den Konflikt zu lösen.
-pulls.merge_pull_request=Merge Pull Request
+pulls.merge_pull_request=Pull-Request zusammenführen
 
 milestones.new=Neuer Meilenstein
 milestones.open_tab=%d offen
@@ -924,7 +924,7 @@ commit_repo=hat nach <a href="%s/src/%s">%[2]s</a> in <a href="%[1]s">%[3]s</a>
 create_issue=`hat Issue <a href="%s/issues/%s">%s#%[2]s</a> eröffnet`
 create_pull_request=`Pull-Anforderung erstellt <a href="%s/pulls/%s">%s#%[2]s</a>`
 comment_issue=`hat Issue <a href="%s/issues/%s">%s#%[2]s</a> kommentiert`
-merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
+merge_pull_request=`Pull-Request <a href="%s/pulls/%s">%s#%[2]s</a> zuammengeführt`
 transfer_repo=hat Repository <code>%s</code> transferiert an <a href="%s">%s</a>
 push_tag=hat nach <a href="%s/src/%s">%[2]s</a> in <a href="%[1]s">%[3]s</a> gepusht
 compare_2_commits=Zeige Vergleich dieser 2 Commits

+ 17 - 18
conf/locale/locale_en-US.ini

@@ -241,7 +241,7 @@ location = Location
 update_profile = Update Profile
 update_profile_success = Your profile has been updated successfully.
 change_username = Username Changed
-change_username_desc = You changed your username. This will affect the way how links relate to your account. Do you want to continue?
+change_username_prompt = This change will affect the way how links relate to your account.
 continue = Continue
 cancel = Cancel
 
@@ -630,7 +630,6 @@ release.tag_name_already_exist = Release with this tag name has already existed.
 [org]
 org_name_holder = Organization Name
 org_name_helper = Great organization names are short and memorable.
-org_email_helper = Organization's E-mail receives all notifications and confirmations.
 create_org = Create Organization
 repo_updated = Updated
 people = People
@@ -655,9 +654,9 @@ settings.full_name = Full Name
 settings.website = Website
 settings.location = Location
 settings.update_settings = Update Settings
-settings.change_orgname = Organization Name Changed
-settings.change_orgname_desc = Organization name has been changed. This will affect how links relate to the organization. Do you want to continue?
-settings.update_setting_success = Organization settings were successfully updated.
+settings.update_setting_success = Organization settings has been updated successfully.
+settings.change_orgname_prompt = This change will affect how links relate to the organization.
+settings.update_avatar_success = Organization avatar setting has been updated successfully.
 settings.delete = Delete Organization
 settings.delete_account = Delete This Organization
 settings.delete_prompt = The organization will be permanently removed, and this <strong>CANNOT</strong> be undone!
@@ -774,9 +773,9 @@ users.admin = Admin
 users.repos = Repos
 users.created = Created
 users.edit = Edit
-users.auth_source = Authorization Source
+users.auth_source = Authentication Source
 users.local = Local
-users.auth_login_name = Authorization Login Name
+users.auth_login_name = Authentication Login Name
 users.update_profile_success = Account profile has been updated successfully.
 users.edit_account = Edit Account
 users.is_activated = This account is activated
@@ -800,14 +799,14 @@ repos.watches = Watches
 repos.stars = Stars
 repos.issues = Issues
 
-auths.auth_manage_panel = Authorization Manage Panel
-auths.new = Add New Authorization Source
+auths.auth_manage_panel = Authentication Manage Panel
+auths.new = Add New Authentication Source
 auths.name = Name
 auths.type = Type
 auths.enabled = Enabled
 auths.updated = Updated
-auths.auth_type = Authorization Type
-auths.auth_name = Authorization Name
+auths.auth_type = Authentication Type
+auths.auth_name = Authentication Name
 auths.domain = Domain
 auths.host = Host
 auths.port = Port
@@ -821,7 +820,7 @@ auths.attribute_mail = E-mail attribute
 auths.filter = User Filter
 auths.admin_filter = Admin Filter
 auths.ms_ad_sa = Ms Ad SA
-auths.smtp_auth = SMTP Authorization Type
+auths.smtp_auth = SMTP Authentication Type
 auths.smtphost = SMTP Host
 auths.smtpport = SMTP Port
 auths.enable_tls = Enable TLS Encryption
@@ -829,13 +828,13 @@ auths.skip_tls_verify = Skip TLS Verify
 auths.pam_service_name = PAM Service Name
 auths.enable_auto_register = Enable Auto Registration
 auths.tips = Tips
-auths.edit = Edit Authorization Setting
+auths.edit = Edit Authentication Setting
 auths.activated = This authentication has activated
-auths.update_success = Authorization setting has been updated successfully.
-auths.update = Update Authorization Setting
-auths.delete = Delete This Authorization
-auths.delete_auth_title = Authorization Deletion
-auths.delete_auth_desc = This authorization is going to be deleted, do you want to continue?
+auths.update_success = Authentication setting has been updated successfully.
+auths.update = Update Authentication Setting
+auths.delete = Delete This Authentication
+auths.delete_auth_title = Authentication Deletion
+auths.delete_auth_desc = This authentication is going to be deleted, do you want to continue?
 
 config.server_config = Server Configuration
 config.app_name = Application Name

+ 127 - 127
conf/locale/locale_es-ES.ini

@@ -76,8 +76,8 @@ run_user=Abrir el usuario
 run_user_helper=El usuario necesita tener acceso a la Ruta Raíz del Repositorio y ejecutar Gogs.
 domain=Dominio
 domain_helper=Esto afecta a las URLs para clonar por SSH.
-ssh_port=SSH Port
-ssh_port_helper=Port number which your SSH server is using, leave it empty to disable SSH feature.
+ssh_port=Puerto SSH
+ssh_port_helper=Número de puerto de su servidor SSH, déjelo en blanco para desactivar SSH.
 http_port=Puerto HTTP
 http_port_helper=Puerto en el que escuchará la aplicación.
 app_url=URL de la aplicación
@@ -95,8 +95,8 @@ mail_notify=Habilitar las Notificaciones de Correo
 server_service_title=Configuración de Servidor y Otros Servicios
 offline_mode=Activar el modo Sin Conexión
 offline_mode_popup=Desactivar el CDN incluso en el modo de producción, todos los recursos se servirán localmente.
-disable_gravatar=Disable Gravatar Service
-disable_gravatar_popup=Disable Gravatar and custom sources, all avatars are uploaded by users or default.
+disable_gravatar=Desactivar el Servicio Gravatar
+disable_gravatar_popup=Desactivar Gravatar y cualquier fuente personalizada, todos los avatares deben ser cargados por los usuarios o el avatar por defecto.
 disable_registration=Desactivar Auto-Registro
 disable_registration_popup=Desactivar auto-registro del usuario, solo el administrador podrá crear cuentas nuevas.
 require_sign_in_view=Activar el Inicio de Sesión obligatorio para Ver Páginas
@@ -125,9 +125,9 @@ my_repos=Mis Repositorios
 collaborative_repos=Repositorios Colaborativos
 my_orgs=Mis Organizaciones
 my_mirrors=Mis Mirrors
-view_home=View %s
+view_home=Ver %s
 
-issues.in_your_repos=In your repositories
+issues.in_your_repos=En sus repositorios
 
 [explore]
 repos=Repositorios
@@ -281,14 +281,14 @@ key_name=Nombre de la Clave
 key_content=Contenido
 add_key_success=¡Nueva clave SSH '%s' añadida correctamente!
 delete_key=Eliminar
-ssh_key_deletion=SSH Key Deletion
-ssh_key_deletion_desc=Delete this SSH key will remove all related accesses for your account. Do you want to continue?
-ssh_key_deletion_success=SSH key has been deleted successfully!
+ssh_key_deletion=Borrado de Llave SSH
+ssh_key_deletion_desc=Al borrar esta llave SSH no podrá volver a acceder con ella a su cuenta. Desea continuar?
+ssh_key_deletion_success=¡La llave SSH ha sido eliminada con éxito!
 add_on=Añadido en
 last_used=Utilizado por última vez en
 no_activity=No hay actividad reciente
 key_state_desc=Esta clave ha sido usada en los últimos 7 días
-token_state_desc=This token is used in last 7 days
+token_state_desc=Este token ha sido usado en los últimos 7 días
 
 manage_social=Gestionar Redes Sociales asociadas
 social_desc=Esta es una lista de las Redes Sociales asociadas. Elimina cualquier vínculo que no reconozcas.
@@ -297,15 +297,15 @@ unbind_success=La Red Social ha sido desvinculada.
 
 manage_access_token=Gestionar los Tokens de Acceso personales
 generate_new_token=Generar nuevo Token
-tokens_desc=Tokens you have generated that can be used to access the Gogs APIs.
+tokens_desc=Tokens generados que se pueden usar para acceder al API de Gogs.
 new_token_desc=Desde ahora, todos los tokens tendrán acceso completo a tu cuenta.
 token_name=Nombre del Token
 generate_token=Generar Token
 generate_token_succees=¡Los nuevos tokens de acceso se han generado correctamente! Asegúrate de copiar tu nuevo token de acceso personal. ¡No podrás verlo de nuevo!
 delete_token=Eliminar
-access_token_deletion=Personal Access Token Deletion
-access_token_deletion_desc=Delete this personal access token will remove all related accesses of application. Do you want to continue?
-delete_token_success=Personal access token has been removed successfully! Don't forget to update your application as well.
+access_token_deletion=Borrado de Token de Acceso Personal
+access_token_deletion_desc=Si elimina este token de acceso personal la aplicación asociada perderá el permiso de acceso. ¿Desea continuar?
+delete_token_success=¡El token de acceso personal ha sido eliminado con éxito! No se olvide de actualizar también las aplicaciones asociadas.
 
 delete_account=Elimina tu cuenta
 delete_prompt=La operación eliminará tu cuenta de forma permanente y ¡<strong>NO</strong> se puede deshacer!
@@ -319,18 +319,18 @@ repo_name=Nombre del Repositorio
 repo_name_helper=Los grandes nombres de repositorios son cortos, memorables y <strong>únicos</strong>.
 visibility=Visibilidad
 visiblity_helper=Este repositorio es <span class="ui red text">Privado</span>
-visiblity_fork_helper=(Change of this value will affect all forks)
+visiblity_fork_helper=(Este cambio afectará a todos los forks)
 fork_repo=Hacer Fork del repositorio
 fork_from=Crear un Fork desde
 fork_visiblity_helper=No es posible cambiar la visibilidad de un Fork
 repo_desc=Descripción
 repo_lang=Idioma
-repo_lang_helper=Select .gitignore files
+repo_lang_helper=Seleccione archivo .gitignore
 license=Licencia
 license_helper=Selecciona un fichero de licencia
 readme=Readme
-readme_helper=Select a readme template
-auto_init=Initialize this repository selected files and template
+readme_helper=Seleccione una plantilla de archivo readme
+auto_init=Inicializar los archivos seleccionados y plantillas de este repositorio
 create_repo=Crear Repositorio
 default_branch=Rama por defecto
 mirror_interval=Intervalo de mirror(en horas)
@@ -340,10 +340,10 @@ form.name_pattern_not_allowed=El patrón del nombre del repositorio '%s' no est
 
 need_auth=Requiere Autorización
 migrate_type=Tipo de Migración
-migrate_type_helper=This repository will be a <span class="text blue">mirror</span>
+migrate_type_helper=Este repositorio será un <span class="text blue">mirror</span>
 migrate_repo=Migrar Repositorio
 migrate.clone_address=Clonar Dirección
-migrate.clone_address_desc=This can be a HTTP/HTTPS/GIT URL or local server path.
+migrate.clone_address_desc=Esto puede ser una URL HTTP/HTTPS/GIT o una ruta local del servidor.
 migrate.invalid_local_path=Rutal local inválida, no existe o no es un directorio.
 
 forked_from=forked de
@@ -410,46 +410,46 @@ issues.close_tab=%d cerradas
 issues.filter_label=Etiqueta
 issues.filter_label_no_select=Ninguna etiqueta seleccionada
 issues.filter_milestone=Milestone
-issues.filter_milestone_no_select=No selected milestone
+issues.filter_milestone_no_select=Milestone no seleccionado
 issues.filter_assignee=Asignada por
-issues.filter_assginee_no_select=No selected Assignee
+issues.filter_assginee_no_select=Sin asignar
 issues.filter_type=Tipo
 issues.filter_type.all_issues=Todas las incidencias
 issues.filter_type.assigned_to_you=Asignada a ti
 issues.filter_type.created_by_you=Creada por ti
 issues.filter_type.mentioning_you=Citado en
-issues.filter_sort=Sort
-issues.filter_sort.latest=Newest
-issues.filter_sort.oldest=Oldest
-issues.filter_sort.recentupdate=Recently updated
+issues.filter_sort=Ordenar
+issues.filter_sort.latest=Más recientes
+issues.filter_sort.oldest=Más antiguos
+issues.filter_sort.recentupdate=Actualizada recientemente
 issues.filter_sort.leastupdate=Least recently updated
-issues.filter_sort.mostcomment=Most commented
-issues.filter_sort.leastcomment=Least commented
-issues.opened_by=opened %[1]s by <a href="%[2]s">%[3]s</a>
-issues.opened_by_fake=opened %[1]s by %[2]s
+issues.filter_sort.mostcomment=Más comentadas
+issues.filter_sort.leastcomment=Menos comentadas
+issues.opened_by=abierta %[1]s por <a href="%[2]s">%[3]s</a>
+issues.opened_by_fake=abierta %[1]s por %[2]s
 issues.previous=Página Anterior
 issues.next=Página Siguiente
-issues.open_title=Open
-issues.closed_title=Closed
-issues.num_comments=%d comments
-issues.commented_at=`commented <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.no_content=There is no content yet.
-issues.close_issue=Close
-issues.close_comment_issue=Close and comment
-issues.reopen_issue=Reopen
-issues.reopen_comment_issue=Reopen and comment
-issues.create_comment=Comment
-issues.closed_at=`closed <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.reopened_at=`reopened <a id="%[1]s" href="#%[1]s">%[2]s</a>`
-issues.commit_ref_at=`referenced this issue from a commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.open_title=Abierta
+issues.closed_title=Cerrada
+issues.num_comments=%d comentarios
+issues.commented_at=`comentada <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.no_content=No hay contenido por el momento.
+issues.close_issue=Cerrar
+issues.close_comment_issue=Cerrar y Comentar
+issues.reopen_issue=Reabrir
+issues.reopen_comment_issue=Reabrir y Comentar
+issues.create_comment=Comentar
+issues.closed_at=`cerrada <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.reopened_at=`reabierta <a id="%[1]s" href="#%[1]s">%[2]s</a>`
+issues.commit_ref_at=`mencionada esta incidencia en un commit <a id="%[1]s" href="#%[1]s">%[2]s</a>`
 issues.poster=Poster
-issues.admin=Admin
-issues.owner=Owner
-issues.sign_up_for_free=Sign up for free
-issues.sign_in_require_desc=to join this conversation. Already have an account? <a href="%s">Sign in to comment</a>
-issues.edit=Edit
-issues.cancel=Cancel
-issues.save=Save
+issues.admin=Administrador
+issues.owner=Propietario
+issues.sign_up_for_free=Registro gratuito
+issues.sign_in_require_desc=para unirse a esta conversación. Ya dispone de una cuenta? <a href="%s">Inicie sesión para comentar</a>
+issues.edit=Editar
+issues.cancel=Cancelar
+issues.save=Guardar
 issues.label_title=Nombre etiqueta
 issues.label_color=Color etiqueta
 issues.label_count=%d etiquetas
@@ -461,52 +461,52 @@ issues.label_deletion=Borrado de Etiqueta
 issues.label_deletion_desc=Al borrar la etiqueta su información será eliminada de todas las incidencias relacionadas. Desea continuar?
 issues.label_deletion_success=Etiqueta borrada con éxito!
 
-pulls.compare_changes=Compare Changes
-pulls.compare_changes_desc=Compare two branches and make a pull request for changes.
+pulls.compare_changes=Comparar Cambios
+pulls.compare_changes_desc=Comparar dos ramas y generar un pull request con las diferencias.
 pulls.compare_base=base
-pulls.compare_compare=compare
-pulls.filter_branch=Filter branch
-pulls.no_results=No results found.
-pulls.nothing_to_compare=There is nothing to compare because base and head branches are even.
-pulls.has_pull_request=`There is already a pull request between these two targets: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
-pulls.create=Create Pull Request
-pulls.title_desc=wants to merge %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code>
-pulls.merged_title_desc=merged %[1]d commits from <code>%[2]s</code> into <code>%[3]s</code> %[4]s
-pulls.tab_conversation=Conversation
+pulls.compare_compare=comparar con
+pulls.filter_branch=Filtrar rama
+pulls.no_results=No se han encontrado resultados.
+pulls.nothing_to_compare=No hay nada que comparar porque las dos ramas coinciden.
+pulls.has_pull_request=`Ya existe un pull request entre estas dos ramas: <a href="%[1]s/pulls/%[3]d">%[2]s#%[3]d</a>`
+pulls.create=Nuevo Pull Request
+pulls.title_desc=desea fusionar %[1]d commits de <code>%[2]s</code> en <code>%[3]s</code>
+pulls.merged_title_desc=fusionados %[1]d commits de <code>%[2]s</code> en <code>%[3]s</code> %[4]s
+pulls.tab_conversation=Conversación
 pulls.tab_commits=Commits
-pulls.tab_files=Files changed
-pulls.reopen_to_merge=Please reopen this pull request to perform merge operation.
-pulls.merged=Merged
-pulls.has_merged=This pull request has been merged successfully!
-pulls.data_broken=Data of this pull request has been broken due to deletion of fork information.
-pulls.can_auto_merge_desc=You can perform auto-merge operation on this pull request.
-pulls.cannot_auto_merge_desc=You can't perform auto-merge operation because there are conflicts between commits.
-pulls.cannot_auto_merge_helper=Please use command line tool to solve it.
-pulls.merge_pull_request=Merge Pull Request
-
-milestones.new=New Milestone
-milestones.open_tab=%d Open
-milestones.close_tab=%d Closed
-milestones.closed=Closed %s
-milestones.no_due_date=No due date
-milestones.open=Open
-milestones.close=Close
-milestones.new_subheader=Create milestones to organize your issues.
-milestones.create=Create Milestone
-milestones.title=Title
-milestones.desc=Description
-milestones.due_date=Due Date (optional)
-milestones.clear=Clear
-milestones.invalid_due_date_format=Due date format is invalid, must be 'year-mm-dd'.
-milestones.create_success=Milestone '%s' has been created successfully!
-milestones.edit=Edit Milestone
-milestones.edit_subheader=Use better description for milestones so people won't be confused.
-milestones.cancel=Cancel
-milestones.modify=Modify Milestone
-milestones.edit_success=Changes of milestone '%s' has been saved successfully!
-milestones.deletion=Milestone Deletion
-milestones.deletion_desc=Delete this milestone will remove its information in all related issues. Do you want to continue?
-milestones.deletion_success=Milestone has been deleted successfully!
+pulls.tab_files=Archivos modificados
+pulls.reopen_to_merge=Por favor reabra este pull request para proceder con la operación de fusionado.
+pulls.merged=Fuisionado
+pulls.has_merged=¡Este pull request se ha completado con éxito!
+pulls.data_broken=Los datos de este pull request ya no están disponibles porque se ha eliminado la información del fork.
+pulls.can_auto_merge_desc=Puede realizar la operación auto-fusionado en este pull request.
+pulls.cannot_auto_merge_desc=No puede realizar la operación de auto-fusionado porque existen conflictos entre los commits.
+pulls.cannot_auto_merge_helper=Por favor use la línea de comandos para resolverlo.
+pulls.merge_pull_request=Fusionar Pull Request
+
+milestones.new=Nuevo Milestone
+milestones.open_tab=%d abiertas
+milestones.close_tab=%d cerradas
+milestones.closed=Cerrada %s
+milestones.no_due_date=Sin fecha límite
+milestones.open=Abrir
+milestones.close=Cerrada
+milestones.new_subheader=Cree milestones para organizar las incidencias.
+milestones.create=Nuevo Milestone
+milestones.title=Título
+milestones.desc=Descripción
+milestones.due_date=Fecha límite (opcional)
+milestones.clear=Eliminar
+milestones.invalid_due_date_format=El formato de la fecha límite no es válido, debe ser 'y-mm-dd'.
+milestones.create_success=¡El milestone '%s' ha sido creado con éxito!
+milestones.edit=Editar Milestone
+milestones.edit_subheader=Use una buena descripción en el milestone para no confundir al resto de usuarios.
+milestones.cancel=Cancelar
+milestones.modify=Modificar Milestone
+milestones.edit_success=¡Los cambios al milestone '%s' se han guardado con éxito!
+milestones.deletion=Borrar milestone
+milestones.deletion_desc=El borrado de este milestone eliminará su información y las incidencias asociadas. ¿Desea continuar?
+milestones.deletion_success=¡El milestone ha sido eliminado con éxito!
 
 settings=Configuración
 settings.options=Opciones
@@ -523,14 +523,14 @@ settings.transfer_desc=Transferir este repositorio a otro usuario u organizació
 settings.new_owner_has_same_repo=El nuevo propietario tiene un repositorio con el mismo nombre.
 settings.delete=Eliminar este Repositorio
 settings.delete_desc=Una vez has eliminado un repositorio, no hay vuelta atrás. Por favor, asegúrate de que es lo que quieres.
-settings.transfer_notices_1=- You will lose access if new owner is a individual user.
-settings.transfer_notices_2=- You will conserve access if new owner is an organization and if you're one of the owners.
-settings.transfer_form_title=Please enter following information to confirm your operation:
-settings.delete_notices_1=- This operation <strong>CANNOT</strong> be undone.
-settings.delete_notices_2=- This operation will permanently delete the everything of this repository, including Git data, issues, comments and accesses of collaborators.
-settings.delete_notices_fork_1=- If this repository is public, all forks will be became independent after deletion.
-settings.delete_notices_fork_2=- If this repository is private, all forks will be removed at the same time.
-settings.delete_notices_fork_3=- If you want to keep all forks after deletion, please change visibility of this repository to public first.
+settings.transfer_notices_1=- Perderá el permiso de acceso si el nuevo propietario es otro usuario.
+settings.transfer_notices_2=- Conservará el privilegio de acceso si el nuevo propietario es una organización y usted es uno de los propietarios de dicha organización.
+settings.transfer_form_title=Por favor introduzca esta información para confirmar la operación:
+settings.delete_notices_1=- Esta operación <strong>NO PUEDE</strong> revertirse.
+settings.delete_notices_2=- Esta operación eliminará de manera permanente todo el contenido de este repositorio, incluyendo los datos de git, las incidencias, los comentarios y los permisos de acceso de los colaboradores.
+settings.delete_notices_fork_1=- Si este repositorio es público, todos los forks se convertirán en repositorios independientes tras el borrado.
+settings.delete_notices_fork_2=- Si este repositorio es privado, todos los forks serán eliminados simultáneamente.
+settings.delete_notices_fork_3=- Si desea mantener los forks tras el borrado, por favor convierta este repositorio en público antes de proceder.
 settings.update_settings_success=Las opciones del repositorio se han actualizado correctamente.
 settings.transfer_owner=Nuevo Propietario
 settings.make_transfer=Transferir
@@ -542,14 +542,14 @@ settings.remove_collaborator_success=Se ha eliminado el colaborador.
 settings.user_is_org_member=El usuario es miembro de la organización, no puede ser añadido como colaborador.
 settings.add_webhook=Añadir Webhook
 settings.hooks_desc=Los Webhooks permiten a servicios externos recibir notificaciones cuando sucedan ciertos eventos en Gogs. Cuando sucedan los eventos especificados, enviaremos una petición POST a cada una de las URLs indicadas. Para obtener más información, consulta nuestra <a target="_blank" href="%s">Guía de Webhooks</a>.
-settings.webhook_deletion=Delete Webhook
-settings.webhook_deletion_desc=Delete this webhook will remove its information and all delivery history. Do you want to continue?
-settings.webhook_deletion_success=Webhook has been deleted successfully!
-settings.webhook.request=Request
-settings.webhook.response=Response
-settings.webhook.headers=Headers
+settings.webhook_deletion=Eliminar Webhook
+settings.webhook_deletion_desc=El borrado de este webhook eliminará su información y todo su historial. ¿Desea continuar?
+settings.webhook_deletion_success=¡Webhook eliminado con éxito!
+settings.webhook.request=Petición
+settings.webhook.response=Respuesta
+settings.webhook.headers=Encabezado
 settings.webhook.payload=Payload
-settings.webhook.body=Body
+settings.webhook.body=Cuerpo del mensaje
 settings.githooks_desc=Los Git Hooks son una funcionalidad del propio Git, puedes editar los ficheros de los hooks soportados en la siguiente lista para aplicar operaciones personalizadas.
 settings.githook_edit_desc=Si el hook no está activo, se mostrará contenido de ejemplo. Dejar el contenido vacío deshabilitará este hook.
 settings.githook_name=Nombre del Hook
@@ -559,17 +559,17 @@ settings.add_webhook_desc=Enviaremos una petición <code>POST</code> a la siguie
 settings.payload_url=URL de Payload
 settings.content_type=Tipo de Contenido
 settings.secret=Secreto
-settings.slack_username=Username
-settings.slack_icon_url=Icon URL
+settings.slack_username=Nombre de usuario
+settings.slack_icon_url=URL de icono
 settings.slack_color=Color
 settings.event_desc=¿Qué eventos te gustaría que desencadenasen este webhook?
 settings.event_push_only=Solo el evento <code>push</code>.
-settings.event_send_everything=I need <strong>everything</strong>.
-settings.event_choose=Let me choose what I need.
-settings.event_create=Create
-settings.event_create_desc=Branch, or tag created
+settings.event_send_everything=Necesito <strong>todo</strong>.
+settings.event_choose=Déjeme elegir lo que necesito.
+settings.event_create=Crear
+settings.event_create_desc=Branch o etiqueta creada
 settings.event_push=Push
-settings.event_push_desc=Git push to a repository
+settings.event_push_desc=Git push a un repositorio
 settings.active=Activo
 settings.active_helper=Enviaremos detalles del evento cuando este hook se dispare.
 settings.add_hook_success=Se ha añadido un nuevo webhook.
@@ -585,8 +585,8 @@ settings.slack_channel=Canal
 settings.deploy_keys=Claves de Despliegue
 settings.add_deploy_key=Add Deploy Key
 settings.no_deploy_keys=You haven't added any deploy key.
-settings.title=Title
-settings.deploy_key_content=Content
+settings.title=Título
+settings.deploy_key_content=Contenido
 settings.key_been_used=Deploy key content has been used.
 settings.key_name_used=Deploy key with same name has already existed.
 settings.add_key_success=New deploy key '%s' has been added successfully!
@@ -824,7 +824,7 @@ auths.smtp_auth=Tipo de Autorización SMTP
 auths.smtphost=SMTP Host
 auths.smtpport=Puerto SMTP
 auths.enable_tls=Habilitar Cifrado TLS
-auths.skip_tls_verify=Skip TLS Verify
+auths.skip_tls_verify=Omitir la verificación TLS
 auths.pam_service_name=Nombre del Servicio PAM
 auths.enable_auto_register=Hablilitar Auto-Registro
 auths.tips=Consejos
@@ -869,7 +869,7 @@ config.enable_cache_avatar=Activar la Caché de Avatar
 config.active_code_lives=Habilitar Vida del Código
 config.reset_password_code_lives=Restablecer Contraseña de Vida del Código
 config.webhook_config=Configuración de Webhooks
-config.queue_length=Queue Length
+config.queue_length=Tamaño de Cola de Envío
 config.deliver_timeout=Timeout de Entrega
 config.skip_tls_verify=Omitir la Verificación TLS
 config.mailer_config=Configuración del Mailer
@@ -919,12 +919,12 @@ notices.delete_success=La notificación del sistema se ha eliminado correctament
 
 [action]
 create_repo=Repositorio creado <a href="%s">%s</a>
-rename_repo=renamed repository from <code>%[1]s</code> to <a href="%[2]s">%[3]s</a>
+rename_repo=repositorio renombrado de <code>%[1]s</code> a <a href="%[2]s">%[3]s</a>
 commit_repo=hizo push a <a href="%s/src/%s">%[2]s</a> en <a href="%[1]s">%[3]s</a>
 create_issue=`incidencia abierta <a href="%s/issues/%s">%s#%[2]s</a>`
-create_pull_request=`created pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
+create_pull_request=`creado pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 comment_issue=`comentó en la incidencia <a href="%s/issues/%s">%s#%[2]s</a>`
-merge_pull_request=`merged pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
+merge_pull_request=`fusionado pull request <a href="%s/pulls/%s">%s#%[2]s</a>`
 transfer_repo=transfirió el repositorio <code>%s</code> a <a href="%s">%s</a>
 push_tag=hizo push del tag <a href="%s/src/%s">%[2]s</a> a <a href="%[1]s">%[3]s</a>
 compare_2_commits=Ver la comparación de estos 2 commits
@@ -951,8 +951,8 @@ raw_seconds=segundos
 raw_minutes=minutos
 
 [dropzone]
-default_message=Drop files here or click to upload.
-invalid_input_type=You can't upload files of this type.
-file_too_big=File size({{filesize}} MB) exceeds maximum size({{maxFilesize}} MB).
-remove_file=Remove file
+default_message=Suéltelos aquí o pulse para cargar archivos.
+invalid_input_type=No está permitido cargar archivos de este tipo.
+file_too_big=El tamaño del archivo ({{filesize}} MB) excede el tamaño máximo ({{maxFilesize}} MB).
+remove_file=Eliminar archivo
 

+ 2 - 2
conf/locale/locale_pt-BR.ini

@@ -328,7 +328,7 @@ repo_lang=Idioma
 repo_lang_helper=Selecione arquivos .gitignore
 license=Licença
 license_helper=Selecione um arquivo de licença
-readme=Readme
+readme=Leia-me
 readme_helper=Select a readme template
 auto_init=Initialize this repository selected files and template
 create_repo=Criar Repositório
@@ -464,7 +464,7 @@ issues.label_deletion_success=A etiqueta foi excluída com sucesso!
 pulls.compare_changes=Comparar mudanças
 pulls.compare_changes_desc=Comparar dois ramos e criar solicitação de pull com as mudanças.
 pulls.compare_base=base
-pulls.compare_compare=compare
+pulls.compare_compare=comparar
 pulls.filter_branch=Filter branch
 pulls.no_results=Nada encontrado.
 pulls.nothing_to_compare=There is nothing to compare because base and head branches are even.

+ 8 - 8
conf/locale/locale_zh-HK.ini

@@ -95,8 +95,8 @@ mail_notify=啟用郵件通知提醒
 server_service_title=伺服器和其他服務設置
 offline_mode=啓用離線模式
 offline_mode_popup=在部署模式下也禁用從 CDN 獲取文件,所有的資源將從本地伺服器獲取。
-disable_gravatar=Disable Gravatar Service
-disable_gravatar_popup=Disable Gravatar and custom sources, all avatars are uploaded by users or default.
+disable_gravatar=禁用 Gravatar 服務
+disable_gravatar_popup=禁用 Gravatar 和自定義源,僅使用由用戶上傳或默認的頭像。
 disable_registration=禁止用戶自主註冊
 disable_registration_popup=禁止用戶自主註冊功能,只有管理員可以添加帳號。
 require_sign_in_view=啓用登錄訪問限制
@@ -125,9 +125,9 @@ my_repos=我的倉庫
 collaborative_repos=參與協作的倉庫
 my_orgs=我的組織
 my_mirrors=我的鏡像
-view_home=View %s
+view_home=訪問 %s
 
-issues.in_your_repos=In your repositories
+issues.in_your_repos=屬於該用戶倉庫的
 
 [explore]
 repos=探索倉庫
@@ -281,9 +281,9 @@ key_name=密鑰名稱
 key_content=密鑰內容
 add_key_success=新的 SSH 密鑰 '%s' 添加成功!
 delete_key=刪除
-ssh_key_deletion=SSH Key Deletion
-ssh_key_deletion_desc=Delete this SSH key will remove all related accesses for your account. Do you want to continue?
-ssh_key_deletion_success=SSH key has been deleted successfully!
+ssh_key_deletion=刪除 SSH 公鑰
+ssh_key_deletion_desc=刪除該 SSH 公鑰將刪除所有與您帳戶相關的訪問權限。是否繼續?
+ssh_key_deletion_success=SSH 公鑰刪除成功!
 add_on=增加於
 last_used=上次使用在
 no_activity=沒有最近活動
@@ -319,7 +319,7 @@ repo_name=倉庫名稱
 repo_name_helper=偉大的倉庫名稱一般都較短、令人深刻並且 <strong>獨一無二</strong> 的。
 visibility=可見度
 visiblity_helper=該倉庫為 <span class="ui red text">私有的</span>
-visiblity_fork_helper=(Change of this value will affect all forks)
+visiblity_fork_helper=(修改該值將會影響到所有派生倉庫)
 fork_repo=派生倉庫
 fork_from=派生自
 fork_visiblity_helper=派生倉庫無法修改可見性。

+ 0 - 4
docker/start.sh

@@ -21,10 +21,6 @@ fi
 
 service ssh start
 
-# sync templates
-test -d /data/gogs/templates || cp -ar ./templates /data/gogs/
-rsync -rtv /data/gogs/templates/ ./templates/
-
 ln -sf /data/gogs/log ./log
 ln -sf /data/gogs/data ./data
 ln -sf /data/git /home/git

+ 1 - 1
gogs.go

@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.6.9.0903 Beta"
+const APP_VER = "0.6.9.0909 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())

+ 1 - 1
models/issue.go

@@ -758,7 +758,7 @@ func GetUserIssueStats(repoID, uid int64, repoIDs []int64, filterMode int, isPul
 
 	queryStr := "SELECT COUNT(*) FROM `issue` "
 	baseCond := " WHERE issue.is_closed=?"
-	if repoID > 0 {
+	if repoID > 0 || len(repoIDs) == 0 {
 		baseCond += " AND issue.repo_id=" + com.ToStr(repoID)
 	} else {
 		baseCond += " AND issue.repo_id IN (" + strings.Join(base.Int64sToStrings(repoIDs), ",") + ")"

+ 9 - 0
models/models.go

@@ -72,6 +72,7 @@ var (
 	}
 
 	EnableSQLite3 bool
+	EnableTidb    bool
 )
 
 func init() {
@@ -143,6 +144,14 @@ func getEngine() (*xorm.Engine, error) {
 			return nil, fmt.Errorf("Fail to create directories: %v", err)
 		}
 		cnnstr = "file:" + DbCfg.Path + "?cache=shared&mode=rwc"
+	case "tidb":
+		if !EnableTidb {
+			return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
+		}
+		if err := os.MkdirAll(path.Dir(DbCfg.Path), os.ModePerm); err != nil {
+			return nil, fmt.Errorf("Fail to create directories: %v", err)
+		}
+		cnnstr = "goleveldb://" + DbCfg.Path
 	default:
 		return nil, fmt.Errorf("Unknown database type: %s", DbCfg.Type)
 	}

+ 16 - 0
models/models_tidb.go

@@ -0,0 +1,16 @@
+// +build tidb go1.4.2
+
+// Copyright 2015 The Gogs Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package models
+
+import (
+	_ "github.com/go-xorm/tidb"
+	_ "github.com/pingcap/tidb"
+)
+
+func init() {
+	EnableTidb = true
+}

+ 45 - 48
models/org.go

@@ -10,7 +10,7 @@ import (
 	"os"
 	"strings"
 
-	"github.com/gogits/gogs/modules/base"
+	"github.com/go-xorm/xorm"
 )
 
 var (
@@ -93,17 +93,6 @@ func (org *User) RemoveOrgRepo(repoID int64) error {
 	return org.removeOrgRepo(x, repoID)
 }
 
-// IsOrgEmailUsed returns true if the e-mail has been used in organization account.
-func IsOrgEmailUsed(email string) (bool, error) {
-	if len(email) == 0 {
-		return false, nil
-	}
-	return x.Get(&User{
-		Email: email,
-		Type:  ORGANIZATION,
-	})
-}
-
 // CreateOrganization creates record of a new organization.
 func CreateOrganization(org, owner *User) (err error) {
 	if err = IsUsableName(org.Name); err != nil {
@@ -117,18 +106,9 @@ func CreateOrganization(org, owner *User) (err error) {
 		return ErrUserAlreadyExist{org.Name}
 	}
 
-	isExist, err = IsOrgEmailUsed(org.Email)
-	if err != nil {
-		return err
-	} else if isExist {
-		return ErrEmailAlreadyUsed{org.Email}
-	}
-
 	org.LowerName = strings.ToLower(org.Name)
 	org.FullName = org.Name
-	org.Avatar = base.EncodeMd5(org.Email)
-	org.AvatarEmail = org.Email
-	// No password for organization.
+	org.UseCustomAvatar = true
 	org.NumTeams = 1
 	org.NumMembers = 1
 
@@ -141,6 +121,17 @@ func CreateOrganization(org, owner *User) (err error) {
 	if _, err = sess.Insert(org); err != nil {
 		return fmt.Errorf("insert organization: %v", err)
 	}
+	org.GenerateRandomAvatar()
+
+	// Add initial creator to organization and owner team.
+	if _, err = sess.Insert(&OrgUser{
+		Uid:      owner.Id,
+		OrgID:    org.Id,
+		IsOwner:  true,
+		NumTeams: 1,
+	}); err != nil {
+		return fmt.Errorf("insert org-user relation: %v", err)
+	}
 
 	// Create default owner team.
 	t := &Team{
@@ -154,23 +145,11 @@ func CreateOrganization(org, owner *User) (err error) {
 		return fmt.Errorf("insert owner team: %v", err)
 	}
 
-	// Add initial creator to organization and owner team.
-	ou := &OrgUser{
-		Uid:      owner.Id,
-		OrgID:    org.Id,
-		IsOwner:  true,
-		NumTeams: 1,
-	}
-	if _, err = sess.Insert(ou); err != nil {
-		return fmt.Errorf("insert org-user relation: %v", err)
-	}
-
-	tu := &TeamUser{
+	if _, err = sess.Insert(&TeamUser{
 		Uid:    owner.Id,
 		OrgID:  org.Id,
 		TeamID: t.ID,
-	}
-	if _, err = sess.Insert(tu); err != nil {
+	}); err != nil {
 		return fmt.Errorf("insert team-user relation: %v", err)
 	}
 
@@ -212,7 +191,6 @@ func GetOrganizations(num, offset int) ([]*User, error) {
 	return orgs, err
 }
 
-// TODO: need some kind of mechanism to record failure.
 // DeleteOrganization completely and permanently deletes everything of organization.
 func DeleteOrganization(org *User) (err error) {
 	if err := DeleteUser(org); err != nil {
@@ -220,23 +198,23 @@ func DeleteOrganization(org *User) (err error) {
 	}
 
 	sess := x.NewSession()
-	defer sess.Close()
+	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
 		return err
 	}
 
-	if _, err = sess.Delete(&Team{OrgID: org.Id}); err != nil {
-		sess.Rollback()
-		return err
-	}
-	if _, err = sess.Delete(&OrgUser{OrgID: org.Id}); err != nil {
-		sess.Rollback()
-		return err
+	if err = deleteBeans(sess,
+		&Team{OrgID: org.Id},
+		&OrgUser{OrgID: org.Id},
+		&TeamUser{OrgID: org.Id},
+	); err != nil {
+		return fmt.Errorf("deleteBeans: %v", err)
 	}
-	if _, err = sess.Delete(&TeamUser{OrgID: org.Id}); err != nil {
-		sess.Rollback()
-		return err
+
+	if err = deleteUser(sess, org); err != nil {
+		return fmt.Errorf("deleteUser: %v", err)
 	}
+
 	return sess.Commit()
 }
 
@@ -275,6 +253,25 @@ func IsPublicMembership(orgId, uid int64) bool {
 	return has
 }
 
+func getOwnedOrgsByUserID(sess *xorm.Session, userID int64) ([]*User, error) {
+	orgs := make([]*User, 0, 10)
+	return orgs, sess.Where("`org_user`.uid=?", userID).And("`org_user`.is_owner=?", true).
+		Join("INNER", "`org_user`", "`org_user`.org_id=`user`.id").Find(&orgs)
+}
+
+// GetOwnedOrgsByUserID returns a list of organizations are owned by given user ID.
+func GetOwnedOrgsByUserID(userID int64) ([]*User, error) {
+	sess := x.NewSession()
+	return getOwnedOrgsByUserID(sess, userID)
+}
+
+// GetOwnedOrganizationsByUserIDDesc returns a list of organizations are owned by
+// given user ID and descring order by given condition.
+func GetOwnedOrgsByUserIDDesc(userID int64, desc string) ([]*User, error) {
+	sess := x.NewSession()
+	return getOwnedOrgsByUserID(sess.Desc(desc), userID)
+}
+
 // GetOrgUsersByUserId returns all organization-user relations by user ID.
 func GetOrgUsersByUserId(uid int64) ([]*OrgUser, error) {
 	ous := make([]*OrgUser, 0, 10)

+ 21 - 2
models/repo.go

@@ -272,6 +272,11 @@ func (repo *Repository) IsOwnedBy(userID int64) bool {
 	return repo.OwnerID == userID
 }
 
+// CanBeForked returns true if repository meets the requirements of being forked.
+func (repo *Repository) CanBeForked() bool {
+	return !repo.IsBare && !repo.IsMirror
+}
+
 func (repo *Repository) NextIssueIndex() int64 {
 	return int64(repo.NumIssues+repo.NumPulls) + 1
 }
@@ -465,6 +470,16 @@ func MigrateRepository(u *User, name, desc string, private, mirror bool, url str
 		return repo, fmt.Errorf("create update hook: %v", err)
 	}
 
+	// Check if repository is empty.
+	_, stderr, err = com.ExecCmdDir(repoPath, "git", "log", "-1")
+	if err != nil {
+		if strings.Contains(stderr, "fatal: bad default revision 'HEAD'") {
+			repo.IsBare = true
+		} else {
+			return repo, fmt.Errorf("check bare: %v - %s", err, stderr)
+		}
+	}
+
 	// Check if repository has master branch, if so set it to default branch.
 	gitRepo, err := git.OpenRepository(repoPath)
 	if err != nil {
@@ -615,7 +630,7 @@ func initRepository(e Engine, repoPath string, u *User, repo *Repository, opts C
 	}
 
 	tmpDir := filepath.Join(os.TempDir(), "gogs-"+repo.Name+"-"+com.ToStr(time.Now().Nanosecond()))
-	fmt.Println(tmpDir)
+
 	// Initialize repository according to user's choice.
 	if opts.AutoInit {
 		os.MkdirAll(tmpDir, os.ModePerm)
@@ -1185,9 +1200,13 @@ func GetRecentUpdatedRepositories(page int) (repos []*Repository, err error) {
 		Where("is_private=?", false).Limit(setting.ExplorePagingNum).Desc("updated").Find(&repos)
 }
 
+func getRepositoryCount(e Engine, u *User) (int64, error) {
+	return x.Count(&Repository{OwnerID: u.Id})
+}
+
 // GetRepositoryCount returns the total number of repositories of user.
 func GetRepositoryCount(u *User) (int64, error) {
-	return x.Count(&Repository{OwnerID: u.Id})
+	return getRepositoryCount(x, u)
 }
 
 type SearchOption struct {

+ 102 - 76
models/user.go

@@ -55,12 +55,13 @@ type User struct {
 	Name      string `xorm:"UNIQUE NOT NULL"`
 	FullName  string
 	// Email is the primary email address (to be used for communication).
-	Email       string `xorm:"UNIQUE(s) NOT NULL"`
+	Email       string `xorm:"NOT NULL"`
 	Passwd      string `xorm:"NOT NULL"`
 	LoginType   LoginType
 	LoginSource int64 `xorm:"NOT NULL DEFAULT 0"`
 	LoginName   string
-	Type        UserType      `xorm:"UNIQUE(s)"`
+	Type        UserType
+	OwnedOrgs   []*User       `xorm:"-"`
 	Orgs        []*User       `xorm:"-"`
 	Repos       []*Repository `xorm:"-"`
 	Location    string
@@ -132,42 +133,56 @@ func (u *User) HomeLink() string {
 	return setting.AppSubUrl + "/" + u.Name
 }
 
+// CustomAvatarPath returns user custom avatar file path.
+func (u *User) CustomAvatarPath() string {
+	return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
+}
+
+// GenerateRandomAvatar generates a random avatar for user.
+func (u *User) GenerateRandomAvatar() error {
+	seed := u.Email
+	if len(seed) == 0 {
+		seed = u.Name
+	}
+
+	img, err := avatar.RandomImage([]byte(seed))
+	if err != nil {
+		return fmt.Errorf("RandomImage: %v", err)
+	}
+	if err = os.MkdirAll(path.Dir(u.CustomAvatarPath()), os.ModePerm); err != nil {
+		return fmt.Errorf("MkdirAll: %v", err)
+	}
+	fw, err := os.Create(u.CustomAvatarPath())
+	if err != nil {
+		return fmt.Errorf("Create: %v", err)
+	}
+	defer fw.Close()
+
+	if err = jpeg.Encode(fw, img, nil); err != nil {
+		return fmt.Errorf("Encode: %v", err)
+	}
+
+	log.Info("New random avatar created: %d", u.Id)
+	return nil
+}
+
 func (u *User) RelAvatarLink() string {
 	defaultImgUrl := "/img/avatar_default.jpg"
 	if u.Id == -1 {
 		return defaultImgUrl
 	}
 
-	imgPath := path.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
 	switch {
 	case u.UseCustomAvatar:
-		if !com.IsExist(imgPath) {
+		if !com.IsExist(u.CustomAvatarPath()) {
 			return defaultImgUrl
 		}
 		return "/avatars/" + com.ToStr(u.Id)
 	case setting.DisableGravatar, setting.OfflineMode:
-		if !com.IsExist(imgPath) {
-			img, err := avatar.RandomImage([]byte(u.Email))
-			if err != nil {
-				log.Error(3, "RandomImage: %v", err)
-				return defaultImgUrl
-			}
-			if err = os.MkdirAll(path.Dir(imgPath), os.ModePerm); err != nil {
-				log.Error(3, "MkdirAll: %v", err)
-				return defaultImgUrl
-			}
-			fw, err := os.Create(imgPath)
-			if err != nil {
-				log.Error(3, "Create: %v", err)
-				return defaultImgUrl
-			}
-			defer fw.Close()
-
-			if err = jpeg.Encode(fw, img, nil); err != nil {
-				log.Error(3, "Encode: %v", err)
-				return defaultImgUrl
+		if !com.IsExist(u.CustomAvatarPath()) {
+			if err := u.GenerateRandomAvatar(); err != nil {
+				log.Error(3, "GenerateRandomAvatar: %v", err)
 			}
-			log.Info("New random avatar created: %d", u.Id)
 		}
 
 		return "/avatars/" + com.ToStr(u.Id)
@@ -208,11 +223,6 @@ func (u *User) ValidatePassword(passwd string) bool {
 	return u.Passwd == newUser.Passwd
 }
 
-// CustomAvatarPath returns user custom avatar file path.
-func (u *User) CustomAvatarPath() string {
-	return filepath.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
-}
-
 // UploadAvatar saves custom avatar for user.
 // FIXME: split uploads to different subdirs in case we have massive users.
 func (u *User) UploadAvatar(data []byte) error {
@@ -222,28 +232,27 @@ func (u *User) UploadAvatar(data []byte) error {
 	if err != nil {
 		return err
 	}
+
 	m := resize.Resize(234, 234, img, resize.NearestNeighbor)
 
 	sess := x.NewSession()
-	defer sess.Close()
+	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
 		return err
 	}
 
 	if _, err = sess.Id(u.Id).AllCols().Update(u); err != nil {
-		sess.Rollback()
 		return err
 	}
 
 	os.MkdirAll(setting.AvatarUploadPath, os.ModePerm)
 	fw, err := os.Create(u.CustomAvatarPath())
 	if err != nil {
-		sess.Rollback()
 		return err
 	}
 	defer fw.Close()
+
 	if err = jpeg.Encode(fw, m, nil); err != nil {
-		sess.Rollback()
 		return err
 	}
 
@@ -284,9 +293,13 @@ func (u *User) IsPublicMember(orgId int64) bool {
 	return IsPublicMembership(orgId, u.Id)
 }
 
+func (u *User) getOrganizationCount(e Engine) (int64, error) {
+	return e.Where("uid=?", u.Id).Count(new(OrgUser))
+}
+
 // GetOrganizationCount returns count of membership of organization of user.
 func (u *User) GetOrganizationCount() (int64, error) {
-	return x.Where("uid=?", u.Id).Count(new(OrgUser))
+	return u.getOrganizationCount(x)
 }
 
 // GetRepositories returns all repositories that user owns, including private repositories.
@@ -295,6 +308,12 @@ func (u *User) GetRepositories() (err error) {
 	return err
 }
 
+// GetOwnedOrganizations returns all organizations that user owns.
+func (u *User) GetOwnedOrganizations() (err error) {
+	u.OwnedOrgs, err = GetOwnedOrgsByUserID(u.Id)
+	return err
+}
+
 // GetOrganizations returns all organizations that user belongs to.
 func (u *User) GetOrganizations() error {
 	ous, err := GetOrgUsersByUserId(u.Id)
@@ -490,12 +509,20 @@ func ChangeUserName(u *User, newUserName string) (err error) {
 }
 
 func updateUser(e Engine, u *User) error {
-	u.Email = strings.ToLower(u.Email)
-	has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
-	if err != nil {
-		return err
-	} else if has {
-		return ErrEmailAlreadyUsed{u.Email}
+	// Organization does not need e-mail.
+	if !u.IsOrganization() {
+		u.Email = strings.ToLower(u.Email)
+		has, err := e.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
+		if err != nil {
+			return err
+		} else if has {
+			return ErrEmailAlreadyUsed{u.Email}
+		}
+
+		if len(u.AvatarEmail) == 0 {
+			u.AvatarEmail = u.Email
+		}
+		u.Avatar = avatar.HashEmail(u.AvatarEmail)
 	}
 
 	u.LowerName = strings.ToLower(u.Name)
@@ -510,13 +537,8 @@ func updateUser(e Engine, u *User) error {
 		u.Description = u.Description[:255]
 	}
 
-	if u.AvatarEmail == "" {
-		u.AvatarEmail = u.Email
-	}
-	u.Avatar = avatar.HashEmail(u.AvatarEmail)
-
 	u.FullName = base.Sanitizer.Sanitize(u.FullName)
-	_, err = e.Id(u.Id).AllCols().Update(u)
+	_, err := e.Id(u.Id).AllCols().Update(u)
 	return err
 }
 
@@ -525,8 +547,8 @@ func UpdateUser(u *User) error {
 	return updateUser(x, u)
 }
 
-// DeleteBeans deletes all given beans, beans should contain delete conditions.
-func DeleteBeans(e Engine, beans ...interface{}) (err error) {
+// deleteBeans deletes all given beans, beans should contain delete conditions.
+func deleteBeans(e Engine, beans ...interface{}) (err error) {
 	for i := range beans {
 		if _, err = e.Delete(beans[i]); err != nil {
 			return err
@@ -536,14 +558,12 @@ func DeleteBeans(e Engine, beans ...interface{}) (err error) {
 }
 
 // FIXME: need some kind of mechanism to record failure. HINT: system notice
-// DeleteUser completely and permanently deletes everything of a user,
-// but issues/comments/pulls will be kept and shown as someone has been deleted.
-func DeleteUser(u *User) error {
+func deleteUser(e *xorm.Session, u *User) error {
 	// Note: A user owns any repository or belongs to any organization
 	//	cannot perform delete operation.
 
 	// Check ownership of repository.
-	count, err := GetRepositoryCount(u)
+	count, err := getRepositoryCount(e, u)
 	if err != nil {
 		return fmt.Errorf("GetRepositoryCount: %v", err)
 	} else if count > 0 {
@@ -551,26 +571,20 @@ func DeleteUser(u *User) error {
 	}
 
 	// Check membership of organization.
-	count, err = u.GetOrganizationCount()
+	count, err = u.getOrganizationCount(e)
 	if err != nil {
 		return fmt.Errorf("GetOrganizationCount: %v", err)
 	} else if count > 0 {
 		return ErrUserHasOrgs{UID: u.Id}
 	}
 
-	sess := x.NewSession()
-	defer sessionRelease(sess)
-	if err = sess.Begin(); err != nil {
-		return err
-	}
-
 	// ***** START: Watch *****
 	watches := make([]*Watch, 0, 10)
-	if err = x.Find(&watches, &Watch{UserID: u.Id}); err != nil {
+	if err = e.Find(&watches, &Watch{UserID: u.Id}); err != nil {
 		return fmt.Errorf("get all watches: %v", err)
 	}
 	for i := range watches {
-		if _, err = sess.Exec("UPDATE `repository` SET num_watches=num_watches-1 WHERE id=?", watches[i].RepoID); err != nil {
+		if _, err = e.Exec("UPDATE `repository` SET num_watches=num_watches-1 WHERE id=?", watches[i].RepoID); err != nil {
 			return fmt.Errorf("decrease repository watch number[%d]: %v", watches[i].RepoID, err)
 		}
 	}
@@ -578,11 +592,11 @@ func DeleteUser(u *User) error {
 
 	// ***** START: Star *****
 	stars := make([]*Star, 0, 10)
-	if err = x.Find(&stars, &Star{UID: u.Id}); err != nil {
+	if err = e.Find(&stars, &Star{UID: u.Id}); err != nil {
 		return fmt.Errorf("get all stars: %v", err)
 	}
 	for i := range stars {
-		if _, err = sess.Exec("UPDATE `repository` SET num_stars=num_stars-1 WHERE id=?", stars[i].RepoID); err != nil {
+		if _, err = e.Exec("UPDATE `repository` SET num_stars=num_stars-1 WHERE id=?", stars[i].RepoID); err != nil {
 			return fmt.Errorf("decrease repository star number[%d]: %v", stars[i].RepoID, err)
 		}
 	}
@@ -590,17 +604,17 @@ func DeleteUser(u *User) error {
 
 	// ***** START: Follow *****
 	followers := make([]*Follow, 0, 10)
-	if err = x.Find(&followers, &Follow{UserID: u.Id}); err != nil {
+	if err = e.Find(&followers, &Follow{UserID: u.Id}); err != nil {
 		return fmt.Errorf("get all followers: %v", err)
 	}
 	for i := range followers {
-		if _, err = sess.Exec("UPDATE `user` SET num_followers=num_followers-1 WHERE id=?", followers[i].UserID); err != nil {
+		if _, err = e.Exec("UPDATE `user` SET num_followers=num_followers-1 WHERE id=?", followers[i].UserID); err != nil {
 			return fmt.Errorf("decrease user follower number[%d]: %v", followers[i].UserID, err)
 		}
 	}
 	// ***** END: Follow *****
 
-	if err = DeleteBeans(sess,
+	if err = deleteBeans(e,
 		&Oauth2{Uid: u.Id},
 		&AccessToken{UID: u.Id},
 		&Collaboration{UserID: u.Id},
@@ -612,34 +626,30 @@ func DeleteUser(u *User) error {
 		&IssueUser{UID: u.Id},
 		&EmailAddress{Uid: u.Id},
 	); err != nil {
-		return fmt.Errorf("DeleteBeans: %v", err)
+		return fmt.Errorf("deleteUser: %v", err)
 	}
 
 	// ***** START: PublicKey *****
 	keys := make([]*PublicKey, 0, 10)
-	if err = sess.Find(&keys, &PublicKey{OwnerID: u.Id}); err != nil {
+	if err = e.Find(&keys, &PublicKey{OwnerID: u.Id}); err != nil {
 		return fmt.Errorf("get all public keys: %v", err)
 	}
 	for _, key := range keys {
-		if err = deletePublicKey(sess, key.ID); err != nil {
+		if err = deletePublicKey(e, key.ID); err != nil {
 			return fmt.Errorf("deletePublicKey: %v", err)
 		}
 	}
 	// ***** END: PublicKey *****
 
 	// Clear assignee.
-	if _, err = sess.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.Id); err != nil {
+	if _, err = e.Exec("UPDATE `issue` SET assignee_id=0 WHERE assignee_id=?", u.Id); err != nil {
 		return fmt.Errorf("clear assignee: %v", err)
 	}
 
-	if _, err = sess.Id(u.Id).Delete(new(User)); err != nil {
+	if _, err = e.Id(u.Id).Delete(new(User)); err != nil {
 		return fmt.Errorf("Delete: %v", err)
 	}
 
-	if err = sess.Commit(); err != nil {
-		return fmt.Errorf("Commit: %v", err)
-	}
-
 	// FIXME: system notice
 	// Note: There are something just cannot be roll back,
 	//	so just keep error logs of those operations.
@@ -651,6 +661,22 @@ func DeleteUser(u *User) error {
 	return nil
 }
 
+// DeleteUser completely and permanently deletes everything of a user,
+// but issues/comments/pulls will be kept and shown as someone has been deleted.
+func DeleteUser(u *User) (err error) {
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
+		return err
+	}
+
+	if err = deleteUser(sess, u); err != nil {
+		return fmt.Errorf("deleteUser: %v", err)
+	}
+
+	return sess.Commit()
+}
+
 // DeleteInactivateUsers deletes all inactivate users and email addresses.
 func DeleteInactivateUsers() (err error) {
 	users := make([]*User, 0, 10)

+ 6 - 9
modules/auth/org.go

@@ -17,8 +17,7 @@ import (
 //         \/     /_____/     \/     \/         \/     \/                    \/
 
 type CreateOrgForm struct {
-	OrgName string `form:"org_name" binding:"Required;AlphaDashDot;MaxSize(30)"`
-	Email   string `form:"email" binding:"Required;Email;MaxSize(50)"`
+	OrgName string `binding:"Required;AlphaDashDot;MaxSize(30)" locale:"org.org_name_holder"`
 }
 
 func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
@@ -26,13 +25,11 @@ func (f *CreateOrgForm) Validate(ctx *macaron.Context, errs binding.Errors) bind
 }
 
 type UpdateOrgSettingForm struct {
-	OrgUserName string `form:"uname" binding:"Required;AlphaDashDot;MaxSize(30)" locale:"org.org_name_holder"`
-	OrgFullName string `form:"fullname" binding:"MaxSize(100)"`
-	Email       string `form:"email" binding:"Required;Email;MaxSize(50)"`
-	Description string `form:"desc" binding:"MaxSize(255)"`
-	Website     string `form:"website" binding:"Url;MaxSize(100)"`
-	Location    string `form:"location" binding:"MaxSize(50)"`
-	Avatar      string `form:"avatar" binding:"Required;Email;MaxSize(50)"`
+	Name        string `binding:"Required;AlphaDashDot;MaxSize(30)" locale:"org.org_name_holder"`
+	FullName    string `binding:"MaxSize(100)"`
+	Description string `binding:"MaxSize(255)"`
+	Website     string `binding:"Url;MaxSize(100)"`
+	Location    string `binding:"MaxSize(50)"`
 }
 
 func (f *UpdateOrgSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

+ 8 - 8
modules/auth/user_form.go

@@ -88,12 +88,12 @@ func (f *SignInForm) Validate(ctx *macaron.Context, errs binding.Errors) binding
 //         \/         \/                                   \/        \/        \/
 
 type UpdateProfileForm struct {
-	UserName string `form:"uname" binding:"Required;MaxSize(35)"`
-	FullName string `form:"fullname" binding:"MaxSize(100)"`
-	Email    string `form:"email" binding:"Required;Email;MaxSize(254)"`
-	Website  string `form:"website" binding:"Url;MaxSize(100)"`
-	Location string `form:"location" binding:"MaxSize(50)"`
-	Avatar   string `form:"avatar" binding:"Required;Email;MaxSize(254)"`
+	Name     string `binding:"Required;MaxSize(35)"`
+	FullName string `binding:"MaxSize(100)"`
+	Email    string `binding:"Required;Email;MaxSize(254)"`
+	Website  string `binding:"Url;MaxSize(100)"`
+	Location string `binding:"MaxSize(50)"`
+	Gravatar string `binding:"Required;Email;MaxSize(254)"`
 }
 
 func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
@@ -101,8 +101,8 @@ func (f *UpdateProfileForm) Validate(ctx *macaron.Context, errs binding.Errors)
 }
 
 type UploadAvatarForm struct {
-	Enable bool                  `form:"enable"`
-	Avatar *multipart.FileHeader `form:"avatar"`
+	Enable bool
+	Avatar *multipart.FileHeader
 }
 
 func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

+ 1 - 2
modules/base/template.go

@@ -76,9 +76,8 @@ func ToUtf8WithErr(content []byte) (error, string) {
 	}
 
 	encoding, _ := charset.Lookup(charsetLabel)
-
 	if encoding == nil {
-		return fmt.Errorf("unknow char decoder %s", charsetLabel), string(content)
+		return fmt.Errorf("unknown char decoder %s", charsetLabel), string(content)
 	}
 
 	result, n, err := transform.String(encoding.NewDecoder(), string(content))

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
modules/bindata/bindata.go


+ 4 - 2
modules/mailer/mailer.go

@@ -48,6 +48,7 @@ type Message struct {
 	To      []string
 	From    string
 	Subject string
+	ReplyTo string
 	Body    string
 	Type    string
 	Massive bool
@@ -63,7 +64,7 @@ func (m Message) Content() string {
 	}
 
 	// create mail content
-	content := "From: " + m.From + "\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
+	content := "From: " + m.From + "\r\nReply-To: " + m.ReplyTo + "\r\nSubject: " + m.Subject + "\r\nContent-Type: " + contentType + "\r\n\r\n" + m.Body
 	return content
 }
 
@@ -252,7 +253,8 @@ func SendAsync(msg *Message) {
 func NewHtmlMessage(To []string, From, Subject, Body string) Message {
 	return Message{
 		To:      To,
-		From:    From,
+		From:    setting.MailService.From,
+		ReplyTo: From,
 		Subject: Subject,
 		Body:    Body,
 		Type:    "html",

+ 5 - 0
modules/setting/setting.go

@@ -201,6 +201,11 @@ func ExecPath() (string, error) {
 
 // WorkDir returns absolute path of work directory.
 func WorkDir() (string, error) {
+	wd := os.Getenv("GOGS_WORK_DIR")
+	if len(wd) > 0 {
+		return wd, nil
+	}
+
 	execPath, err := ExecPath()
 	if err != nil {
 		return execPath, err

+ 92 - 73
public/config.codekit

@@ -1,6 +1,6 @@
 {
 "CodeKitInfo": "This is a CodeKit 2.x project configuration file. It is designed to sync project settings across multiple machines. MODIFYING THE CONTENTS OF THIS FILE IS A POOR LIFE DECISION. If you do so, you will likely cause CodeKit to crash. This file is not useful unless accompanied by the project that created it in CodeKit 2. This file is not backwards-compatible with CodeKit 1.x. For more information, see: http:\/\/incident57.com\/codekit",
-"creatorBuild": "18493",
+"creatorBuild": "19051",
 "files": {
 	"\/css\/dropzone-4.0.1.css": {
 		"fileType": 16,
@@ -83,6 +83,17 @@
 		"outputPathIsOutsideProject": 0,
 		"outputPathIsSetByUser": 0
 		},
+	"\/css\/themes\/default\/assets\/images\/flags.png": {
+		"fileType": 32768,
+		"ignore": 0,
+		"ignoreWasSetByUser": 0,
+		"initialSize": 28123,
+		"inputAbbreviatedPath": "\/css\/themes\/default\/assets\/images\/flags.png",
+		"outputAbbreviatedPath": "\/css\/themes\/default\/assets\/images\/flags.png",
+		"outputPathIsOutsideProject": 0,
+		"outputPathIsSetByUser": 0,
+		"processed": 0
+		},
 	"\/img\/404.png": {
 		"fileType": 32768,
 		"ignore": 0,
@@ -169,7 +180,7 @@
 		"outputPathIsOutsideProject": 0,
 		"outputPathIsSetByUser": 0,
 		"outputStyle": 1,
-		"syntaxCheckerStyle": 1
+		"syntaxCheckerStyle": 0
 		},
 	"\/js\/jquery-1.11.3.min.js": {
 		"fileType": 64,
@@ -193,6 +204,17 @@
 		"outputStyle": 1,
 		"syntaxCheckerStyle": 1
 		},
+	"\/js\/libs\/emojify-1.1.0.min.js": {
+		"fileType": 64,
+		"ignore": 0,
+		"ignoreWasSetByUser": 0,
+		"inputAbbreviatedPath": "\/js\/libs\/emojify-1.1.0.min.js",
+		"outputAbbreviatedPath": "\/js\/libs\/min\/emojify-1.1.0.min-min.js",
+		"outputPathIsOutsideProject": 0,
+		"outputPathIsSetByUser": 0,
+		"outputStyle": 1,
+		"syntaxCheckerStyle": 1
+		},
 	"\/js\/libs\/highlight-8.7.pack.js": {
 		"fileType": 64,
 		"ignore": 0,
@@ -308,6 +330,26 @@
 		"strictMath": 0,
 		"strictUnits": 0
 		},
+	"\/less\/_emojify.less": {
+		"allowInsecureImports": 0,
+		"createSourceMap": 0,
+		"disableJavascript": 0,
+		"fileType": 1,
+		"ieCompatibility": 1,
+		"ignore": 1,
+		"ignoreWasSetByUser": 0,
+		"inputAbbreviatedPath": "\/less\/_emojify.less",
+		"outputAbbreviatedPath": "\/css\/_emojify.css",
+		"outputPathIsOutsideProject": 0,
+		"outputPathIsSetByUser": 0,
+		"outputStyle": 0,
+		"relativeURLS": 0,
+		"shouldRunAutoprefixer": 0,
+		"shouldRunBless": 0,
+		"strictImports": 0,
+		"strictMath": 0,
+		"strictUnits": 0
+		},
 	"\/less\/_explore.less": {
 		"allowInsecureImports": 0,
 		"createSourceMap": 0,
@@ -500,7 +542,7 @@
 		"outputAbbreviatedPath": "\/css\/gogs.min.css",
 		"outputPathIsOutsideProject": 0,
 		"outputPathIsSetByUser": 1,
-		"outputStyle": 2,
+		"outputStyle": 1,
 		"relativeURLS": 0,
 		"shouldRunAutoprefixer": 0,
 		"shouldRunBless": 0,
@@ -564,8 +606,8 @@
 		},
 	"\/ng\/js\/gogs.js": {
 		"fileType": 64,
-		"ignore": 0,
-		"ignoreWasSetByUser": 0,
+		"ignore": 1,
+		"ignoreWasSetByUser": 1,
 		"inputAbbreviatedPath": "\/ng\/js\/gogs.js",
 		"outputAbbreviatedPath": "\/ng\/js\/min\/gogs-min.js",
 		"outputPathIsOutsideProject": 0,
@@ -1415,6 +1457,10 @@
 			"active": 0,
 			"flagValue": -1
 			},
+		"futurehostile": {
+			"active": 0,
+			"flagValue": -1
+			},
 		"globalstrict": {
 			"active": 0,
 			"flagValue": -1
@@ -1503,6 +1549,10 @@
 			"active": 1,
 			"flagValue": -1
 			},
+		"nocomma": {
+			"active": 0,
+			"flagValue": -1
+			},
 		"node": {
 			"active": 0,
 			"flagValue": -1
@@ -1603,6 +1653,10 @@
 			"active": 1,
 			"flagValue": -1
 			},
+		"varstmt": {
+			"active": 0,
+			"flagValue": -1
+			},
 		"withstmt": {
 			"active": 0,
 			"flagValue": -1
@@ -1621,10 +1675,6 @@
 			}
 		},
 	"jsLintFlags2": {
-		"ass": {
-			"active": 0,
-			"flagValue": -1
-			},
 		"bitwise": {
 			"active": 0,
 			"flagValue": -1
@@ -1633,15 +1683,7 @@
 			"active": 1,
 			"flagValue": -1
 			},
-		"closure": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"continue": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"debug": {
+		"couch": {
 			"active": 0,
 			"flagValue": -1
 			},
@@ -1649,75 +1691,27 @@
 			"active": 0,
 			"flagValue": -1
 			},
-		"eqeq": {
+		"es6": {
 			"active": 0,
 			"flagValue": -1
 			},
-		"evil": {
+		"eval": {
 			"active": 0,
 			"flagValue": -1
 			},
-		"forin": {
+		"for": {
 			"active": 0,
 			"flagValue": -1
 			},
-		"indent": {
-			"active": 0,
-			"flagValue": 4
-			},
 		"maxlen": {
 			"active": 0,
 			"flagValue": 150
 			},
-		"newcap": {
-			"active": 0,
-			"flagValue": -1
-			},
 		"node": {
 			"active": 0,
 			"flagValue": -1
 			},
-		"nomen": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"plusplus": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"properties": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"regexp": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"rhino": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"sloppy": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"stupid": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"sub": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"todo": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"unparam": {
-			"active": 0,
-			"flagValue": -1
-			},
-		"vars": {
+		"this": {
 			"active": 0,
 			"flagValue": -1
 			},
@@ -1726,6 +1720,14 @@
 			"flagValue": -1
 			}
 		},
+	"jsonAutoOutputPathEnabled": 0,
+	"jsonAutoOutputPathFilenamePattern": "*-min.json",
+	"jsonAutoOutputPathRelativePath": "",
+	"jsonAutoOutputPathReplace1": "",
+	"jsonAutoOutputPathReplace2": "",
+	"jsonAutoOutputPathStyle": 0,
+	"jsonOrderOutput": 0,
+	"jsonOutputStyle": 1,
 	"kitAutoOutputPathEnabled": 1,
 	"kitAutoOutputPathFilenamePattern": "*.html",
 	"kitAutoOutputPathRelativePath": "",
@@ -1753,9 +1755,18 @@
 	"markdownAutoOutputPathReplace1": "",
 	"markdownAutoOutputPathReplace2": "",
 	"markdownAutoOutputPathStyle": 0,
+	"markdownCriticStyle": 0,
 	"markdownEnableFootnotes": 0,
-	"markdownEnableSmartyPants": 1,
-	"markdownExpandTabs": 1,
+	"markdownEnableLabels": 1,
+	"markdownEnableSmartQuotes": 1,
+	"markdownEscapeLineBreaks": 0,
+	"markdownMaskEmailAddresses": 1,
+	"markdownOutputFormat": 0,
+	"markdownOutputStyle": 0,
+	"markdownParseMetadata": 1,
+	"markdownProcessHTML": 0,
+	"markdownRandomFootnoteNumbers": 0,
+	"markdownUseCompatibilityMode": 0,
 	"reloadFileURLs": 0,
 	"sassAutoOutputPathEnabled": 1,
 	"sassAutoOutputPathFilenamePattern": "*.css",
@@ -1770,7 +1781,7 @@
 	"sassUseLibsass": 0,
 	"shouldRunAutoprefixer": 0,
 	"shouldRunBless": 0,
-	"skippedItemsString": ".svn, .git, .hg, log, _logs, _cache, cache, logs, node_modules",
+	"skippedItemsString": "_cache, logs, _logs, cache, \/img\/emoji, .git, log, node_modules, .svn, .hg",
 	"slimAutoOutputPathEnabled": 1,
 	"slimAutoOutputPathFilenamePattern": "*.html",
 	"slimAutoOutputPathRelativePath": "",
@@ -1814,6 +1825,10 @@
 			"active": 0,
 			"flagValue": -1
 			},
+		"bare-returns": {
+			"active": 0,
+			"flagValue": -1
+			},
 		"booleans": {
 			"active": 1,
 			"flagValue": -1
@@ -1894,6 +1909,10 @@
 			"active": 0,
 			"flagValue": -1
 			},
+		"keep_fnames": {
+			"active": 0,
+			"flagValue": -1
+			},
 		"loops": {
 			"active": 1,
 			"flagValue": -1

Datei-Diff unterdrückt, da er zu groß ist
+ 0 - 0
public/css/gogs.min.css


BIN
public/img/emoji/+1.png


BIN
public/img/emoji/-1.png


BIN
public/img/emoji/100.png


BIN
public/img/emoji/1234.png


BIN
public/img/emoji/8ball.png


BIN
public/img/emoji/a.png


BIN
public/img/emoji/ab.png


BIN
public/img/emoji/abc.png


BIN
public/img/emoji/abcd.png


BIN
public/img/emoji/accept.png


BIN
public/img/emoji/aerial_tramway.png


BIN
public/img/emoji/airplane.png


BIN
public/img/emoji/alarm_clock.png


BIN
public/img/emoji/alien.png


BIN
public/img/emoji/ambulance.png


BIN
public/img/emoji/anchor.png


BIN
public/img/emoji/angel.png


BIN
public/img/emoji/anger.png


BIN
public/img/emoji/angry.png


BIN
public/img/emoji/anguished.png


BIN
public/img/emoji/ant.png


BIN
public/img/emoji/apple.png


BIN
public/img/emoji/aquarius.png


BIN
public/img/emoji/aries.png


BIN
public/img/emoji/arrow_backward.png


BIN
public/img/emoji/arrow_double_down.png


BIN
public/img/emoji/arrow_double_up.png


BIN
public/img/emoji/arrow_down.png


BIN
public/img/emoji/arrow_down_small.png


BIN
public/img/emoji/arrow_forward.png


BIN
public/img/emoji/arrow_heading_down.png


BIN
public/img/emoji/arrow_heading_up.png


BIN
public/img/emoji/arrow_left.png


BIN
public/img/emoji/arrow_lower_left.png


BIN
public/img/emoji/arrow_lower_right.png


BIN
public/img/emoji/arrow_right.png


BIN
public/img/emoji/arrow_right_hook.png


BIN
public/img/emoji/arrow_up.png


BIN
public/img/emoji/arrow_up_down.png


BIN
public/img/emoji/arrow_up_small.png


BIN
public/img/emoji/arrow_upper_left.png


BIN
public/img/emoji/arrow_upper_right.png


BIN
public/img/emoji/arrows_clockwise.png


BIN
public/img/emoji/arrows_counterclockwise.png


BIN
public/img/emoji/art.png


BIN
public/img/emoji/articulated_lorry.png


BIN
public/img/emoji/astonished.png


BIN
public/img/emoji/atm.png


BIN
public/img/emoji/b.png


BIN
public/img/emoji/baby.png


BIN
public/img/emoji/baby_bottle.png


BIN
public/img/emoji/baby_chick.png


BIN
public/img/emoji/baby_symbol.png


BIN
public/img/emoji/back.png


BIN
public/img/emoji/baggage_claim.png


BIN
public/img/emoji/balloon.png


BIN
public/img/emoji/ballot_box_with_check.png


BIN
public/img/emoji/bamboo.png


BIN
public/img/emoji/banana.png


BIN
public/img/emoji/bangbang.png


BIN
public/img/emoji/bank.png


BIN
public/img/emoji/bar_chart.png


BIN
public/img/emoji/barber.png


BIN
public/img/emoji/baseball.png


BIN
public/img/emoji/basketball.png


BIN
public/img/emoji/bath.png


BIN
public/img/emoji/bathtub.png


BIN
public/img/emoji/battery.png


BIN
public/img/emoji/bear.png


BIN
public/img/emoji/bee.png


BIN
public/img/emoji/beer.png


BIN
public/img/emoji/beers.png


BIN
public/img/emoji/beetle.png


BIN
public/img/emoji/beginner.png


BIN
public/img/emoji/bell.png


Einige Dateien werden nicht angezeigt, da zu viele Dateien in diesem Diff geändert wurden.