Browse Source

Merge branch 'access' into access_rewriteserv

Peter Smit 10 years ago
parent
commit
556881964f
76 changed files with 902 additions and 762 deletions
  1. 20 21
      .gopmfile
  2. 2 1
      .travis.yml
  3. 10 10
      CONTRIBUTING.md
  4. 15 15
      README.md
  5. 5 6
      cmd/web.go
  6. 1 0
      conf/locale/locale_de-DE.ini
  7. 2 2
      conf/locale/locale_en-US.ini
  8. 1 0
      conf/locale/locale_es-ES.ini
  9. 8 7
      conf/locale/locale_fr-CA.ini
  10. 1 0
      conf/locale/locale_ja-JP.ini
  11. 1 0
      conf/locale/locale_lv-LV.ini
  12. 3 2
      conf/locale/locale_nl-NL.ini
  13. 10 9
      conf/locale/locale_ru-RU.ini
  14. 1 0
      conf/locale/locale_zh-CN.ini
  15. 1 0
      conf/locale/locale_zh-HK.ini
  16. 1 1
      docker/blocks/docker_gogs/Dockerfile
  17. 1 1
      docker/blocks/docker_gogs_dev/Dockerfile
  18. 1 1
      docker/templates/init_gogs.sh.tpl
  19. 1 1
      gogs.go
  20. 23 14
      models/access.go
  21. 10 10
      models/action.go
  22. 1 1
      models/issue.go
  23. 64 12
      models/migrations/migrations.go
  24. 3 2
      models/models.go
  25. 182 116
      models/org.go
  26. 70 74
      models/repo.go
  27. 14 9
      models/user.go
  28. 1 2
      modules/auth/repo_form.go
  29. 1 1
      modules/auth/user_form.go
  30. 12 2
      modules/base/template.go
  31. 6 61
      modules/base/tool.go
  32. 20 9
      modules/git/signature.go
  33. 10 0
      modules/mailer/mailer.go
  34. 44 23
      modules/middleware/context.go
  35. 18 32
      modules/middleware/repo.go
  36. 8 2
      modules/setting/setting.go
  37. 1 3
      public/ng/css/gogs.css
  38. 19 18
      public/ng/js/gogs.js
  39. 1 3
      public/ng/less/gogs/repository.less
  40. 38 37
      routers/api/v1/repo.go
  41. 1 1
      routers/api/v1/repo_file.go
  42. 6 0
      routers/install.go
  43. 10 5
      routers/org/setting.go
  44. 1 1
      routers/org/teams.go
  45. 5 0
      routers/repo/http.go
  46. 7 7
      routers/repo/issue.go
  47. 5 5
      routers/repo/release.go
  48. 18 12
      routers/repo/repo.go
  49. 1 2
      routers/repo/setting.go
  50. 1 4
      routers/user/auth.go
  51. 44 47
      routers/user/setting.go
  52. 5 0
      scripts/less.sh
  53. 1 1
      templates/.VERSION
  54. 2 2
      templates/admin/auth/list.tmpl
  55. 1 1
      templates/admin/org/list.tmpl
  56. 1 1
      templates/admin/repo/list.tmpl
  57. 1 1
      templates/admin/user/list.tmpl
  58. 1 1
      templates/base/head.tmpl
  59. 1 1
      templates/ng/base/head.tmpl
  60. 1 1
      templates/org/base/header.tmpl
  61. 70 70
      templates/org/home.tmpl
  62. 10 10
      templates/org/settings/nav.tmpl
  63. 57 57
      templates/org/settings/options.tmpl
  64. 2 2
      templates/repo/bare.tmpl
  65. 5 5
      templates/repo/diff.tmpl
  66. 1 1
      templates/repo/header.tmpl
  67. 2 2
      templates/repo/migrate.tmpl
  68. 0 5
      templates/repo/settings/options.tmpl
  69. 1 1
      templates/repo/sidebar.tmpl
  70. 1 1
      templates/repo/toolbar.tmpl
  71. 2 0
      templates/user/auth/signin.tmpl
  72. 1 1
      templates/user/profile.tmpl
  73. 1 1
      templates/user/settings/applications.tmpl
  74. 3 3
      templates/user/settings/email.tmpl
  75. 1 1
      templates/user/settings/social.tmpl
  76. 1 1
      templates/user/settings/sshkeys.tmpl

+ 20 - 21
.gopmfile

@@ -2,35 +2,34 @@
 path = github.com/gogits/gogs
 path = github.com/gogits/gogs
 
 
 [deps]
 [deps]
-github.com/beego/memcache = commit:2aea774416
-github.com/bradfitz/gomemcache = 
+github.com/bradfitz/gomemcache = commit:72a68649ba
 github.com/Unknwon/cae = commit:2e70a1351b
 github.com/Unknwon/cae = commit:2e70a1351b
-github.com/Unknwon/com = commit:d9bcf409c8
+github.com/Unknwon/com = commit:188d690b1a
 github.com/Unknwon/i18n = commit:1e88666229
 github.com/Unknwon/i18n = commit:1e88666229
-github.com/Unknwon/macaron = 
-github.com/codegangsta/cli = commit:a14c5b47c7
-github.com/go-sql-driver/mysql = commit:04cf947760
-github.com/go-xorm/core = commit:e7882d8b00
-github.com/go-xorm/xorm = commit:dcc529b68a
+github.com/Unknwon/macaron = commit:e089393c3f
+github.com/codegangsta/cli = commit:6086d7927e
+github.com/go-sql-driver/mysql = commit:27633f0519
+github.com/go-xorm/core = commit:16cb27928f
+github.com/go-xorm/xorm = commit:f2d3be988e
 github.com/gogits/chardet = commit:2404f77725
 github.com/gogits/chardet = commit:2404f77725
 github.com/gogits/go-gogs-client = commit:92e76d616a
 github.com/gogits/go-gogs-client = commit:92e76d616a
-github.com/lib/pq = commit:3e3efe51a0
-github.com/macaron-contrib/binding = commit:0fbe4b9707
-github.com/macaron-contrib/cache = 
-github.com/macaron-contrib/captcha = commit:3567dc48b8
-github.com/macaron-contrib/csrf = commit:3ea14e7ee7
-github.com/macaron-contrib/i18n = commit:0ee0539c84
+github.com/lib/pq = commit:835d5eb08d
+github.com/macaron-contrib/binding = commit:dc739fabc3
+github.com/macaron-contrib/cache = commit:b68f6b448f
+github.com/macaron-contrib/captcha = commit:066c50c7eb
+github.com/macaron-contrib/csrf = commit:98ddf5a710
+github.com/macaron-contrib/i18n = commit:eeebd44f64
 github.com/macaron-contrib/oauth2 = commit:8f394c3629
 github.com/macaron-contrib/oauth2 = commit:8f394c3629
-github.com/macaron-contrib/session = 
-github.com/macaron-contrib/toolbox = commit:57127bcc89
-github.com/mattn/go-sqlite3 = commit:a80c27ba33
-github.com/microcosm-cc/bluemonday = 
+github.com/macaron-contrib/session = commit:8e8d938b27
+github.com/macaron-contrib/toolbox = commit:acbfe36e16
+github.com/mattn/go-sqlite3 = commit:25d045f12a
+github.com/microcosm-cc/bluemonday = commit:fcd0f5074e
 github.com/nfnt/resize = commit:8f44931448
 github.com/nfnt/resize = commit:8f44931448
-github.com/russross/blackfriday = commit:05b8cefd6a
-github.com/shurcooL/go = commit:48293cbc7a
+github.com/russross/blackfriday = commit:77efab57b2
+github.com/shurcooL/go = commit:329f57438c
 golang.org/x/net = 
 golang.org/x/net = 
 golang.org/x/text = 
 golang.org/x/text = 
-gopkg.in/ini.v1 = commit:28ad8c408b
+gopkg.in/ini.v1 = commit:4febc4104c
 gopkg.in/redis.v2 = commit:e617904962
 gopkg.in/redis.v2 = commit:e617904962
 
 
 [res]
 [res]

+ 2 - 1
.travis.yml

@@ -12,4 +12,5 @@ script: go build -v
 
 
 notifications:
 notifications:
   email:
   email:
-    - [email protected]
+    - [email protected]
+  slack: gophercn:o5pSanyTeNhnfYc3QnG0X7Wx

+ 10 - 10
CONTRIBUTING.md

@@ -2,20 +2,20 @@
 
 
 > This guidelines sheet is forked from [CONTRIBUTING.md](https://github.com/drone/drone/blob/master/CONTRIBUTING.md).
 > This guidelines sheet is forked from [CONTRIBUTING.md](https://github.com/drone/drone/blob/master/CONTRIBUTING.md).
 
 
-Gogs is not perfect and it has bugs, or incomplete features for rare cases. You're welcome to tell us or contribute some code. This document describes details about how can you contribute to Gogs project.
+Gogs is not perfect, and it has bugs or incomplete features in rare cases. You're welcome to tell us, or to contribute some code. This document describes details about how can you contribute to Gogs project.
 
 
 ## Contribution guidelines
 ## Contribution guidelines
 
 
 Depends on the situation, you will:
 Depends on the situation, you will:
 
 
-- Find bug, create an issue
-- Need more functionality, make a feature request
-- Want to contribute code, open a pull request
-- Run into issue, need help
+- Find a bug and create an issue
+- Need more functionality and make a feature request
+- Want to contribute code and open a pull request
+- Run into issue and need help
 
 
 ### Bug Report
 ### Bug Report
 
 
-If you find or consider something is a bug, please create a issue on [GitHub](https://github.com/gogits/gogs/issues). To reduce unnecessary time wasting of interacting and waiting with team members, please include following information in the first place with a comfortable form for you:
+If you find something you consider a bug, please create a issue on [GitHub](https://github.com/gogits/gogs/issues). To avoid wasting time and reduce back-and-forth communication with team members, please include at least the following information in a form comfortable for you:
 
 
 - Bug Description
 - Bug Description
 - Gogs Version
 - Gogs Version
@@ -28,7 +28,7 @@ Please take a moment to check that an issue on [GitHub](https://github.com/gogit
 
 
 #### Bug Report Example
 #### Bug Report Example
 
 
-Gogs crashed when create repository with license, using v0.5.13.0207, SQLite3, Git 1.9.0, Ubuntu 12.04.
+Gogs crashed when creating a repository with a license, using v0.5.13.0207, SQLite3, Git 1.9.0, Ubuntu 12.04.
 
 
 Error log:
 Error log:
 
 
@@ -38,11 +38,11 @@ Error log:
 
 
 ### Feature Request
 ### Feature Request
 
 
-There is no standard form of making a feature request, just try to describe the feature as clear as possible because team members may not have experience with the functionality you're talking about.
+There is no standard form of making a feature request. Just try to describe the feature as clearly as possible, because team members may not have experience with the functionality you're talking about.
 
 
 ### Pull Request
 ### Pull Request
 
 
-Pull requests are always welcome, but note that **ALL PULL REQUESTS MUST SEND TO `DEV` BRANCH**.
+Pull requests are always welcome, but note that **ALL PULL REQUESTS MUST APPLY TO THE `DEV` BRANCH**.
 
 
 We are always thrilled to receive pull requests, and do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it.
 We are always thrilled to receive pull requests, and do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it.
 
 
@@ -52,7 +52,7 @@ We're trying very hard to keep Gogs lean and focused. We don't want it to do eve
 
 
 ### Ask For Help
 ### Ask For Help
 
 
-Before open any new issue, please check your problem on [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages.
+Before opening an issue, please make sure your problem isn't already addressed on the [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md) and [FAQs](http://gogs.io/docs/intro/faqs.html) pages.
 
 
 ## Things To Notice
 ## Things To Notice
 
 

+ 15 - 15
README.md

@@ -3,7 +3,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
 
 
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/gogits/gogs?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 
 
-Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
+Gogs (Go Git Service) is a painless self-hosted Git service written in Go.
 
 
 ![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
 ![Demo](http://gogs.qiniudn.com/gogs_demo.gif)
 
 
@@ -12,9 +12,9 @@ Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
 ### NOTICES
 ### NOTICES
 
 
 - Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
 - Due to testing purpose, data of [try.gogs.io](https://try.gogs.io) has been reset in **Jan 28, 2015** and will reset multiple times after. Please do **NOT** put your important data on the site.
-- Demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
+- The demo site [try.gogs.io](https://try.gogs.io) is running under `dev` branch.
 - You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing a issue or making a Pull Request.
 - You **MUST** read [CONTRIBUTING.md](CONTRIBUTING.md) before you start filing a issue or making a Pull Request.
-- If you think there are vulnerabilities in the project, please talk private to **[email protected]**, thanks!
+- If you think there are vulnerabilities in the project, please talk privately to **[email protected]**.  Thanks!
 
 
 #### Other language version
 #### Other language version
 
 
@@ -22,15 +22,15 @@ Gogs(Go Git Service) is a painless self-hosted Git Service written in Go.
 
 
 ## Purpose
 ## Purpose
 
 
-The goal of this project is to make the easiest, fastest and most painless way to set up a self-hosted Git service. With Go, this can be done in independent binary distribution across **ALL platforms** that Go supports, including Linux, Mac OS X, and Windows.
+The goal of this project is to make the easiest, fastest, and most painless way to set up a self-hosted Git service. With Go, this can be done via an independent binary distribution across **ALL platforms** that Go supports, including Linux, Mac OS X, and Windows.
 
 
 ## Overview
 ## Overview
 
 
-- Please see [Documentation](http://gogs.io/docs/intro/) for project design, known issues, and change log.
-- See [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
-- Try it before anything? Do it [online](https://try.gogs.io/unknwon/gogs) or go down to **Installation -> Install from binary** section!
-- Having troubles? Get help from [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md).
-- Want to help on localization? Check out [Crowdin](https://crowdin.com/project/gogs)!
+- Please see the [Documentation](http://gogs.io/docs/intro/) for project design, known issues, and change log.
+- See the [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) to follow the develop team.
+- Want to try it before doing anything else? Do it [online](https://try.gogs.io/unknwon/gogs) or go down to the **Installation -> Install from binary** section!
+- Having trouble? Get help with [Troubleshooting](http://gogs.io/docs/intro/troubleshooting.md).
+- Want to help with localization? Check out [Crowdin](https://crowdin.com/project/gogs)!
 
 
 ## Features
 ## Features
 
 
@@ -46,22 +46,22 @@ The goal of this project is to make the easiest, fastest and most painless way t
 - Repository Git hooks
 - Repository Git hooks
 - Add/remove repository collaborators
 - Add/remove repository collaborators
 - Gravatar and cache support
 - Gravatar and cache support
-- Mail service(register, issue)
+- Mail service (register, issue)
 - Administration panel
 - Administration panel
 - Slack webhook integration
 - Slack webhook integration
 - Drone CI integration
 - Drone CI integration
 - Supports MySQL, PostgreSQL and SQLite3
 - Supports MySQL, PostgreSQL and SQLite3
-- Social account login(GitHub, Google, QQ, Weibo)
-- Multi-language support([10 languages](https://crowdin.com/project/gogs))
+- Social account login (GitHub, Google, QQ, Weibo)
+- Multi-language support ([10 languages](https://crowdin.com/project/gogs))
 
 
 ## System Requirements
 ## System Requirements
 
 
-- A cheap Raspberry Pi is powerful enough to match the minimal requirement.
-- 4 CPU Cores and 1GB RAM would be the baseline for teamwork.
+- A cheap Raspberry Pi is powerful enough for basic functionality.
+- At least 4 CPU cores and 1GB RAM would be the baseline for teamwork.
 
 
 ## Installation
 ## Installation
 
 
-Make sure you install [Prerequirements](http://gogs.io/docs/installation/) first.
+Make sure you install the [prerequisites](http://gogs.io/docs/installation/) first.
 
 
 There are 5 ways to install Gogs:
 There are 5 ways to install Gogs:
 
 

+ 5 - 6
cmd/web.go

@@ -79,7 +79,7 @@ func checkVersion() {
 	// Check dependency version.
 	// Check dependency version.
 	checkers := []VerChecker{
 	checkers := []VerChecker{
 		{"github.com/Unknwon/macaron", macaron.Version, "0.5.1"},
 		{"github.com/Unknwon/macaron", macaron.Version, "0.5.1"},
-		{"github.com/macaron-contrib/binding", binding.Version, "0.0.4"},
+		{"github.com/macaron-contrib/binding", binding.Version, "0.0.5"},
 		{"github.com/macaron-contrib/cache", cache.Version, "0.0.7"},
 		{"github.com/macaron-contrib/cache", cache.Version, "0.0.7"},
 		{"github.com/macaron-contrib/csrf", csrf.Version, "0.0.3"},
 		{"github.com/macaron-contrib/csrf", csrf.Version, "0.0.3"},
 		{"github.com/macaron-contrib/i18n", i18n.Version, "0.0.5"},
 		{"github.com/macaron-contrib/i18n", i18n.Version, "0.0.5"},
@@ -166,12 +166,11 @@ func newMacaron() *macaron.Macaron {
 }
 }
 
 
 func runWeb(ctx *cli.Context) {
 func runWeb(ctx *cli.Context) {
-	checkVersion()
-
 	if ctx.IsSet("config") {
 	if ctx.IsSet("config") {
 		setting.CustomConf = ctx.String("config")
 		setting.CustomConf = ctx.String("config")
 	}
 	}
 	routers.GlobalInit()
 	routers.GlobalInit()
+	checkVersion()
 
 
 	m := newMacaron()
 	m := newMacaron()
 
 
@@ -230,7 +229,7 @@ func runWeb(ctx *cli.Context) {
 			})
 			})
 
 
 			m.Any("/*", func(ctx *middleware.Context) {
 			m.Any("/*", func(ctx *middleware.Context) {
-				ctx.JSON(404, &base.ApiJsonErr{"Not Found", base.DOC_URL})
+				ctx.HandleAPI(404, "Page not found")
 			})
 			})
 		})
 		})
 	})
 	})
@@ -319,7 +318,7 @@ func runWeb(ctx *cli.Context) {
 		m.Get("/template/*", dev.TemplatePreview)
 		m.Get("/template/*", dev.TemplatePreview)
 	}
 	}
 
 
-	reqTrueOwner := middleware.RequireTrueOwner()
+	reqAdmin := middleware.RequireAdmin()
 
 
 	// Organization.
 	// Organization.
 	m.Group("/org", func() {
 	m.Group("/org", func() {
@@ -394,7 +393,7 @@ func runWeb(ctx *cli.Context) {
 				m.Post("/:name", repo.GitHooksEditPost)
 				m.Post("/:name", repo.GitHooksEditPost)
 			}, middleware.GitHookService())
 			}, middleware.GitHookService())
 		})
 		})
-	}, reqSignIn, middleware.RepoAssignment(true), reqTrueOwner)
+	}, reqSignIn, middleware.RepoAssignment(true), reqAdmin)
 
 
 	m.Group("/:username/:reponame", func() {
 	m.Group("/:username/:reponame", func() {
 		m.Get("/action/:action", repo.Action)
 		m.Get("/action/:action", repo.Action)

+ 1 - 0
conf/locale/locale_de-DE.ini

@@ -647,6 +647,7 @@ config.reset_password_code_lives=Passwortcode Lebensdauer
 config.webhook_config=Webhook-Einstellungen
 config.webhook_config=Webhook-Einstellungen
 config.task_interval=Task-Intervall
 config.task_interval=Task-Intervall
 config.deliver_timeout=Zeitlimit für Zustellung
 config.deliver_timeout=Zeitlimit für Zustellung
+config.skip_tls_verify=Skip TLS Verify
 config.mailer_config=Mailer-Einstellungen
 config.mailer_config=Mailer-Einstellungen
 config.mailer_enabled=Aktiviert
 config.mailer_enabled=Aktiviert
 config.mailer_name=Name
 config.mailer_name=Name

+ 2 - 2
conf/locale/locale_en-US.ini

@@ -281,13 +281,13 @@ init_readme = Initialize this repository with a README.md
 create_repo = Create Repository
 create_repo = Create Repository
 default_branch = Default Branch
 default_branch = Default Branch
 mirror_interval = Mirror Interval (hour)
 mirror_interval = Mirror Interval (hour)
-goget_meta = Go-Get Meta
-goget_meta_helper = This repository will be <span class="label label-blue label-radius">Go-Getable</span>
 
 
 need_auth = Need Authorization
 need_auth = Need Authorization
 migrate_type = Migration Type
 migrate_type = Migration Type
 migrate_type_helper = This repository will be a <span class="label label-blue label-radius">Mirror</span>
 migrate_type_helper = This repository will be a <span class="label label-blue label-radius">Mirror</span>
 migrate_repo = Migrate Repository
 migrate_repo = Migrate Repository
+migrate.clone_address = Clone Address
+migrate.invalid_local_path = Invalid local path, it does not exist or not a directory.
 
 
 copy_link = Copy
 copy_link = Copy
 click_to_copy = Copy to clipboard
 click_to_copy = Copy to clipboard

+ 1 - 0
conf/locale/locale_es-ES.ini

@@ -647,6 +647,7 @@ config.reset_password_code_lives=Reset Password Code Lives
 config.webhook_config=Webhook Configuration
 config.webhook_config=Webhook Configuration
 config.task_interval=Task Interval
 config.task_interval=Task Interval
 config.deliver_timeout=Deliver Timeout
 config.deliver_timeout=Deliver Timeout
+config.skip_tls_verify=Skip TLS Verify
 config.mailer_config=Mailer Configuration
 config.mailer_config=Mailer Configuration
 config.mailer_enabled=Enabled
 config.mailer_enabled=Enabled
 config.mailer_name=Name
 config.mailer_name=Name

+ 8 - 7
conf/locale/locale_fr-CA.ini

@@ -59,8 +59,8 @@ run_user=Entrer un Utilisateur
 run_user_helper=L'utilisateur doit avoir accès à la Racine du Référentiel et éxécuter Gogs.
 run_user_helper=L'utilisateur doit avoir accès à la Racine du Référentiel et éxécuter Gogs.
 domain=Domaine
 domain=Domaine
 domain_helper=Cela affecte les doublons d'URL SSH.
 domain_helper=Cela affecte les doublons d'URL SSH.
-http_port=HTTP Port
-http_port_helper=Port number which application will listen on.
+http_port=Port HTTP
+http_port_helper=Numéro de port que l'application écoutera.
 app_url=URL de l'Application
 app_url=URL de l'Application
 app_url_helper=Cela affecte les doublons d'URL HTTP/HTTPS et le contenu d'e-mail.
 app_url_helper=Cela affecte les doublons d'URL HTTP/HTTPS et le contenu d'e-mail.
 email_title=Paramètres du Service de Messagerie (Facultatif)
 email_title=Paramètres du Service de Messagerie (Facultatif)
@@ -514,10 +514,10 @@ dashboard.delete_repo_archives=Supprimer toutes les archives de référentiels
 dashboard.delete_repo_archives_success=Toutes les archives de référentiels ont été supprimés avec succès.
 dashboard.delete_repo_archives_success=Toutes les archives de référentiels ont été supprimés avec succès.
 dashboard.git_gc_repos=Collecter les déchets des référentiels
 dashboard.git_gc_repos=Collecter les déchets des référentiels
 dashboard.git_gc_repos_success=Tous les référentiels ont effectué la collecte avec succès.
 dashboard.git_gc_repos_success=Tous les référentiels ont effectué la collecte avec succès.
-dashboard.resync_all_sshkeys=Rewrite '.ssh/autorized_key' file (caution: non-Gogs keys will be lost)
-dashboard.resync_all_sshkeys_success=All public keys have been rewritten successfully.
-dashboard.resync_all_update_hooks=Rewrite all update hook of repositories (needed when custom config path is changed)
-dashboard.resync_all_update_hooks_success=All repositories' update hook have been rewritten successfully.
+dashboard.resync_all_sshkeys=Ré-écrire le fichier '.ssh/autorized_key' (attention : les clés hors-Gogs vont être perdues)
+dashboard.resync_all_sshkeys_success=Toutes les clés publiques ont été ré-écrites avec succès.
+dashboard.resync_all_update_hooks=Ré-écrire tous les hooks de mises à jour des dépôts (requis quand le chemin de la configuration personnalisé est modifié)
+dashboard.resync_all_update_hooks_success=Tous les hooks de mises à jour des dépôts ont été ré-écris avec succès.
 
 
 dashboard.server_uptime=Durée de Marche Serveur
 dashboard.server_uptime=Durée de Marche Serveur
 dashboard.current_goroutine=Goroutines actuelles
 dashboard.current_goroutine=Goroutines actuelles
@@ -638,7 +638,7 @@ config.db_path_helper=("sqlite3" uniquement)
 config.service_config=Configuration du Service
 config.service_config=Configuration du Service
 config.register_email_confirm=Nécessite une confirmation par courriel
 config.register_email_confirm=Nécessite une confirmation par courriel
 config.disable_register=Désactiver l'Enregistrement
 config.disable_register=Désactiver l'Enregistrement
-config.show_registration_button=Show Register Button
+config.show_registration_button=Afficher le bouton d'enregistrement
 config.require_sign_in_view=Connexion Obligatoire pour Visualiser
 config.require_sign_in_view=Connexion Obligatoire pour Visualiser
 config.mail_notify=Mailer les Notifications
 config.mail_notify=Mailer les Notifications
 config.enable_cache_avatar=Activer le Cache d'Avatar
 config.enable_cache_avatar=Activer le Cache d'Avatar
@@ -647,6 +647,7 @@ config.reset_password_code_lives=Réinitialiser le Mot De Passe des Limites de C
 config.webhook_config=Configuration Webhook
 config.webhook_config=Configuration Webhook
 config.task_interval=Intervalles de Tâches
 config.task_interval=Intervalles de Tâches
 config.deliver_timeout=Expiration d'Envoi
 config.deliver_timeout=Expiration d'Envoi
+config.skip_tls_verify=Ne pas vérifier TLS
 config.mailer_config=Configuration du Maileur
 config.mailer_config=Configuration du Maileur
 config.mailer_enabled=Activé
 config.mailer_enabled=Activé
 config.mailer_name=Nom
 config.mailer_name=Nom

+ 1 - 0
conf/locale/locale_ja-JP.ini

@@ -647,6 +647,7 @@ config.reset_password_code_lives=パスワードリンクの有効期限をリ
 config.webhook_config=Webhook設定
 config.webhook_config=Webhook設定
 config.task_interval=タスクの間隔
 config.task_interval=タスクの間隔
 config.deliver_timeout=送信タイムアウト
 config.deliver_timeout=送信タイムアウト
+config.skip_tls_verify=TLSの確認を省略
 config.mailer_config=メーラーの構成
 config.mailer_config=メーラーの構成
 config.mailer_enabled=有効にした
 config.mailer_enabled=有効にした
 config.mailer_name=名前
 config.mailer_name=名前

+ 1 - 0
conf/locale/locale_lv-LV.ini

@@ -647,6 +647,7 @@ config.reset_password_code_lives=Paroles atiestatīšanas koda ilgums
 config.webhook_config=Tīkla āķu konfigurācija
 config.webhook_config=Tīkla āķu konfigurācija
 config.task_interval=Uzdevuma intervāls
 config.task_interval=Uzdevuma intervāls
 config.deliver_timeout=Piegādes noildze
 config.deliver_timeout=Piegādes noildze
+config.skip_tls_verify=Skip TLS Verify
 config.mailer_config=Sūtītāja konfigurācija
 config.mailer_config=Sūtītāja konfigurācija
 config.mailer_enabled=Iespējots
 config.mailer_enabled=Iespējots
 config.mailer_name=Nosaukums
 config.mailer_name=Nosaukums

+ 3 - 2
conf/locale/locale_nl-NL.ini

@@ -516,8 +516,8 @@ dashboard.git_gc_repos=Garbage collectie uitvoeren
 dashboard.git_gc_repos_success=Garbage collectie met succes uitgevoerd.
 dashboard.git_gc_repos_success=Garbage collectie met succes uitgevoerd.
 dashboard.resync_all_sshkeys=Herschrijf '.ssh/authorized_keys' (Let op: alle sleutels die niet van Gogs zijn zullen verloren gaan!)
 dashboard.resync_all_sshkeys=Herschrijf '.ssh/authorized_keys' (Let op: alle sleutels die niet van Gogs zijn zullen verloren gaan!)
 dashboard.resync_all_sshkeys_success=Alle publieke sleutels zijn herschreven.
 dashboard.resync_all_sshkeys_success=Alle publieke sleutels zijn herschreven.
-dashboard.resync_all_update_hooks=Rewrite all update hook of repositories (needed when custom config path is changed)
-dashboard.resync_all_update_hooks_success=All repositories' update hook have been rewritten successfully.
+dashboard.resync_all_update_hooks=Herschrijf alle repositorie-hooks (nodig als de configuratie bestandslocatie is gewijzigd)
+dashboard.resync_all_update_hooks_success=Alle repositorie-hooks zijn herschreven.
 
 
 dashboard.server_uptime=Uptime server
 dashboard.server_uptime=Uptime server
 dashboard.current_goroutine=Huidige Goroutines
 dashboard.current_goroutine=Huidige Goroutines
@@ -647,6 +647,7 @@ config.reset_password_code_lives=Reset wachtwoord Code leven
 config.webhook_config=Webhook configuratie
 config.webhook_config=Webhook configuratie
 config.task_interval=Taakinterval
 config.task_interval=Taakinterval
 config.deliver_timeout=Bezorging verlooptijd
 config.deliver_timeout=Bezorging verlooptijd
+config.skip_tls_verify=TLS certificaat controle overslaan
 config.mailer_config=Mailerconfiguatie
 config.mailer_config=Mailerconfiguatie
 config.mailer_enabled=Ingeschakeld
 config.mailer_enabled=Ingeschakeld
 config.mailer_name=Naam
 config.mailer_name=Naam

+ 10 - 9
conf/locale/locale_ru-RU.ini

@@ -58,11 +58,11 @@ repo_path_helper=Всех удаленные репозитории Git буде
 run_user=Пользователь
 run_user=Пользователь
 run_user_helper=У пользователя должен быть доступ к пути к корню репозитория и к запуску Gogs.
 run_user_helper=У пользователя должен быть доступ к пути к корню репозитория и к запуску Gogs.
 domain=Домен
 domain=Домен
-domain_helper=This affects SSH clone URLs.
-http_port=HTTP Port
-http_port_helper=Port number which application will listen on.
+domain_helper=Влияет на URL-адреса для клонирования по SSH.
+http_port=Порт HTTP
+http_port_helper=Номер порта, который приложение будет слушать.
 app_url=URL приложения
 app_url=URL приложения
-app_url_helper=This affects HTTP/HTTPS clone URL and somewhere in e-mail.
+app_url_helper=Этот параметр влияет на URL для клонирования по HTTP/HTTPS и на адреса в электронной почте.
 email_title=Настройки службы электронной почты (опционально)
 email_title=Настройки службы электронной почты (опционально)
 smtp_host=Узел SMTP
 smtp_host=Узел SMTP
 mailer_user=Электронная почта отправителя
 mailer_user=Электронная почта отправителя
@@ -80,7 +80,7 @@ test_git_failed=Не удалось проверить 'git' команду: %v
 sqlite3_not_available=Ваша версия не поддерживает SQLite3, пожалуйста скачайте официальную бинарную версию от %s, а не версию gobuild.
 sqlite3_not_available=Ваша версия не поддерживает SQLite3, пожалуйста скачайте официальную бинарную версию от %s, а не версию gobuild.
 invalid_db_setting=Настройки базы данных не правильные: %v
 invalid_db_setting=Настройки базы данных не правильные: %v
 invalid_repo_path=Недопустимый путь к корню репозитория: %v
 invalid_repo_path=Недопустимый путь к корню репозитория: %v
-run_user_not_match=Run user isn't the current user: %s -> %s
+run_user_not_match=Текущий пользователь не является пользователем для запуска: %s -> %s
 save_config_failed=Не удалось сохранить конфигурацию: %v
 save_config_failed=Не удалось сохранить конфигурацию: %v
 invalid_admin_setting=Указан недопустимый параметр учетной записи администратора: %v
 invalid_admin_setting=Указан недопустимый параметр учетной записи администратора: %v
 install_success=Добро пожаловать! Мы рады, что вы выбрали Gogs. Веселитесь и берегите себя.
 install_success=Добро пожаловать! Мы рады, что вы выбрали Gogs. Веселитесь и берегите себя.
@@ -88,7 +88,7 @@ install_success=Добро пожаловать! Мы рады, что вы вы
 [home]
 [home]
 uname_holder=Имя пользователь или E-mail
 uname_holder=Имя пользователь или E-mail
 password_holder=Пароль
 password_holder=Пароль
-switch_dashboard_context=Switch Dashboard Context
+switch_dashboard_context=Переключить контекст панели управления
 my_repos=Мои репозитории
 my_repos=Мои репозитории
 collaborative_repos=Совместные репозитории
 collaborative_repos=Совместные репозитории
 my_orgs=Моя Организация
 my_orgs=Моя Организация
@@ -280,9 +280,9 @@ license_helper=Выберите файл лицензии
 init_readme=Создать репозиторий с файлом README.md
 init_readme=Создать репозиторий с файлом README.md
 create_repo=Создание репозитория
 create_repo=Создание репозитория
 default_branch=Ветка по умолчанию
 default_branch=Ветка по умолчанию
-mirror_interval=Mirror Interval (hour)
-goget_meta=Go-Get Meta
-goget_meta_helper=This repository will be <span class="label label-blue label-radius">Go-Getable</span>
+mirror_interval=Интервал зеркалирования (час)
+goget_meta=Meta-тег для go get
+goget_meta_helper=Репозиторий будет доступен для <span class="label label-blue label-radius">go get</span>
 
 
 need_auth=Требуется авторизация
 need_auth=Требуется авторизация
 migrate_type=Тип миграции
 migrate_type=Тип миграции
@@ -647,6 +647,7 @@ config.reset_password_code_lives=Reset Password Code Lives
 config.webhook_config=Настройка автоматического обновления репозиции
 config.webhook_config=Настройка автоматического обновления репозиции
 config.task_interval=Интервал задания
 config.task_interval=Интервал задания
 config.deliver_timeout=Задержка доставки
 config.deliver_timeout=Задержка доставки
+config.skip_tls_verify=Skip TLS Verify
 config.mailer_config=Настройки почты
 config.mailer_config=Настройки почты
 config.mailer_enabled=Включено
 config.mailer_enabled=Включено
 config.mailer_name=Имя
 config.mailer_name=Имя

+ 1 - 0
conf/locale/locale_zh-CN.ini

@@ -647,6 +647,7 @@ config.reset_password_code_lives=重置密码链接有效期
 config.webhook_config=Web 钩子配置
 config.webhook_config=Web 钩子配置
 config.task_interval=任务周期
 config.task_interval=任务周期
 config.deliver_timeout=推送超时
 config.deliver_timeout=推送超时
+config.skip_tls_verify=忽略 TLS 验证
 config.mailer_config=邮件配置
 config.mailer_config=邮件配置
 config.mailer_enabled=启用服务
 config.mailer_enabled=启用服务
 config.mailer_name=发送者名称
 config.mailer_name=发送者名称

+ 1 - 0
conf/locale/locale_zh-HK.ini

@@ -647,6 +647,7 @@ config.reset_password_code_lives=重置密碼連結有效期
 config.webhook_config=Web 鉤子配置
 config.webhook_config=Web 鉤子配置
 config.task_interval=任務周期
 config.task_interval=任務周期
 config.deliver_timeout=推送超時
 config.deliver_timeout=推送超時
+config.skip_tls_verify=Skip TLS Verify
 config.mailer_config=郵件配置
 config.mailer_config=郵件配置
 config.mailer_enabled=啟用服務
 config.mailer_enabled=啟用服務
 config.mailer_name=發送者名稱
 config.mailer_name=發送者名稱

+ 1 - 1
docker/blocks/docker_gogs/Dockerfile

@@ -4,7 +4,7 @@ FROM ubuntu:14.04
 
 
 RUN apt-get update && apt-get install -y \
 RUN apt-get update && apt-get install -y \
 		build-essential ca-certificates curl \
 		build-essential ca-certificates curl \
-		bzr git mercurial \
+		bzr git mercurial openssh-client\
 		--no-install-recommends
 		--no-install-recommends
 
 
 ENV GOLANG_VERSION 1.3
 ENV GOLANG_VERSION 1.3

+ 1 - 1
docker/blocks/docker_gogs_dev/Dockerfile

@@ -5,7 +5,7 @@ FROM ubuntu:14.04
 RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
 RUN DEBIAN_FRONTEND=noninteractive apt-get update && \
 		apt-get install -qy \
 		apt-get install -qy \
 		build-essential ca-certificates curl \
 		build-essential ca-certificates curl \
-		bzr git mercurial \
+		bzr git mercurial openssh-client\
 		--no-install-recommends
 		--no-install-recommends
 
 
 ENV GOLANG_VERSION 1.3
 ENV GOLANG_VERSION 1.3

+ 1 - 1
docker/templates/init_gogs.sh.tpl

@@ -1,6 +1,6 @@
 #!/bin/sh
 #!/bin/sh
 
 
-if [ ! -d "$DIRECTORY" ]; then
+if [ ! -d "$GOGS_CUSTOM_CONF_PATH" ]; then
     mkdir -p $GOGS_CUSTOM_CONF_PATH
     mkdir -p $GOGS_CUSTOM_CONF_PATH
 
 
 echo "
 echo "

+ 1 - 1
gogs.go

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

+ 23 - 14
models/access.go

@@ -24,21 +24,19 @@ type Access struct {
 	Mode   AccessMode
 	Mode   AccessMode
 }
 }
 
 
-// Return the Access a user has to a repository. Will return NoneAccess if the
-// user does not have access. User can be nil!
-func AccessLevel(u *User, r *Repository) (AccessMode, error) {
+func accessLevel(e Engine, u *User, repo *Repository) (AccessMode, error) {
 	mode := ACCESS_MODE_NONE
 	mode := ACCESS_MODE_NONE
-	if !r.IsPrivate {
+	if !repo.IsPrivate {
 		mode = ACCESS_MODE_READ
 		mode = ACCESS_MODE_READ
 	}
 	}
 
 
 	if u != nil {
 	if u != nil {
-		if u.Id == r.OwnerId {
+		if u.Id == repo.OwnerId {
 			return ACCESS_MODE_OWNER, nil
 			return ACCESS_MODE_OWNER, nil
 		}
 		}
 
 
-		a := &Access{UserID: u.Id, RepoID: r.Id}
-		if has, err := x.Get(a); !has || err != nil {
+		a := &Access{UserID: u.Id, RepoID: repo.Id}
+		if has, err := e.Get(a); !has || err != nil {
 			return mode, err
 			return mode, err
 		}
 		}
 		return a.Mode, nil
 		return a.Mode, nil
@@ -47,12 +45,22 @@ func AccessLevel(u *User, r *Repository) (AccessMode, error) {
 	return mode, nil
 	return mode, nil
 }
 }
 
 
-// HasAccess returns true if someone has the request access level. User can be nil!
-func HasAccess(u *User, r *Repository, testMode AccessMode) (bool, error) {
-	mode, err := AccessLevel(u, r)
+// AccessLevel returns the Access a user has to a repository. Will return NoneAccess if the
+// user does not have access. User can be nil!
+func AccessLevel(u *User, repo *Repository) (AccessMode, error) {
+	return accessLevel(x, u, repo)
+}
+
+func hasAccess(e Engine, u *User, repo *Repository, testMode AccessMode) (bool, error) {
+	mode, err := accessLevel(e, u, repo)
 	return testMode <= mode, err
 	return testMode <= mode, err
 }
 }
 
 
+// HasAccess returns true if someone has the request access level. User can be nil!
+func HasAccess(u *User, repo *Repository, testMode AccessMode) (bool, error) {
+	return hasAccess(x, u, repo, testMode)
+}
+
 // GetAccessibleRepositories finds all repositories where a user has access to,
 // GetAccessibleRepositories finds all repositories where a user has access to,
 // besides his own.
 // besides his own.
 func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) {
 func (u *User) GetAccessibleRepositories() (map[*Repository]AccessMode, error) {
@@ -96,7 +104,8 @@ func (repo *Repository) recalculateTeamAccesses(e Engine, mode AccessMode) error
 func (repo *Repository) recalculateAccesses(e Engine) error {
 func (repo *Repository) recalculateAccesses(e Engine) error {
 	accessMap := make(map[int64]AccessMode, 20)
 	accessMap := make(map[int64]AccessMode, 20)
 
 
-	// Give all collaborators write access
+	// FIXME: should be able to have read-only access.
+	// Give all collaborators write access.
 	collaborators, err := repo.getCollaborators(e)
 	collaborators, err := repo.getCollaborators(e)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -114,9 +123,7 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
 		}
 		}
 
 
 		for _, team := range repo.Owner.Teams {
 		for _, team := range repo.Owner.Teams {
-			if !(team.IsOwnerTeam() || team.HasRepository(repo)) {
-				continue
-			} else if team.IsOwnerTeam() {
+			if team.IsOwnerTeam() {
 				team.Authorize = ACCESS_MODE_OWNER
 				team.Authorize = ACCESS_MODE_OWNER
 			}
 			}
 
 
@@ -129,6 +136,8 @@ func (repo *Repository) recalculateAccesses(e Engine) error {
 		}
 		}
 	}
 	}
 
 
+	// FIXME: do corss-comparison so reduce deletions and additions to the minimum?
+
 	minMode := ACCESS_MODE_READ
 	minMode := ACCESS_MODE_READ
 	if !repo.IsPrivate {
 	if !repo.IsPrivate {
 		minMode = ACCESS_MODE_WRITE
 		minMode = ACCESS_MODE_WRITE

+ 10 - 10
models/action.go

@@ -434,20 +434,20 @@ func NewRepoAction(u *User, repo *Repository) (err error) {
 	return newRepoAction(x, u, repo)
 	return newRepoAction(x, u, repo)
 }
 }
 
 
-func transferRepoAction(e Engine, u, newUser *User, repo *Repository) (err error) {
+func transferRepoAction(e Engine, actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
 	action := &Action{
 	action := &Action{
-		ActUserId:    u.Id,
-		ActUserName:  u.Name,
-		ActEmail:     u.Email,
+		ActUserId:    actUser.Id,
+		ActUserName:  actUser.Name,
+		ActEmail:     actUser.Email,
 		OpType:       TRANSFER_REPO,
 		OpType:       TRANSFER_REPO,
 		RepoId:       repo.Id,
 		RepoId:       repo.Id,
-		RepoUserName: newUser.Name,
+		RepoUserName: newOwner.Name,
 		RepoName:     repo.Name,
 		RepoName:     repo.Name,
 		IsPrivate:    repo.IsPrivate,
 		IsPrivate:    repo.IsPrivate,
-		Content:      path.Join(repo.Owner.LowerName, repo.LowerName),
+		Content:      path.Join(oldOwner.LowerName, repo.LowerName),
 	}
 	}
 	if err = notifyWatchers(e, action); err != nil {
 	if err = notifyWatchers(e, action); err != nil {
-		return fmt.Errorf("notify watchers '%d/%s'", u.Id, repo.Id)
+		return fmt.Errorf("notify watchers '%d/%s'", actUser.Id, repo.Id)
 	}
 	}
 
 
 	// Remove watch for organization.
 	// Remove watch for organization.
@@ -457,13 +457,13 @@ func transferRepoAction(e Engine, u, newUser *User, repo *Repository) (err error
 		}
 		}
 	}
 	}
 
 
-	log.Trace("action.TransferRepoAction: %s/%s", u.Name, repo.Name)
+	log.Trace("action.TransferRepoAction: %s/%s", actUser.Name, repo.Name)
 	return nil
 	return nil
 }
 }
 
 
 // TransferRepoAction adds new action for transferring repository.
 // TransferRepoAction adds new action for transferring repository.
-func TransferRepoAction(u, newUser *User, repo *Repository) (err error) {
-	return transferRepoAction(x, u, newUser, repo)
+func TransferRepoAction(actUser, oldOwner, newOwner *User, repo *Repository) (err error) {
+	return transferRepoAction(x, actUser, oldOwner, newOwner, repo)
 }
 }
 
 
 // GetFeeds returns action list of given user in given context.
 // GetFeeds returns action list of given user in given context.

+ 1 - 1
models/issue.go

@@ -564,7 +564,7 @@ func GetLabels(repoId int64) ([]*Label, error) {
 
 
 // UpdateLabel updates label information.
 // UpdateLabel updates label information.
 func UpdateLabel(l *Label) error {
 func UpdateLabel(l *Label) error {
-	_, err := x.Id(l.Id).Update(l)
+	_, err := x.Id(l.Id).AllCols().Update(l)
 	return err
 	return err
 }
 }
 
 

+ 64 - 12
models/migrations/migrations.go

@@ -53,6 +53,7 @@ var migrations = []Migration{
 	NewMigration("generate collaboration from access", accessToCollaboration), // V0 -> V1
 	NewMigration("generate collaboration from access", accessToCollaboration), // V0 -> V1
 	NewMigration("make authorize 4 if team is owners", ownerTeamUpdate),       // V1 -> V2
 	NewMigration("make authorize 4 if team is owners", ownerTeamUpdate),       // V1 -> V2
 	NewMigration("refactor access table to use id's", accessRefactor),         // V2 -> V3
 	NewMigration("refactor access table to use id's", accessRefactor),         // V2 -> V3
+	NewMigration("generate team-repo from team", teamToTeamRepo),              // V3 -> V4
 }
 }
 
 
 // Migrate database to current version
 // Migrate database to current version
@@ -214,8 +215,8 @@ func accessToCollaboration(x *xorm.Engine) (err error) {
 }
 }
 
 
 func ownerTeamUpdate(x *xorm.Engine) (err error) {
 func ownerTeamUpdate(x *xorm.Engine) (err error) {
-	if _, err := x.Exec("UPDATE team SET authorize=4 WHERE lower_name=?", "owners"); err != nil {
-		return fmt.Errorf("drop table: %v", err)
+	if _, err := x.Exec("UPDATE `team` SET authorize=4 WHERE lower_name=?", "owners"); err != nil {
+		return fmt.Errorf("update owner team table: %v", err)
 	}
 	}
 	return nil
 	return nil
 }
 }
@@ -239,9 +240,9 @@ func accessRefactor(x *xorm.Engine) (err error) {
 
 
 	accessMap := make(map[UserRepo]AccessMode, 50)
 	accessMap := make(map[UserRepo]AccessMode, 50)
 
 
-	results, err := x.Query("SELECT r.id as `repo_id`, r.is_private as `is_private`, r.owner_id as `owner_id`, u.type as `owner_type` FROM `repository` r LEFT JOIN user u ON r.owner_id=u.id")
+	results, err := x.Query("SELECT r.id AS `repo_id`, r.is_private AS `is_private`, r.owner_id AS `owner_id`, u.type AS `owner_type` FROM `repository` r LEFT JOIN `user` u ON r.owner_id=u.id")
 	if err != nil {
 	if err != nil {
-		return err
+		return fmt.Errorf("select repositories: %v", err)
 	}
 	}
 	for _, repo := range results {
 	for _, repo := range results {
 		repoID := com.StrTo(repo["repo_id"]).MustInt64()
 		repoID := com.StrTo(repo["repo_id"]).MustInt64()
@@ -249,9 +250,9 @@ func accessRefactor(x *xorm.Engine) (err error) {
 		ownerID := com.StrTo(repo["owner_id"]).MustInt64()
 		ownerID := com.StrTo(repo["owner_id"]).MustInt64()
 		ownerIsOrganization := com.StrTo(repo["owner_type"]).MustInt() > 0
 		ownerIsOrganization := com.StrTo(repo["owner_type"]).MustInt() > 0
 
 
-		results, err := x.Query("SELECT user_id FROM collaboration WHERE repo_id=?", repoID)
+		results, err := x.Query("SELECT `user_id` FROM `collaboration` WHERE repo_id=?", repoID)
 		if err != nil {
 		if err != nil {
-			return fmt.Errorf("select repos: %v", err)
+			return fmt.Errorf("select collaborators: %v", err)
 		}
 		}
 		for _, user := range results {
 		for _, user := range results {
 			userID := com.StrTo(user["user_id"]).MustInt64()
 			userID := com.StrTo(user["user_id"]).MustInt64()
@@ -262,6 +263,8 @@ func accessRefactor(x *xorm.Engine) (err error) {
 			continue
 			continue
 		}
 		}
 
 
+		// The minimum level to add a new access record,
+		// because public repository has implicit open access.
 		minAccessLevel := AccessMode(0)
 		minAccessLevel := AccessMode(0)
 		if !isPrivate {
 		if !isPrivate {
 			minAccessLevel = 1
 			minAccessLevel = 1
@@ -269,7 +272,7 @@ func accessRefactor(x *xorm.Engine) (err error) {
 
 
 		repoString := "$" + string(repo["repo_id"]) + "|"
 		repoString := "$" + string(repo["repo_id"]) + "|"
 
 
-		results, err = x.Query("SELECT id, authorize, repo_ids FROM team WHERE org_id=? AND authorize > ? ORDER BY authorize ASC", ownerID, int(minAccessLevel))
+		results, err = x.Query("SELECT `id`,`authorize`,`repo_ids` FROM `team` WHERE org_id=? AND authorize>? ORDER BY `authorize` ASC", ownerID, int(minAccessLevel))
 		if err != nil {
 		if err != nil {
 			return fmt.Errorf("select teams from org: %v", err)
 			return fmt.Errorf("select teams from org: %v", err)
 		}
 		}
@@ -281,20 +284,20 @@ func accessRefactor(x *xorm.Engine) (err error) {
 			teamID := com.StrTo(team["id"]).MustInt64()
 			teamID := com.StrTo(team["id"]).MustInt64()
 			mode := AccessMode(com.StrTo(team["authorize"]).MustInt())
 			mode := AccessMode(com.StrTo(team["authorize"]).MustInt())
 
 
-			results, err := x.Query("SELECT uid FROM team_user WHERE team_id=?", teamID)
+			results, err := x.Query("SELECT `uid` FROM `team_user` WHERE team_id=?", teamID)
 			if err != nil {
 			if err != nil {
 				return fmt.Errorf("select users from team: %v", err)
 				return fmt.Errorf("select users from team: %v", err)
 			}
 			}
 			for _, user := range results {
 			for _, user := range results {
-				userID := com.StrTo(user["uid"]).MustInt64()
+				userID := com.StrTo(user["user_id"]).MustInt64()
 				accessMap[UserRepo{userID, repoID}] = mode
 				accessMap[UserRepo{userID, repoID}] = mode
 			}
 			}
 		}
 		}
 	}
 	}
 
 
 	// Drop table can't be in a session (at least not in sqlite)
 	// Drop table can't be in a session (at least not in sqlite)
-	if _, err = x.Exec("DROP TABLE access"); err != nil {
-		return fmt.Errorf("drop table: %v", err)
+	if _, err = x.Exec("DROP TABLE `access`"); err != nil {
+		return fmt.Errorf("drop access table: %v", err)
 	}
 	}
 
 
 	// Now we start writing so we make a session
 	// Now we start writing so we make a session
@@ -313,7 +316,56 @@ func accessRefactor(x *xorm.Engine) (err error) {
 		accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode})
 		accesses = append(accesses, &Access{UserID: ur.UserID, RepoID: ur.RepoID, Mode: mode})
 	}
 	}
 
 
-	_, err = sess.Insert(accesses)
+	if _, err = sess.Insert(accesses); err != nil {
+		return fmt.Errorf("insert accesses: %v", err)
+	}
+
+	return sess.Commit()
+}
+
+func teamToTeamRepo(x *xorm.Engine) error {
+	type TeamRepo struct {
+		ID     int64 `xorm:"pk autoincr"`
+		OrgID  int64 `xorm:"INDEX"`
+		TeamID int64 `xorm:"UNIQUE(s)"`
+		RepoID int64 `xorm:"UNIQUE(s)"`
+	}
+
+	teamRepos := make([]*TeamRepo, 0, 50)
+
+	results, err := x.Query("SELECT `id`,`org_id`,`repo_ids` FROM `team`")
+	if err != nil {
+		return fmt.Errorf("select teams: %v", err)
+	}
+	for _, team := range results {
+		orgID := com.StrTo(team["org_id"]).MustInt64()
+		teamID := com.StrTo(team["id"]).MustInt64()
+
+		for _, idStr := range strings.Split(string(team["repo_ids"]), "|") {
+			repoID := com.StrTo(strings.TrimPrefix(idStr, "$")).MustInt64()
+			if repoID == 0 {
+				continue
+			}
+
+			teamRepos = append(teamRepos, &TeamRepo{
+				OrgID:  orgID,
+				TeamID: teamID,
+				RepoID: repoID,
+			})
+		}
+	}
+
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
+		return err
+	}
+
+	if err = sess.Sync2(new(TeamRepo)); err != nil {
+		return fmt.Errorf("sync: %v", err)
+	} else if _, err = sess.Insert(teamRepos); err != nil {
+		return fmt.Errorf("insert team-repos: %v", err)
+	}
 
 
 	return sess.Commit()
 	return sess.Commit()
 }
 }

+ 3 - 2
models/models.go

@@ -59,7 +59,8 @@ func init() {
 		new(Watch), new(Star), new(Follow), new(Action),
 		new(Watch), new(Star), new(Follow), new(Action),
 		new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
 		new(Issue), new(Comment), new(Attachment), new(IssueUser), new(Label), new(Milestone),
 		new(Mirror), new(Release), new(LoginSource), new(Webhook),
 		new(Mirror), new(Release), new(LoginSource), new(Webhook),
-		new(UpdateTask), new(HookTask), new(Team), new(OrgUser), new(TeamUser),
+		new(UpdateTask), new(HookTask),
+		new(Team), new(OrgUser), new(TeamUser), new(TeamRepo),
 		new(Notice), new(EmailAddress))
 		new(Notice), new(EmailAddress))
 }
 }
 
 
@@ -140,7 +141,7 @@ func SetEngine() (err error) {
 	if err != nil {
 	if err != nil {
 		return fmt.Errorf("models.init(fail to create xorm.log): %v", err)
 		return fmt.Errorf("models.init(fail to create xorm.log): %v", err)
 	}
 	}
-	x.Logger = xorm.NewSimpleLogger(f)
+	x.SetLogger(xorm.NewSimpleLogger(f))
 
 
 	x.ShowSQL = true
 	x.ShowSQL = true
 	x.ShowInfo = true
 	x.ShowInfo = true

+ 182 - 116
models/org.go

@@ -6,11 +6,10 @@ package models
 
 
 import (
 import (
 	"errors"
 	"errors"
+	"fmt"
 	"os"
 	"os"
 	"strings"
 	"strings"
 
 
-	"github.com/Unknwon/com"
-
 	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/base"
 )
 )
 
 
@@ -103,7 +102,7 @@ func CreateOrganization(org, owner *User) (*User, error) {
 		return nil, ErrUserNameIllegal
 		return nil, ErrUserNameIllegal
 	}
 	}
 
 
-	isExist, err := IsUserExist(org.Name)
+	isExist, err := IsUserExist(0, org.Name)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	} else if isExist {
 	} else if isExist {
@@ -126,53 +125,48 @@ func CreateOrganization(org, owner *User) (*User, error) {
 	org.NumMembers = 1
 	org.NumMembers = 1
 
 
 	sess := x.NewSession()
 	sess := x.NewSession()
-	defer sess.Close()
+	defer sessionRelease(sess)
 	if err = sess.Begin(); err != nil {
 	if err = sess.Begin(); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	if _, err = sess.Insert(org); err != nil {
 	if _, err = sess.Insert(org); err != nil {
-		sess.Rollback()
-		return nil, err
-	}
-
-	if err = os.MkdirAll(UserPath(org.Name), os.ModePerm); err != nil {
-		sess.Rollback()
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	// Create default owner team.
 	// Create default owner team.
 	t := &Team{
 	t := &Team{
-		OrgId:      org.Id,
+		OrgID:      org.Id,
 		LowerName:  strings.ToLower(OWNER_TEAM),
 		LowerName:  strings.ToLower(OWNER_TEAM),
 		Name:       OWNER_TEAM,
 		Name:       OWNER_TEAM,
 		Authorize:  ACCESS_MODE_OWNER,
 		Authorize:  ACCESS_MODE_OWNER,
 		NumMembers: 1,
 		NumMembers: 1,
 	}
 	}
 	if _, err = sess.Insert(t); err != nil {
 	if _, err = sess.Insert(t); err != nil {
-		sess.Rollback()
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	// Add initial creator to organization and owner team.
 	// Add initial creator to organization and owner team.
 	ou := &OrgUser{
 	ou := &OrgUser{
 		Uid:      owner.Id,
 		Uid:      owner.Id,
-		OrgId:    org.Id,
+		OrgID:    org.Id,
 		IsOwner:  true,
 		IsOwner:  true,
 		NumTeams: 1,
 		NumTeams: 1,
 	}
 	}
 	if _, err = sess.Insert(ou); err != nil {
 	if _, err = sess.Insert(ou); err != nil {
-		sess.Rollback()
 		return nil, err
 		return nil, err
 	}
 	}
 
 
 	tu := &TeamUser{
 	tu := &TeamUser{
 		Uid:    owner.Id,
 		Uid:    owner.Id,
-		OrgId:  org.Id,
-		TeamId: t.Id,
+		OrgID:  org.Id,
+		TeamID: t.ID,
 	}
 	}
 	if _, err = sess.Insert(tu); err != nil {
 	if _, err = sess.Insert(tu); err != nil {
-		sess.Rollback()
+		return nil, err
+	}
+
+	if err = os.MkdirAll(UserPath(org.Name), os.ModePerm); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -223,15 +217,15 @@ func DeleteOrganization(org *User) (err error) {
 		return err
 		return err
 	}
 	}
 
 
-	if _, err = sess.Delete(&Team{OrgId: org.Id}); err != nil {
+	if _, err = sess.Delete(&Team{OrgID: org.Id}); err != nil {
 		sess.Rollback()
 		sess.Rollback()
 		return err
 		return err
 	}
 	}
-	if _, err = sess.Delete(&OrgUser{OrgId: org.Id}); err != nil {
+	if _, err = sess.Delete(&OrgUser{OrgID: org.Id}); err != nil {
 		sess.Rollback()
 		sess.Rollback()
 		return err
 		return err
 	}
 	}
-	if _, err = sess.Delete(&TeamUser{OrgId: org.Id}); err != nil {
+	if _, err = sess.Delete(&TeamUser{OrgID: org.Id}); err != nil {
 		sess.Rollback()
 		sess.Rollback()
 		return err
 		return err
 	}
 	}
@@ -247,9 +241,9 @@ func DeleteOrganization(org *User) (err error) {
 
 
 // OrgUser represents an organization-user relation.
 // OrgUser represents an organization-user relation.
 type OrgUser struct {
 type OrgUser struct {
-	Id       int64
+	ID       int64 `xorm:"pk autoincr"`
 	Uid      int64 `xorm:"INDEX UNIQUE(s)"`
 	Uid      int64 `xorm:"INDEX UNIQUE(s)"`
-	OrgId    int64 `xorm:"INDEX UNIQUE(s)"`
+	OrgID    int64 `xorm:"INDEX UNIQUE(s)"`
 	IsPublic bool
 	IsPublic bool
 	IsOwner  bool
 	IsOwner  bool
 	NumTeams int
 	NumTeams int
@@ -298,7 +292,7 @@ func ChangeOrgUserStatus(orgId, uid int64, public bool) error {
 	}
 	}
 
 
 	ou.IsPublic = public
 	ou.IsPublic = public
-	_, err = x.Id(ou.Id).AllCols().Update(ou)
+	_, err = x.Id(ou.ID).AllCols().Update(ou)
 	return err
 	return err
 }
 }
 
 
@@ -316,7 +310,7 @@ func AddOrgUser(orgId, uid int64) error {
 
 
 	ou := &OrgUser{
 	ou := &OrgUser{
 		Uid:   uid,
 		Uid:   uid,
-		OrgId: orgId,
+		OrgID: orgId,
 	}
 	}
 
 
 	if _, err := sess.Insert(ou); err != nil {
 	if _, err := sess.Insert(ou); err != nil {
@@ -367,7 +361,7 @@ func RemoveOrgUser(orgId, uid int64) error {
 		return err
 		return err
 	}
 	}
 
 
-	if _, err := sess.Id(ou.Id).Delete(ou); err != nil {
+	if _, err := sess.Id(ou.ID).Delete(ou); err != nil {
 		sess.Rollback()
 		sess.Rollback()
 		return err
 		return err
 	} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgId); err != nil {
 	} else if _, err = sess.Exec("UPDATE `user` SET num_members = num_members - 1 WHERE id = ?", orgId); err != nil {
@@ -400,7 +394,7 @@ func RemoveOrgUser(orgId, uid int64) error {
 		return err
 		return err
 	}
 	}
 	for _, t := range ts {
 	for _, t := range ts {
-		if err = removeTeamMember(sess, org.Id, t.Id, u.Id); err != nil {
+		if err = removeTeamMember(sess, org.Id, t.ID, u.Id); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
@@ -419,13 +413,12 @@ const OWNER_TEAM = "Owners"
 
 
 // Team represents a organization team.
 // Team represents a organization team.
 type Team struct {
 type Team struct {
-	Id          int64
-	OrgId       int64 `xorm:"INDEX"`
+	ID          int64 `xorm:"pk autoincr"`
+	OrgID       int64 `xorm:"INDEX"`
 	LowerName   string
 	LowerName   string
 	Name        string
 	Name        string
 	Description string
 	Description string
 	Authorize   AccessMode
 	Authorize   AccessMode
-	RepoIds     string        `xorm:"TEXT"`
 	Repos       []*Repository `xorm:"-"`
 	Repos       []*Repository `xorm:"-"`
 	Members     []*User       `xorm:"-"`
 	Members     []*User       `xorm:"-"`
 	NumRepos    int
 	NumRepos    int
@@ -439,23 +432,20 @@ func (t *Team) IsOwnerTeam() bool {
 
 
 // IsTeamMember returns true if given user is a member of team.
 // IsTeamMember returns true if given user is a member of team.
 func (t *Team) IsMember(uid int64) bool {
 func (t *Team) IsMember(uid int64) bool {
-	return IsTeamMember(t.OrgId, t.Id, uid)
+	return IsTeamMember(t.OrgID, t.ID, uid)
 }
 }
 
 
-func (t *Team) getRepositories(e Engine) error {
-	idStrs := strings.Split(t.RepoIds, "|")
-	t.Repos = make([]*Repository, 0, len(idStrs))
-	for _, str := range idStrs {
-		if len(str) == 0 {
-			continue
-		}
-		id := com.StrTo(str[1:]).MustInt64()
-		if id == 0 {
-			continue
-		}
-		repo, err := getRepositoryById(e, id)
+func (t *Team) getRepositories(e Engine) (err error) {
+	teamRepos := make([]*TeamRepo, 0, t.NumRepos)
+	if err = x.Where("team_id=?", t.ID).Find(&teamRepos); err != nil {
+		return fmt.Errorf("get team-repos: %v", err)
+	}
+
+	t.Repos = make([]*Repository, 0, len(teamRepos))
+	for i := range teamRepos {
+		repo, err := getRepositoryById(e, teamRepos[i].RepoID)
 		if err != nil {
 		if err != nil {
-			return err
+			return fmt.Errorf("getRepositoryById(%d): %v", teamRepos[i].RepoID, err)
 		}
 		}
 		t.Repos = append(t.Repos, repo)
 		t.Repos = append(t.Repos, repo)
 	}
 	}
@@ -468,7 +458,7 @@ func (t *Team) GetRepositories() error {
 }
 }
 
 
 func (t *Team) getMembers(e Engine) (err error) {
 func (t *Team) getMembers(e Engine) (err error) {
-	t.Members, err = getTeamMembers(e, t.Id)
+	t.Members, err = getTeamMembers(e, t.ID)
 	return err
 	return err
 }
 }
 
 
@@ -479,96 +469,121 @@ func (t *Team) GetMembers() (err error) {
 
 
 // AddMember adds new member to team of organization.
 // AddMember adds new member to team of organization.
 func (t *Team) AddMember(uid int64) error {
 func (t *Team) AddMember(uid int64) error {
-	return AddTeamMember(t.OrgId, t.Id, uid)
+	return AddTeamMember(t.OrgID, t.ID, uid)
 }
 }
 
 
 // RemoveMember removes member from team of organization.
 // RemoveMember removes member from team of organization.
 func (t *Team) RemoveMember(uid int64) error {
 func (t *Team) RemoveMember(uid int64) error {
-	return RemoveTeamMember(t.OrgId, t.Id, uid)
+	return RemoveTeamMember(t.OrgID, t.ID, uid)
 }
 }
 
 
-// AddRepository adds new repository to team of organization.
-func (t *Team) AddRepository(repo *Repository) (err error) {
-	idStr := "$" + com.ToStr(repo.Id) + "|"
-	if repo.OwnerId != t.OrgId {
-		return errors.New("Repository not belong to organization")
-	} else if strings.Contains(t.RepoIds, idStr) {
-		return nil
-	}
+func (t *Team) hasRepository(e Engine, repoID int64) bool {
+	return hasTeamRepo(e, t.OrgID, t.ID, repoID)
+}
 
 
-	if err = repo.GetOwner(); err != nil {
-		return err
-	} else if err = t.GetMembers(); err != nil {
-		return err
-	}
+// HasRepository returns true if given repository belong to team.
+func (t *Team) HasRepository(repoID int64) bool {
+	return HasTeamRepo(t.OrgID, t.ID, repoID)
+}
 
 
-	sess := x.NewSession()
-	defer sessionRelease(sess)
-	if err = sess.Begin(); err != nil {
+func (t *Team) addRepository(e Engine, repo *Repository) (err error) {
+	if err = addTeamRepo(e, t.OrgID, t.ID, repo.Id); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	t.NumRepos++
 	t.NumRepos++
-	t.RepoIds += idStr
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if err = repo.recalculateAccesses(sess); err != nil {
+	if err = repo.recalculateAccesses(e); err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	if err = t.getMembers(e); err != nil {
+		return fmt.Errorf("get team members: %v", err)
+	}
 	for _, u := range t.Members {
 	for _, u := range t.Members {
-		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
+		if err = watchRepo(e, u.Id, repo.Id, true); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
-	return sess.Commit()
-}
-
-func (t *Team) HasRepository(repo *Repository) bool {
-	idStr := "$" + com.ToStr(repo.Id) + "|"
-	return strings.Contains(t.RepoIds, idStr)
+	return nil
 }
 }
 
 
-// RemoveRepository removes repository from team of organization.
-func (t *Team) RemoveRepository(repoId int64) error {
-	idStr := "$" + com.ToStr(repoId) + "|"
-	if !strings.Contains(t.RepoIds, idStr) {
+// AddRepository adds new repository to team of organization.
+func (t *Team) AddRepository(repo *Repository) (err error) {
+	if repo.OwnerId != t.OrgID {
+		return errors.New("Repository does not belong to organization")
+	} else if t.HasRepository(repo.Id) {
 		return nil
 		return nil
 	}
 	}
 
 
-	repo, err := GetRepositoryById(repoId)
-	if err != nil {
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if err = repo.GetOwner(); err != nil {
-		return err
-	} else if err = t.GetMembers(); err != nil {
+	if err = t.addRepository(sess, repo); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	sess := x.NewSession()
-	defer sessionRelease(sess)
-	if err = sess.Begin(); err != nil {
+	return sess.Commit()
+}
+
+func (t *Team) removeRepository(e Engine, repo *Repository) (err error) {
+	if err = removeTeamRepo(e, t.ID, repo.Id); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	t.NumRepos--
 	t.NumRepos--
-	t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1)
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+	if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	if err = repo.recalculateAccesses(sess); err != nil {
+	if err = repo.recalculateAccesses(e); err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	if err = t.getMembers(e); err != nil {
+		return fmt.Errorf("get team members: %v", err)
+	}
 	for _, u := range t.Members {
 	for _, u := range t.Members {
-		if err = watchRepo(sess, u.Id, repo.Id, false); err != nil {
+		has, err := hasAccess(e, u, repo, ACCESS_MODE_READ)
+		if err != nil {
 			return err
 			return err
+		} else if has {
+			continue
 		}
 		}
+
+		if err = watchRepo(e, u.Id, repo.Id, false); err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// RemoveRepository removes repository from team of organization.
+func (t *Team) RemoveRepository(repoID int64) error {
+	if !t.HasRepository(repoID) {
+		return nil
+	}
+
+	repo, err := GetRepositoryById(repoID)
+	if err != nil {
+		return err
+	}
+
+	sess := x.NewSession()
+	defer sessionRelease(sess)
+	if err = sess.Begin(); err != nil {
+		return err
+	}
+
+	if err = t.removeRepository(sess, repo); err != nil {
+		return err
 	}
 	}
 
 
 	return sess.Commit()
 	return sess.Commit()
@@ -581,7 +596,7 @@ func NewTeam(t *Team) error {
 		return ErrTeamNameIllegal
 		return ErrTeamNameIllegal
 	}
 	}
 
 
-	has, err := x.Id(t.OrgId).Get(new(User))
+	has, err := x.Id(t.OrgID).Get(new(User))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	} else if !has {
 	} else if !has {
@@ -589,7 +604,7 @@ func NewTeam(t *Team) error {
 	}
 	}
 
 
 	t.LowerName = strings.ToLower(t.Name)
 	t.LowerName = strings.ToLower(t.Name)
-	has, err = x.Where("org_id=?", t.OrgId).And("lower_name=?", t.LowerName).Get(new(Team))
+	has, err = x.Where("org_id=?", t.OrgID).And("lower_name=?", t.LowerName).Get(new(Team))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	} else if has {
 	} else if has {
@@ -608,7 +623,7 @@ func NewTeam(t *Team) error {
 	}
 	}
 
 
 	// Update organization number of teams.
 	// Update organization number of teams.
-	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgId); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams + 1 WHERE id = ?", t.OrgID); err != nil {
 		sess.Rollback()
 		sess.Rollback()
 		return err
 		return err
 	}
 	}
@@ -617,7 +632,7 @@ func NewTeam(t *Team) error {
 
 
 func getTeam(e Engine, orgId int64, name string) (*Team, error) {
 func getTeam(e Engine, orgId int64, name string) (*Team, error) {
 	t := &Team{
 	t := &Team{
-		OrgId:     orgId,
+		OrgID:     orgId,
 		LowerName: strings.ToLower(name),
 		LowerName: strings.ToLower(name),
 	}
 	}
 	has, err := e.Get(t)
 	has, err := e.Get(t)
@@ -667,7 +682,7 @@ func UpdateTeam(t *Team, authChanged bool) (err error) {
 	}
 	}
 
 
 	t.LowerName = strings.ToLower(t.Name)
 	t.LowerName = strings.ToLower(t.Name)
-	if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+	if _, err = sess.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -697,7 +712,7 @@ func DeleteTeam(t *Team) error {
 	}
 	}
 
 
 	// Get organization.
 	// Get organization.
-	org, err := GetUserById(t.OrgId)
+	org, err := GetUserById(t.OrgID)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
@@ -716,16 +731,16 @@ func DeleteTeam(t *Team) error {
 	}
 	}
 
 
 	// Delete team-user.
 	// Delete team-user.
-	if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.Id).Delete(new(TeamUser)); err != nil {
+	if _, err = sess.Where("org_id=?", org.Id).Where("team_id=?", t.ID).Delete(new(TeamUser)); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// Delete team.
 	// Delete team.
-	if _, err = sess.Id(t.Id).Delete(new(Team)); err != nil {
+	if _, err = sess.Id(t.ID).Delete(new(Team)); err != nil {
 		return err
 		return err
 	}
 	}
 	// Update organization number of teams.
 	// Update organization number of teams.
-	if _, err = sess.Exec("UPDATE `user` SET num_teams = num_teams - 1 WHERE id = ?", t.OrgId); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_teams=num_teams-1 WHERE id=?", t.OrgID); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -741,20 +756,20 @@ func DeleteTeam(t *Team) error {
 
 
 // TeamUser represents an team-user relation.
 // TeamUser represents an team-user relation.
 type TeamUser struct {
 type TeamUser struct {
-	Id     int64
-	Uid    int64
-	OrgId  int64 `xorm:"INDEX"`
-	TeamId int64
+	ID     int64 `xorm:"pk autoincr"`
+	OrgID  int64 `xorm:"INDEX"`
+	TeamID int64 `xorm:"UNIQUE(s)"`
+	Uid    int64 `xorm:"UNIQUE(s)"`
 }
 }
 
 
-func isTeamMember(e Engine, orgId, teamId, uid int64) bool {
-	has, _ := e.Where("uid=?", uid).And("org_id=?", orgId).And("team_id=?", teamId).Get(new(TeamUser))
+func isTeamMember(e Engine, orgID, teamID, uid int64) bool {
+	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("uid=?", uid).Get(new(TeamUser))
 	return has
 	return has
 }
 }
 
 
 // IsTeamMember returns true if given user is a member of team.
 // IsTeamMember returns true if given user is a member of team.
-func IsTeamMember(orgId, teamId, uid int64) bool {
-	return isTeamMember(x, orgId, teamId, uid)
+func IsTeamMember(orgID, teamID, uid int64) bool {
+	return isTeamMember(x, orgID, teamID, uid)
 }
 }
 
 
 func getTeamMembers(e Engine, teamID int64) ([]*User, error) {
 func getTeamMembers(e Engine, teamID int64) ([]*User, error) {
@@ -777,7 +792,7 @@ func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
 	ts := make([]*Team, len(tus))
 	ts := make([]*Team, len(tus))
 	for i, tu := range tus {
 	for i, tu := range tus {
 		t := new(Team)
 		t := new(Team)
-		has, err := e.Id(tu.TeamId).Get(t)
+		has, err := e.Id(tu.TeamID).Get(t)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		} else if !has {
 		} else if !has {
@@ -822,13 +837,13 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 
 
 	tu := &TeamUser{
 	tu := &TeamUser{
 		Uid:    uid,
 		Uid:    uid,
-		OrgId:  orgId,
-		TeamId: teamId,
+		OrgID:  orgId,
+		TeamID: teamId,
 	}
 	}
 
 
 	if _, err = sess.Insert(tu); err != nil {
 	if _, err = sess.Insert(tu); err != nil {
 		return err
 		return err
-	} else if _, err = sess.Id(t.Id).Update(t); err != nil {
+	} else if _, err = sess.Id(t.ID).Update(t); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -848,7 +863,7 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 	if t.IsOwnerTeam() {
 	if t.IsOwnerTeam() {
 		ou.IsOwner = true
 		ou.IsOwner = true
 	}
 	}
-	if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil {
+	if _, err = sess.Id(ou.ID).AllCols().Update(ou); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -885,13 +900,13 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
 
 
 	tu := &TeamUser{
 	tu := &TeamUser{
 		Uid:    uid,
 		Uid:    uid,
-		OrgId:  orgId,
-		TeamId: teamId,
+		OrgID:  orgId,
+		TeamID: teamId,
 	}
 	}
 
 
 	if _, err := e.Delete(tu); err != nil {
 	if _, err := e.Delete(tu); err != nil {
 		return err
 		return err
-	} else if _, err = e.Id(t.Id).AllCols().Update(t); err != nil {
+	} else if _, err = e.Id(t.ID).AllCols().Update(t); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -912,7 +927,7 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
 	if t.IsOwnerTeam() {
 	if t.IsOwnerTeam() {
 		ou.IsOwner = false
 		ou.IsOwner = false
 	}
 	}
-	if _, err = e.Id(ou.Id).AllCols().Update(ou); err != nil {
+	if _, err = e.Id(ou.ID).AllCols().Update(ou); err != nil {
 		return err
 		return err
 	}
 	}
 	return nil
 	return nil
@@ -921,13 +936,64 @@ func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
 // RemoveTeamMember removes member from given team of given organization.
 // RemoveTeamMember removes member from given team of given organization.
 func RemoveTeamMember(orgId, teamId, uid int64) error {
 func RemoveTeamMember(orgId, teamId, uid int64) error {
 	sess := x.NewSession()
 	sess := x.NewSession()
-	defer sess.Close()
+	defer sessionRelease(sess)
 	if err := sess.Begin(); err != nil {
 	if err := sess.Begin(); err != nil {
 		return err
 		return err
 	}
 	}
 	if err := removeTeamMember(sess, orgId, teamId, uid); err != nil {
 	if err := removeTeamMember(sess, orgId, teamId, uid); err != nil {
-		sess.Rollback()
 		return err
 		return err
 	}
 	}
 	return sess.Commit()
 	return sess.Commit()
 }
 }
+
+// ___________                  __________
+// \__    ___/___ _____    _____\______   \ ____ ______   ____
+//   |    |_/ __ \\__  \  /     \|       _// __ \\____ \ /  _ \
+//   |    |\  ___/ / __ \|  Y Y  \    |   \  ___/|  |_> >  <_> )
+//   |____| \___  >____  /__|_|  /____|_  /\___  >   __/ \____/
+//              \/     \/      \/       \/     \/|__|
+
+// TeamRepo represents an team-repository relation.
+type TeamRepo struct {
+	ID     int64 `xorm:"pk autoincr"`
+	OrgID  int64 `xorm:"INDEX"`
+	TeamID int64 `xorm:"UNIQUE(s)"`
+	RepoID int64 `xorm:"UNIQUE(s)"`
+}
+
+func hasTeamRepo(e Engine, orgID, teamID, repoID int64) bool {
+	has, _ := e.Where("org_id=?", orgID).And("team_id=?", teamID).And("repo_id=?", repoID).Get(new(TeamRepo))
+	return has
+}
+
+// HasTeamRepo returns true if given repository belongs to team.
+func HasTeamRepo(orgID, teamID, repoID int64) bool {
+	return hasTeamRepo(x, orgID, teamID, repoID)
+}
+
+func addTeamRepo(e Engine, orgID, teamID, repoID int64) error {
+	_, err := e.InsertOne(&TeamRepo{
+		OrgID:  orgID,
+		TeamID: teamID,
+		RepoID: repoID,
+	})
+	return err
+}
+
+// AddTeamRepo adds new repository relation to team.
+func AddTeamRepo(orgID, teamID, repoID int64) error {
+	return addTeamRepo(x, orgID, teamID, repoID)
+}
+
+func removeTeamRepo(e Engine, teamID, repoID int64) error {
+	_, err := e.Delete(&TeamRepo{
+		TeamID: teamID,
+		RepoID: repoID,
+	})
+	return err
+}
+
+// RemoveTeamRepo deletes repository relation to team.
+func RemoveTeamRepo(teamID, repoID int64) error {
+	return removeTeamRepo(x, teamID, repoID)
+}

+ 70 - 74
models/repo.go

@@ -154,7 +154,6 @@ type Repository struct {
 
 
 	IsPrivate bool
 	IsPrivate bool
 	IsBare    bool
 	IsBare    bool
-	IsGoget   bool
 
 
 	IsMirror bool
 	IsMirror bool
 	*Mirror  `xorm:"-"`
 	*Mirror  `xorm:"-"`
@@ -515,7 +514,7 @@ func initRepository(e Engine, f string, u *User, repo *Repository, initReadme bo
 }
 }
 
 
 // CreateRepository creates a repository for given user or organization.
 // CreateRepository creates a repository for given user or organization.
-func CreateRepository(u *User, name, desc, lang, license string, private, mirror, initReadme bool) (*Repository, error) {
+func CreateRepository(u *User, name, desc, lang, license string, isPrivate, isMirror, initReadme bool) (*Repository, error) {
 	if !IsLegalName(name) {
 	if !IsLegalName(name) {
 		return nil, ErrRepoNameIllegal
 		return nil, ErrRepoNameIllegal
 	}
 	}
@@ -533,7 +532,7 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
 		Name:        name,
 		Name:        name,
 		LowerName:   strings.ToLower(name),
 		LowerName:   strings.ToLower(name),
 		Description: desc,
 		Description: desc,
-		IsPrivate:   private,
+		IsPrivate:   isPrivate,
 	}
 	}
 
 
 	sess := x.NewSession()
 	sess := x.NewSession()
@@ -559,34 +558,22 @@ func CreateRepository(u *User, name, desc, lang, license string, private, mirror
 		// Update owner team info and count.
 		// Update owner team info and count.
 		t, err := u.getOwnerTeam(sess)
 		t, err := u.getOwnerTeam(sess)
 		if err != nil {
 		if err != nil {
-			return nil, fmt.Errorf("get owner team: %v", err)
-		} else if err = t.getMembers(sess); err != nil {
-			return nil, fmt.Errorf("get team members: %v", err)
-		}
-
-		for _, u := range t.Members {
-			if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-				return nil, fmt.Errorf("watch repository: %v", err)
-			}
-		}
-
-		t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
-		t.NumRepos++
-		if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
-			return nil, err
+			return nil, fmt.Errorf("getOwnerTeam: %v", err)
+		} else if err = t.addRepository(sess, repo); err != nil {
+			return nil, fmt.Errorf("addRepository: %v", err)
 		}
 		}
 	} else {
 	} else {
 		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
 		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-			return nil, fmt.Errorf("watch repository 2: %v", err)
+			return nil, fmt.Errorf("watchRepo: %v", err)
 		}
 		}
 	}
 	}
 
 
 	if err = newRepoAction(sess, u, repo); err != nil {
 	if err = newRepoAction(sess, u, repo); err != nil {
-		return nil, fmt.Errorf("new repository action: %v", err)
+		return nil, fmt.Errorf("newRepoAction: %v", err)
 	}
 	}
 
 
 	// No need for init mirror.
 	// No need for init mirror.
-	if !mirror {
+	if !isMirror {
 		repoPath := RepoPath(u.Name, repo.Name)
 		repoPath := RepoPath(u.Name, repo.Name)
 		if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil {
 		if err = initRepository(sess, repoPath, u, repo, initReadme, lang, license); err != nil {
 			if err2 := os.RemoveAll(repoPath); err2 != nil {
 			if err2 := os.RemoveAll(repoPath); err2 != nil {
@@ -641,14 +628,14 @@ func RepoPath(userName, repoName string) string {
 }
 }
 
 
 // TransferOwnership transfers all corresponding setting from old user to new one.
 // TransferOwnership transfers all corresponding setting from old user to new one.
-func TransferOwnership(u *User, newOwner string, repo *Repository) error {
-	newUser, err := GetUserByName(newOwner)
+func TransferOwnership(u *User, newOwnerName string, repo *Repository) error {
+	newOwner, err := GetUserByName(newOwnerName)
 	if err != nil {
 	if err != nil {
-		return fmt.Errorf("fail to get new owner(%s): %v", newOwner, err)
+		return fmt.Errorf("get new owner(%s): %v", newOwnerName, err)
 	}
 	}
 
 
 	// Check if new owner has repository with same name.
 	// Check if new owner has repository with same name.
-	has, err := IsRepositoryExist(newUser, repo.Name)
+	has, err := IsRepositoryExist(newOwner, repo.Name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	} else if has {
 	} else if has {
@@ -664,27 +651,50 @@ func TransferOwnership(u *User, newOwner string, repo *Repository) error {
 	owner := repo.Owner
 	owner := repo.Owner
 
 
 	// Update repository.
 	// Update repository.
-	repo.OwnerId = newUser.Id
-	repo.Owner = newUser
+	repo.OwnerId = newOwner.Id
+	repo.Owner = newOwner
 	if _, err := sess.Id(repo.Id).Update(repo); err != nil {
 	if _, err := sess.Id(repo.Id).Update(repo); err != nil {
 		return err
 		return err
 	}
 	}
 
 
+	// Remove redundant collaborators
+	collaborators, err := repo.GetCollaborators()
+	if err != nil {
+		return err
+	}
+	for _, c := range collaborators {
+		if c.Id == newOwner.Id || newOwner.IsOrgMember(c.Id) {
+			if _, err = sess.Delete(&Collaboration{RepoID: repo.Id, UserID: c.Id}); err != nil {
+				return err
+			}
+		}
+	}
+
+	if newOwner.IsOrganization() {
+		// Update owner team info and count.
+		t, err := newOwner.GetOwnerTeam()
+		if err != nil {
+			return err
+		} else if err = t.addRepository(sess, repo); err != nil {
+			return err
+		}
+	}
+
 	// Update user repository number.
 	// Update user repository number.
-	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newUser.Id); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos + 1 WHERE id = ?", newOwner.Id); err != nil {
 		return err
 		return err
 	} else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
 	} else if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", owner.Id); err != nil {
 		return err
 		return err
 	} else if err = repo.recalculateAccesses(sess); err != nil {
 	} else if err = repo.recalculateAccesses(sess); err != nil {
 		return err
 		return err
-	} else if err = watchRepo(sess, newUser.Id, repo.Id, true); err != nil {
+	} else if err = watchRepo(sess, newOwner.Id, repo.Id, true); err != nil {
 		return err
 		return err
-	} else if err = transferRepoAction(sess, u, newUser, repo); err != nil {
+	} else if err = transferRepoAction(sess, u, owner, newOwner, repo); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// Change repository directory name.
 	// Change repository directory name.
-	if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newUser.Name, repo.Name)); err != nil {
+	if err = os.Rename(RepoPath(owner.Name, repo.Name), RepoPath(newOwner.Name, repo.Name)); err != nil {
 		return err
 		return err
 	}
 	}
 
 
@@ -722,8 +732,8 @@ func UpdateRepository(repo *Repository) error {
 }
 }
 
 
 // DeleteRepository deletes a repository for a user or organization.
 // DeleteRepository deletes a repository for a user or organization.
-func DeleteRepository(uid, repoId int64, userName string) error {
-	repo := &Repository{Id: repoId, OwnerId: uid}
+func DeleteRepository(uid, repoID int64, userName string) error {
+	repo := &Repository{Id: repoID, OwnerId: uid}
 	has, err := x.Get(repo)
 	has, err := x.Get(repo)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -749,68 +759,66 @@ func DeleteRepository(uid, repoId int64, userName string) error {
 	}
 	}
 
 
 	if org.IsOrganization() {
 	if org.IsOrganization() {
-		idStr := "$" + com.ToStr(repoId) + "|"
 		for _, t := range org.Teams {
 		for _, t := range org.Teams {
-			if !strings.Contains(t.RepoIds, idStr) {
+			if !t.hasRepository(sess, repoID) {
 				continue
 				continue
-			}
-			t.NumRepos--
-			t.RepoIds = strings.Replace(t.RepoIds, idStr, "", 1)
-			if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
+			} else if err = t.removeRepository(sess, repo); err != nil {
 				return err
 				return err
 			}
 			}
 		}
 		}
 	}
 	}
 
 
-	if _, err = sess.Delete(&Repository{Id: repoId}); err != nil {
+	if _, err = sess.Delete(&Repository{Id: repoID}); err != nil {
 		return err
 		return err
 	} else if _, err := sess.Delete(&Access{RepoID: repo.Id}); err != nil {
 	} else if _, err := sess.Delete(&Access{RepoID: repo.Id}); err != nil {
 		return err
 		return err
 	} else if _, err := sess.Delete(&Action{RepoId: repo.Id}); err != nil {
 	} else if _, err := sess.Delete(&Action{RepoId: repo.Id}); err != nil {
 		return err
 		return err
-	} else if _, err = sess.Delete(&Watch{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Watch{RepoId: repoID}); err != nil {
 		return err
 		return err
-	} else if _, err = sess.Delete(&Mirror{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Mirror{RepoId: repoID}); err != nil {
 		return err
 		return err
-	} else if _, err = sess.Delete(&IssueUser{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&IssueUser{RepoId: repoID}); err != nil {
 		return err
 		return err
-	} else if _, err = sess.Delete(&Milestone{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Milestone{RepoId: repoID}); err != nil {
 		return err
 		return err
-	} else if _, err = sess.Delete(&Release{RepoId: repoId}); err != nil {
+	} else if _, err = sess.Delete(&Release{RepoId: repoID}); err != nil {
+		return err
+	} else if _, err = sess.Delete(&Collaboration{RepoID: repoID}); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// Delete comments.
 	// Delete comments.
-	if err = x.Iterate(&Issue{RepoId: repoId}, func(idx int, bean interface{}) error {
-		issue := bean.(*Issue)
-		if _, err = sess.Delete(&Comment{IssueId: issue.Id}); err != nil {
+	issues := make([]*Issue, 0, 25)
+	if err = sess.Where("repo_id=?", repoID).Find(&issues); err != nil {
+		return err
+	}
+	for i := range issues {
+		if _, err = sess.Delete(&Comment{IssueId: issues[i].Id}); err != nil {
 			return err
 			return err
 		}
 		}
-		return nil
-	}); err != nil {
-		return err
 	}
 	}
 
 
-	if _, err = sess.Delete(&Issue{RepoId: repoId}); err != nil {
+	if _, err = sess.Delete(&Issue{RepoId: repoID}); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	if repo.IsFork {
 	if repo.IsFork {
-		if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks - 1 WHERE id = ?", repo.ForkId); err != nil {
+		if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks-1 WHERE id=?", repo.ForkId); err != nil {
 			return err
 			return err
 		}
 		}
 	}
 	}
 
 
-	if _, err = sess.Exec("UPDATE `user` SET num_repos = num_repos - 1 WHERE id = ?", uid); err != nil {
+	if _, err = sess.Exec("UPDATE `user` SET num_repos=num_repos-1 WHERE id=?", uid); err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// Remove repository files.
 	// Remove repository files.
 	if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
 	if err = os.RemoveAll(RepoPath(userName, repo.Name)); err != nil {
-		desc := fmt.Sprintf("Fail to delete repository files(%s/%s): %v", userName, repo.Name, err)
+		desc := fmt.Sprintf("delete repository files(%s/%s): %v", userName, repo.Name, err)
 		log.Warn(desc)
 		log.Warn(desc)
 		if err = CreateRepositoryNotice(desc); err != nil {
 		if err = CreateRepositoryNotice(desc); err != nil {
-			log.Error(4, "Fail to add notice: %v", err)
+			log.Error(4, "add notice: %v", err)
 		}
 		}
 	}
 	}
 
 
@@ -1314,33 +1322,21 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor
 		// Update owner team info and count.
 		// Update owner team info and count.
 		t, err := u.getOwnerTeam(sess)
 		t, err := u.getOwnerTeam(sess)
 		if err != nil {
 		if err != nil {
-			return nil, fmt.Errorf("get owner team: %v", err)
-		} else if err = t.getMembers(sess); err != nil {
-			return nil, fmt.Errorf("get team members: %v", err)
-		}
-
-		for _, u := range t.Members {
-			if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-				return nil, fmt.Errorf("watch repository: %v", err)
-			}
-		}
-
-		t.RepoIds += "$" + com.ToStr(repo.Id) + "|"
-		t.NumRepos++
-		if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
-			return nil, err
+			return nil, fmt.Errorf("getOwnerTeam: %v", err)
+		} else if err = t.addRepository(sess, repo); err != nil {
+			return nil, fmt.Errorf("addRepository: %v", err)
 		}
 		}
 	} else {
 	} else {
 		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
 		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
-			return nil, fmt.Errorf("watch repository 2: %v", err)
+			return nil, fmt.Errorf("watchRepo: %v", err)
 		}
 		}
 	}
 	}
 
 
 	if err = newRepoAction(sess, u, repo); err != nil {
 	if err = newRepoAction(sess, u, repo); err != nil {
-		return nil, fmt.Errorf("new repository action: %v", err)
+		return nil, fmt.Errorf("newRepoAction: %v", err)
 	}
 	}
 
 
-	if _, err = sess.Exec("UPDATE `repository` SET num_forks = num_forks + 1 WHERE id = ?", oldRepo.Id); err != nil {
+	if _, err = sess.Exec("UPDATE `repository` SET num_forks=num_forks+1 WHERE id=?", oldRepo.Id); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 

+ 14 - 9
models/user.go

@@ -231,7 +231,7 @@ func (u *User) GetOrganizations() error {
 
 
 	u.Orgs = make([]*User, len(ous))
 	u.Orgs = make([]*User, len(ous))
 	for i, ou := range ous {
 	for i, ou := range ous {
-		u.Orgs[i], err = GetUserById(ou.OrgId)
+		u.Orgs[i], err = GetUserById(ou.OrgID)
 		if err != nil {
 		if err != nil {
 			return err
 			return err
 		}
 		}
@@ -249,11 +249,13 @@ func (u *User) GetFullNameFallback() string {
 
 
 // IsUserExist checks if given user name exist,
 // IsUserExist checks if given user name exist,
 // the user name should be noncased unique.
 // the user name should be noncased unique.
-func IsUserExist(name string) (bool, error) {
+// If uid is presented, then check will rule out that one,
+// it is used when update a user name in settings page.
+func IsUserExist(uid int64, name string) (bool, error) {
 	if len(name) == 0 {
 	if len(name) == 0 {
 		return false, nil
 		return false, nil
 	}
 	}
-	return x.Get(&User{LowerName: strings.ToLower(name)})
+	return x.Where("id!=?", uid).Get(&User{LowerName: strings.ToLower(name)})
 }
 }
 
 
 // IsEmailUsed returns true if the e-mail has been used.
 // IsEmailUsed returns true if the e-mail has been used.
@@ -278,7 +280,7 @@ func CreateUser(u *User) error {
 		return ErrUserNameIllegal
 		return ErrUserNameIllegal
 	}
 	}
 
 
-	isExist, err := IsUserExist(u.Name)
+	isExist, err := IsUserExist(0, u.Name)
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	} else if isExist {
 	} else if isExist {
@@ -401,7 +403,7 @@ func ChangeUserName(u *User, newUserName string) (err error) {
 
 
 // UpdateUser updates user's information.
 // UpdateUser updates user's information.
 func UpdateUser(u *User) error {
 func UpdateUser(u *User) error {
-	has, err := x.Where("id!=?", u.Id).And("type=?", INDIVIDUAL).And("email=?", u.Email).Get(new(User))
+	has, err := x.Where("id!=?", u.Id).And("type=?", u.Type).And("email=?", u.Email).Get(new(User))
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	} else if has {
 	} else if has {
@@ -578,7 +580,7 @@ func GetUserIdsByNames(names []string) []int64 {
 	return ids
 	return ids
 }
 }
 
 
-// Get all email addresses
+// GetEmailAddresses returns all e-mail addresses belongs to given user.
 func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
 func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
 	emails := make([]*EmailAddress, 0, 5)
 	emails := make([]*EmailAddress, 0, 5)
 	err := x.Where("uid=?", uid).Find(&emails)
 	err := x.Where("uid=?", uid).Find(&emails)
@@ -592,7 +594,6 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
 	}
 	}
 
 
 	isPrimaryFound := false
 	isPrimaryFound := false
-
 	for _, email := range emails {
 	for _, email := range emails {
 		if email.Email == u.Email {
 		if email.Email == u.Email {
 			isPrimaryFound = true
 			isPrimaryFound = true
@@ -605,7 +606,11 @@ func GetEmailAddresses(uid int64) ([]*EmailAddress, error) {
 	// We alway want the primary email address displayed, even if it's not in
 	// We alway want the primary email address displayed, even if it's not in
 	// the emailaddress table (yet)
 	// the emailaddress table (yet)
 	if !isPrimaryFound {
 	if !isPrimaryFound {
-		emails = append(emails, &EmailAddress{Email: u.Email, IsActivated: true, IsPrimary: true})
+		emails = append(emails, &EmailAddress{
+			Email:       u.Email,
+			IsActivated: true,
+			IsPrimary:   true,
+		})
 	}
 	}
 	return emails, nil
 	return emails, nil
 }
 }
@@ -855,7 +860,7 @@ func UpdateMentions(userNames []string, issueId int64) error {
 		}
 		}
 
 
 		for _, orgUser := range orgUsers {
 		for _, orgUser := range orgUsers {
-			tempIds = append(tempIds, orgUser.Id)
+			tempIds = append(tempIds, orgUser.ID)
 		}
 		}
 
 
 		ids = append(ids, tempIds...)
 		ids = append(ids, tempIds...)

+ 1 - 2
modules/auth/repo_form.go

@@ -31,7 +31,7 @@ func (f *CreateRepoForm) Validate(ctx *macaron.Context, errs binding.Errors) bin
 }
 }
 
 
 type MigrateRepoForm struct {
 type MigrateRepoForm struct {
-	HttpsUrl     string `form:"url" binding:"Required;Url"`
+	CloneAddr    string `binding:"Required"`
 	AuthUserName string `form:"auth_username"`
 	AuthUserName string `form:"auth_username"`
 	AuthPasswd   string `form:"auth_password"`
 	AuthPasswd   string `form:"auth_password"`
 	Uid          int64  `form:"uid" binding:"Required"`
 	Uid          int64  `form:"uid" binding:"Required"`
@@ -52,7 +52,6 @@ type RepoSettingForm struct {
 	Branch      string `form:"branch"`
 	Branch      string `form:"branch"`
 	Interval    int    `form:"interval"`
 	Interval    int    `form:"interval"`
 	Private     bool   `form:"private"`
 	Private     bool   `form:"private"`
-	GoGet       bool   `form:"goget"`
 }
 }
 
 
 func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 func (f *RepoSettingForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

+ 1 - 1
modules/auth/user_form.go

@@ -99,7 +99,7 @@ func (f *UploadAvatarForm) Validate(ctx *macaron.Context, errs binding.Errors) b
 }
 }
 
 
 type AddEmailForm struct {
 type AddEmailForm struct {
-	Email string `form:"email" binding:"Required;Email;MaxSize(50)"`
+	Email string `binding:"Required;Email;MaxSize(50)"`
 }
 }
 
 
 func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
 func (f *AddEmailForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {

+ 12 - 2
modules/base/template.go

@@ -41,6 +41,10 @@ func List(l *list.List) chan interface{} {
 	return c
 	return c
 }
 }
 
 
+func Sha1(str string) string {
+	return EncodeSha1(str)
+}
+
 func ShortSha(sha1 string) string {
 func ShortSha(sha1 string) string {
 	if len(sha1) == 40 {
 	if len(sha1) == 40 {
 		return sha1[:10]
 		return sha1[:10]
@@ -126,8 +130,13 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
 		return a + b
 		return a + b
 	},
 	},
 	"ActionIcon": ActionIcon,
 	"ActionIcon": ActionIcon,
-	"DateFormat": DateFormat,
-	"List":       List,
+	"DateFmtLong": func(t time.Time) string {
+		return t.Format(time.RFC1123Z)
+	},
+	"DateFmtShort": func(t time.Time) string {
+		return t.Format("Jan 02, 2006")
+	},
+	"List": List,
 	"Mail2Domain": func(mail string) string {
 	"Mail2Domain": func(mail string) string {
 		if !strings.Contains(mail, "@") {
 		if !strings.Contains(mail, "@") {
 			return "try.gogs.io"
 			return "try.gogs.io"
@@ -155,6 +164,7 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
 	},
 	},
 	"DiffTypeToStr":     DiffTypeToStr,
 	"DiffTypeToStr":     DiffTypeToStr,
 	"DiffLineTypeToStr": DiffLineTypeToStr,
 	"DiffLineTypeToStr": DiffLineTypeToStr,
+	"Sha1":	             Sha1,
 	"ShortSha":          ShortSha,
 	"ShortSha":          ShortSha,
 	"Md5":               EncodeMd5,
 	"Md5":               EncodeMd5,
 	"ActionContent2Commits": ActionContent2Commits,
 	"ActionContent2Commits": ActionContent2Commits,

+ 6 - 61
modules/base/tool.go

@@ -126,7 +126,7 @@ func VerifyTimeLimitCode(data string, minutes int, code string) bool {
 	retCode := CreateTimeLimitCode(data, minutes, start)
 	retCode := CreateTimeLimitCode(data, minutes, start)
 	if retCode == code && minutes > 0 {
 	if retCode == code && minutes > 0 {
 		// check time is expired or not
 		// check time is expired or not
-		before, _ := DateParse(start, "YmdHi")
+		before, _ := time.ParseInLocation("200601021504", start, time.Local)
 		now := time.Now()
 		now := time.Now()
 		if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
 		if before.Add(time.Minute*time.Duration(minutes)).Unix() > now.Unix() {
 			return true
 			return true
@@ -141,7 +141,7 @@ const TimeLimitCodeLength = 12 + 6 + 40
 // create a time limit code
 // create a time limit code
 // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
 // code format: 12 length date time string + 6 minutes string + 40 sha1 encoded string
 func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
 func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string {
-	format := "YmdHi"
+	format := "200601021504"
 
 
 	var start, end time.Time
 	var start, end time.Time
 	var startStr, endStr string
 	var startStr, endStr string
@@ -149,16 +149,16 @@ func CreateTimeLimitCode(data string, minutes int, startInf interface{}) string
 	if startInf == nil {
 	if startInf == nil {
 		// Use now time create code
 		// Use now time create code
 		start = time.Now()
 		start = time.Now()
-		startStr = DateFormat(start, format)
+		startStr = start.Format(format)
 	} else {
 	} else {
 		// use start string create code
 		// use start string create code
 		startStr = startInf.(string)
 		startStr = startInf.(string)
-		start, _ = DateParse(startStr, format)
-		startStr = DateFormat(start, format)
+		start, _ = time.ParseInLocation(format, startStr, time.Local)
+		startStr = start.Format(format)
 	}
 	}
 
 
 	end = start.Add(time.Minute * time.Duration(minutes))
 	end = start.Add(time.Minute * time.Duration(minutes))
-	endStr = DateFormat(end, format)
+	endStr = end.Format(format)
 
 
 	// create sha1 encode string
 	// create sha1 encode string
 	sh := sha1.New()
 	sh := sha1.New()
@@ -420,58 +420,3 @@ func Subtract(left interface{}, right interface{}) interface{} {
 		return fleft + float64(rleft) - (fright + float64(rright))
 		return fleft + float64(rleft) - (fright + float64(rright))
 	}
 	}
 }
 }
-
-// DateFormat pattern rules.
-var datePatterns = []string{
-	// year
-	"Y", "2006", // A full numeric representation of a year, 4 digits   Examples: 1999 or 2003
-	"y", "06", //A two digit representation of a year   Examples: 99 or 03
-
-	// month
-	"m", "01", // Numeric representation of a month, with leading zeros 01 through 12
-	"n", "1", // Numeric representation of a month, without leading zeros   1 through 12
-	"M", "Jan", // A short textual representation of a month, three letters Jan through Dec
-	"F", "January", // A full textual representation of a month, such as January or March   January through December
-
-	// day
-	"d", "02", // Day of the month, 2 digits with leading zeros 01 to 31
-	"j", "2", // Day of the month without leading zeros 1 to 31
-
-	// week
-	"D", "Mon", // A textual representation of a day, three letters Mon through Sun
-	"l", "Monday", // A full textual representation of the day of the week  Sunday through Saturday
-
-	// time
-	"g", "3", // 12-hour format of an hour without leading zeros    1 through 12
-	"G", "15", // 24-hour format of an hour without leading zeros   0 through 23
-	"h", "03", // 12-hour format of an hour with leading zeros  01 through 12
-	"H", "15", // 24-hour format of an hour with leading zeros  00 through 23
-
-	"a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm
-	"A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM
-
-	"i", "04", // Minutes with leading zeros    00 to 59
-	"s", "05", // Seconds, with leading zeros   00 through 59
-
-	// time zone
-	"T", "MST",
-	"P", "-07:00",
-	"O", "-0700",
-
-	// RFC 2822
-	"r", time.RFC1123Z,
-}
-
-// Parse Date use PHP time format.
-func DateParse(dateString, format string) (time.Time, error) {
-	replacer := strings.NewReplacer(datePatterns...)
-	format = replacer.Replace(format)
-	return time.ParseInLocation(format, dateString, time.Local)
-}
-
-// Date takes a PHP like date func to Go's time format.
-func DateFormat(t time.Time, format string) string {
-	replacer := strings.NewReplacer(datePatterns...)
-	format = replacer.Replace(format)
-	return t.Format(format)
-}

+ 20 - 9
modules/git/signature.go

@@ -17,24 +17,35 @@ type Signature struct {
 	When  time.Time
 	When  time.Time
 }
 }
 
 
-// Helper to get a signature from the commit line, which looks like this:
+// Helper to get a signature from the commit line, which looks like these:
 //     author Patrick Gundlach <[email protected]> 1378823654 +0200
 //     author Patrick Gundlach <[email protected]> 1378823654 +0200
+//     author Patrick Gundlach <[email protected]> Thu, 07 Apr 2005 22:13:13 +0200
 // but without the "author " at the beginning (this method should)
 // but without the "author " at the beginning (this method should)
 // be used for author and committer.
 // be used for author and committer.
 //
 //
-// FIXME: include timezone!
-func newSignatureFromCommitline(line []byte) (*Signature, error) {
+// FIXME: include timezone for timestamp!
+func newSignatureFromCommitline(line []byte) (_ *Signature, err error) {
 	sig := new(Signature)
 	sig := new(Signature)
 	emailstart := bytes.IndexByte(line, '<')
 	emailstart := bytes.IndexByte(line, '<')
 	sig.Name = string(line[:emailstart-1])
 	sig.Name = string(line[:emailstart-1])
 	emailstop := bytes.IndexByte(line, '>')
 	emailstop := bytes.IndexByte(line, '>')
 	sig.Email = string(line[emailstart+1 : emailstop])
 	sig.Email = string(line[emailstart+1 : emailstop])
-	timestop := bytes.IndexByte(line[emailstop+2:], ' ')
-	timestring := string(line[emailstop+2 : emailstop+2+timestop])
-	seconds, err := strconv.ParseInt(timestring, 10, 64)
-	if err != nil {
-		return nil, err
+
+	// Check date format.
+	firstChar := line[emailstop+2]
+	if firstChar >= 48 && firstChar <= 57 {
+		timestop := bytes.IndexByte(line[emailstop+2:], ' ')
+		timestring := string(line[emailstop+2 : emailstop+2+timestop])
+		seconds, err := strconv.ParseInt(timestring, 10, 64)
+		if err != nil {
+			return nil, err
+		}
+		sig.When = time.Unix(seconds, 0)
+	} else {
+		sig.When, err = time.Parse(time.RFC1123Z, string(line[emailstop+2:]))
+		if err != nil {
+			return nil, err
+		}
 	}
 	}
-	sig.When = time.Unix(seconds, 0)
 	return sig, nil
 	return sig, nil
 }
 }

+ 10 - 0
modules/mailer/mailer.go

@@ -10,6 +10,7 @@ import (
 	"net"
 	"net"
 	"net/mail"
 	"net/mail"
 	"net/smtp"
 	"net/smtp"
+	"os"
 	"strings"
 	"strings"
 
 
 	"github.com/gogits/gogs/modules/log"
 	"github.com/gogits/gogs/modules/log"
@@ -95,6 +96,15 @@ func sendMail(settings *setting.Mailer, recipients []string, msgContent []byte)
 		return err
 		return err
 	}
 	}
 
 
+	hostname, err := os.Hostname()
+	if err != nil {
+		return err
+	}
+
+	if err = client.Hello(hostname); err != nil {
+		return err
+	}
+
 	// If not using SMTPS, alway use STARTTLS if available
 	// If not using SMTPS, alway use STARTTLS if available
 	hasStartTLS, _ := client.Extension("STARTTLS")
 	hasStartTLS, _ := client.Extension("STARTTLS")
 	if !isSecureConn && hasStartTLS {
 	if !isSecureConn && hasStartTLS {

+ 44 - 23
modules/middleware/context.go

@@ -38,29 +38,7 @@ type Context struct {
 	IsSigned    bool
 	IsSigned    bool
 	IsBasicAuth bool
 	IsBasicAuth bool
 
 
-	Repo struct {
-		IsOwner      bool
-		IsTrueOwner  bool
-		IsWatching   bool
-		IsBranch     bool
-		IsTag        bool
-		IsCommit     bool
-		IsAdmin      bool // Current user is admin level.
-		HasAccess    bool
-		Repository   *models.Repository
-		Owner        *models.User
-		Commit       *git.Commit
-		Tag          *git.Tag
-		GitRepo      *git.Repository
-		BranchName   string
-		TagName      string
-		TreeName     string
-		CommitId     string
-		RepoLink     string
-		CloneLink    models.CloneLink
-		CommitsCount int
-		Mirror       *models.Mirror
-	}
+	Repo RepoContext
 
 
 	Org struct {
 	Org struct {
 		IsOwner      bool
 		IsOwner      bool
@@ -73,6 +51,37 @@ type Context struct {
 	}
 	}
 }
 }
 
 
+type RepoContext struct {
+	AccessMode   models.AccessMode
+	IsWatching   bool
+	IsBranch     bool
+	IsTag        bool
+	IsCommit     bool
+	Repository   *models.Repository
+	Owner        *models.User
+	Commit       *git.Commit
+	Tag          *git.Tag
+	GitRepo      *git.Repository
+	BranchName   string
+	TagName      string
+	TreeName     string
+	CommitId     string
+	RepoLink     string
+	CloneLink    models.CloneLink
+	CommitsCount int
+	Mirror       *models.Mirror
+}
+
+// Return if the current user has write access for this repository
+func (r RepoContext) IsOwner() bool {
+	return r.AccessMode >= models.ACCESS_MODE_WRITE
+}
+
+// Return if the current user has read access for this repository
+func (r RepoContext) HasAccess() bool {
+	return r.AccessMode >= models.ACCESS_MODE_READ
+}
+
 // HasError returns true if error occurs in form validation.
 // HasError returns true if error occurs in form validation.
 func (ctx *Context) HasApiError() bool {
 func (ctx *Context) HasApiError() bool {
 	hasErr, ok := ctx.Data["HasError"]
 	hasErr, ok := ctx.Data["HasError"]
@@ -130,6 +139,18 @@ func (ctx *Context) Handle(status int, title string, err error) {
 	ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
 	ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status)))
 }
 }
 
 
+func (ctx *Context) HandleAPI(status int, obj interface{}) {
+	var message string
+	if err, ok := obj.(error); ok {
+		message = err.Error()
+	} else {
+		message = obj.(string)
+	}
+	ctx.JSON(status, map[string]string{
+		"message": message,
+	})
+}
+
 func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
 func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) {
 	modtime := time.Now()
 	modtime := time.Now()
 	for _, p := range params {
 	for _, p := range params {

+ 18 - 32
modules/middleware/repo.go

@@ -58,24 +58,19 @@ func ApiRepoAssignment() macaron.Handler {
 			return
 			return
 		}
 		}
 
 
-		if ctx.IsSigned {
-			mode, err := models.AccessLevel(ctx.User, repo)
-			if err != nil {
-				ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL})
-				return
-			}
-
-			ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE
-			ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ
-			ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER
+		mode, err := models.AccessLevel(ctx.User, repo)
+		if err != nil {
+			ctx.JSON(500, &base.ApiJsonErr{"AccessLevel: " + err.Error(), base.DOC_URL})
+			return
 		}
 		}
 
 
+		ctx.Repo.AccessMode = mode
+
 		// Check access.
 		// Check access.
-		if repo.IsPrivate && !ctx.Repo.IsOwner {
+		if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
 			ctx.Error(404)
 			ctx.Error(404)
 			return
 			return
 		}
 		}
-		ctx.Repo.HasAccess = true
 
 
 		ctx.Repo.Repository = repo
 		ctx.Repo.Repository = repo
 	}
 	}
@@ -239,26 +234,18 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 			return
 			return
 		}
 		}
 
 
-		if ctx.IsSigned {
-			mode, err := models.AccessLevel(ctx.User, repo)
-			if err != nil {
-				ctx.Handle(500, "AccessLevel", err)
-				return
-			}
-			ctx.Repo.IsOwner = mode >= models.ACCESS_MODE_WRITE
-			ctx.Repo.IsAdmin = mode >= models.ACCESS_MODE_READ
-			ctx.Repo.IsTrueOwner = mode >= models.ACCESS_MODE_OWNER
-			if !ctx.Repo.IsTrueOwner && ctx.Repo.Owner.IsOrganization() {
-				ctx.Repo.IsTrueOwner = ctx.Repo.Owner.IsOwnedBy(ctx.User.Id)
-			}
+		mode, err := models.AccessLevel(ctx.User, repo)
+		if err != nil {
+			ctx.Handle(500, "AccessLevel", err)
+			return
 		}
 		}
+		ctx.Repo.AccessMode = mode
 
 
 		// Check access.
 		// Check access.
-		if repo.IsPrivate && !ctx.Repo.IsOwner {
+		if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
 			ctx.Handle(404, "no access right", err)
 			ctx.Handle(404, "no access right", err)
 			return
 			return
 		}
 		}
-		ctx.Repo.HasAccess = true
 
 
 		ctx.Data["HasAccess"] = true
 		ctx.Data["HasAccess"] = true
 
 
@@ -306,8 +293,8 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 		ctx.Data["Title"] = u.Name + "/" + repo.Name
 		ctx.Data["Title"] = u.Name + "/" + repo.Name
 		ctx.Data["Repository"] = repo
 		ctx.Data["Repository"] = repo
 		ctx.Data["Owner"] = ctx.Repo.Repository.Owner
 		ctx.Data["Owner"] = ctx.Repo.Repository.Owner
-		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
-		ctx.Data["IsRepositoryTrueOwner"] = ctx.Repo.IsTrueOwner
+		ctx.Data["IsRepositoryOwner"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_WRITE
+		ctx.Data["IsRepositoryAdmin"] = ctx.Repo.AccessMode >= models.ACCESS_MODE_ADMIN
 
 
 		ctx.Data["DisableSSH"] = setting.DisableSSH
 		ctx.Data["DisableSSH"] = setting.DisableSSH
 		ctx.Repo.CloneLink, err = repo.CloneLink()
 		ctx.Repo.CloneLink, err = repo.CloneLink()
@@ -317,8 +304,7 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 		}
 		}
 		ctx.Data["CloneLink"] = ctx.Repo.CloneLink
 		ctx.Data["CloneLink"] = ctx.Repo.CloneLink
 
 
-		if ctx.Repo.Repository.IsGoget {
-			ctx.Data["GoGetLink"] = fmt.Sprintf("%s%s/%s", setting.AppUrl, u.LowerName, repo.LowerName)
+		if ctx.Query("go-get") == "1" {
 			ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName)
 			ctx.Data["GoGetImport"] = fmt.Sprintf("%s/%s/%s", setting.Domain, u.LowerName, repo.LowerName)
 		}
 		}
 
 
@@ -362,9 +348,9 @@ func RepoAssignment(redirect bool, args ...bool) macaron.Handler {
 	}
 	}
 }
 }
 
 
-func RequireTrueOwner() macaron.Handler {
+func RequireAdmin() macaron.Handler {
 	return func(ctx *Context) {
 	return func(ctx *Context) {
-		if !ctx.Repo.IsTrueOwner && !ctx.Repo.IsAdmin {
+		if ctx.Repo.AccessMode < models.ACCESS_MODE_ADMIN {
 			if !ctx.IsSigned {
 			if !ctx.IsSigned {
 				ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
 				ctx.SetCookie("redirect_to", "/"+url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
 				ctx.Redirect(setting.AppSubUrl + "/user/login")
 				ctx.Redirect(setting.AppSubUrl + "/user/login")

+ 8 - 2
modules/setting/setting.go

@@ -245,7 +245,10 @@ func NewConfigContext() {
 	ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
 	ReverseProxyAuthUser = sec.Key("REVERSE_PROXY_AUTHENTICATION_USER").MustString("X-WEBAUTH-USER")
 
 
 	sec = Cfg.Section("attachment")
 	sec = Cfg.Section("attachment")
-	AttachmentPath = path.Join(workDir, sec.Key("PATH").MustString("data/attachments"))
+	AttachmentPath = sec.Key("PATH").MustString("data/attachments")
+	if !filepath.IsAbs(AttachmentPath) {
+		AttachmentPath = path.Join(workDir, AttachmentPath)
+	}
 	AttachmentAllowedTypes = sec.Key("ALLOWED_TYPES").MustString("image/jpeg|image/png")
 	AttachmentAllowedTypes = sec.Key("ALLOWED_TYPES").MustString("image/jpeg|image/png")
 	AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(32)
 	AttachmentMaxSize = sec.Key("MAX_SIZE").MustInt64(32)
 	AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(10)
 	AttachmentMaxFiles = sec.Key("MAX_FILES").MustInt(10)
@@ -302,7 +305,10 @@ func NewConfigContext() {
 
 
 	sec = Cfg.Section("picture")
 	sec = Cfg.Section("picture")
 	PictureService = sec.Key("SERVICE").In("server", []string{"server"})
 	PictureService = sec.Key("SERVICE").In("server", []string{"server"})
-	AvatarUploadPath = path.Join(workDir, sec.Key("AVATAR_UPLOAD_PATH").MustString("data/avatars"))
+	AvatarUploadPath = sec.Key("AVATAR_UPLOAD_PATH").MustString("data/avatars")
+	if !filepath.IsAbs(AvatarUploadPath) {
+		AvatarUploadPath = path.Join(workDir, AvatarUploadPath)
+	}
 	os.MkdirAll(AvatarUploadPath, os.ModePerm)
 	os.MkdirAll(AvatarUploadPath, os.ModePerm)
 	switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") {
 	switch sec.Key("GRAVATAR_SOURCE").MustString("gravatar") {
 	case "duoshuo":
 	case "duoshuo":

+ 1 - 3
public/ng/css/gogs.css

@@ -1066,9 +1066,6 @@ The register and sign-in page style
 #repo-header-download-drop .btn > i {
 #repo-header-download-drop .btn > i {
   margin-right: 6px;
   margin-right: 6px;
 }
 }
-#repo-header-download-drop input {
-  cursor: default;
-}
 #repo-header-download-drop button,
 #repo-header-download-drop button,
 #repo-header-download-drop input {
 #repo-header-download-drop input {
   font-size: 11px;
   font-size: 11px;
@@ -1089,6 +1086,7 @@ The register and sign-in page style
   border-right: none;
   border-right: none;
   width: 190px;
   width: 190px;
   border-left: none;
   border-left: none;
+  cursor: default;
 }
 }
 #repo-clone-help {
 #repo-clone-help {
   clear: both;
   clear: both;

+ 19 - 18
public/ng/js/gogs.js

@@ -209,27 +209,28 @@ var Gogs = {};
             $list.parents('tr').removeClass('end-selected-line');
             $list.parents('tr').removeClass('end-selected-line');
             $list.parents('tr').find('td').removeClass('selected-line');
             $list.parents('tr').find('td').removeClass('selected-line');
             if ($from) {
             if ($from) {
-                var expr = new RegExp(/diff-(\d+)L(\d+)/);
+                var expr = new RegExp(/diff-(\w+)([LR]\d+)/);
                 var selectMatches = $select.attr('rel').match(expr)
                 var selectMatches = $select.attr('rel').match(expr)
                 var fromMatches = $from.attr('rel').match(expr)
                 var fromMatches = $from.attr('rel').match(expr)
-                var a = parseInt(selectMatches[2]);
-                var b = parseInt(fromMatches[2]);
-                var linesIntToStr = {};
-                linesIntToStr[a] = selectMatches[2];
-                linesIntToStr[b] = fromMatches[2];
-
-                var c;
-                if (a != b) {
-                    if (a > b) {
-                        c = a;
-                        a = b;
-                        b = c;
+                var selectTop = $select.offset().top;
+                var fromTop = $from.offset().top;
+                var hash;
+
+                if (selectMatches[2] != fromMatches[2]) {
+                    if ((selectTop > fromTop)) {
+                        $startElem = $from;
+                        $endElem = $select;
+                        hash = fromMatches[1]+fromMatches[2] + '-' + selectMatches[2];
+                    } else {
+                        $startElem = $select;
+                        $endElem = $from;
+                        hash = selectMatches[1]+selectMatches[2] + '-' + fromMatches[2];
                     }
                     }
-                    $('[rel=diff-'+fromMatches[1]+'L' + linesIntToStr[b] + ']').parents('tr').next().addClass('end-selected-line');
-                    var $selectedLines = $('[rel=diff-'+fromMatches[1]+'L' + linesIntToStr[a] + ']').parents('tr').nextUntil('.end-selected-line').andSelf();
+                    $endElem.parents('tr').next().addClass('end-selected-line');
+                    var $selectedLines = $startElem.parents('tr').nextUntil('.end-selected-line').andSelf();
                     $selectedLines.find('td.lines-num > span').addClass('active')
                     $selectedLines.find('td.lines-num > span').addClass('active')
                     $selectedLines.find('td').addClass('selected-line');
                     $selectedLines.find('td').addClass('selected-line');
-                    $.changeHash('#diff-'+fromMatches[1]+'L' + linesIntToStr[a] + '-L' + linesIntToStr[b]);
+                    $.changeHash('#diff-'+hash);
                     return
                     return
                 }
                 }
             }
             }
@@ -262,7 +263,7 @@ var Gogs = {};
         });
         });
 
 
         $(window).on('hashchange', function (e) {
         $(window).on('hashchange', function (e) {
-            var m = window.location.hash.match(/^#diff-(\d+)(L\d+)\-(L\d+)$/);
+            var m = window.location.hash.match(/^#diff-(\w+)([LR]\d+)\-([LR]\d+)$/);
             var $list = $('.code-diff td.lines-num > span');
             var $list = $('.code-diff td.lines-num > span');
             var $first;
             var $first;
             if (m) {
             if (m) {
@@ -271,7 +272,7 @@ var Gogs = {};
                 $("html, body").scrollTop($first.offset().top - 200);
                 $("html, body").scrollTop($first.offset().top - 200);
                 return;
                 return;
             }
             }
-            m = window.location.hash.match(/^#diff-(\d+)(L\d+)$/);
+            m = window.location.hash.match(/^#diff-(\w+)([LR]\d+)$/);
             if (m) {
             if (m) {
                 $first = $list.filter('[rel=diff-' + m[1] + m[2] + ']');
                 $first = $list.filter('[rel=diff-' + m[1] + m[2] + ']');
                 selectRange($list, $first);
                 selectRange($list, $first);

+ 1 - 3
public/ng/less/gogs/repository.less

@@ -81,9 +81,6 @@
     .btn>i {
     .btn>i {
         margin-right: 6px;
         margin-right: 6px;
     }
     }
-    input {
-        cursor: default;
-    }
     button, input {
     button, input {
         font-size: 11px;
         font-size: 11px;
     }
     }
@@ -104,6 +101,7 @@
     border-right: none;
     border-right: none;
     width: 190px;
     width: 190px;
     border-left: none;
     border-left: none;
+    cursor: default;
 }
 }
 #repo-clone-help {
 #repo-clone-help {
     clear: both;
     clear: both;

+ 38 - 37
routers/api/v1/repo.go

@@ -5,7 +5,7 @@
 package v1
 package v1
 
 
 import (
 import (
-	"fmt"
+	"net/url"
 	"path"
 	"path"
 	"strings"
 	"strings"
 
 
@@ -156,17 +156,15 @@ func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
 func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
 func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
 	u, err := models.GetUserByName(ctx.Query("username"))
 	u, err := models.GetUserByName(ctx.Query("username"))
 	if err != nil {
 	if err != nil {
-		ctx.JSON(500, map[string]interface{}{
-			"ok":    false,
-			"error": err.Error(),
-		})
+		if err == models.ErrUserNotExist {
+			ctx.HandleAPI(422, err)
+		} else {
+			ctx.HandleAPI(500, err)
+		}
 		return
 		return
 	}
 	}
 	if !u.ValidtePassword(ctx.Query("password")) {
 	if !u.ValidtePassword(ctx.Query("password")) {
-		ctx.JSON(500, map[string]interface{}{
-			"ok":    false,
-			"error": "username or password is not correct",
-		})
+		ctx.HandleAPI(422, "Username or password is not correct.")
 		return
 		return
 	}
 	}
 
 
@@ -175,56 +173,59 @@ func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
 	if form.Uid != u.Id {
 	if form.Uid != u.Id {
 		org, err := models.GetUserById(form.Uid)
 		org, err := models.GetUserById(form.Uid)
 		if err != nil {
 		if err != nil {
-			log.Error(4, "GetUserById: %v", err)
-			ctx.Error(500)
+			if err == models.ErrUserNotExist {
+				ctx.HandleAPI(422, err)
+			} else {
+				ctx.HandleAPI(500, err)
+			}
 			return
 			return
 		}
 		}
 		ctxUser = org
 		ctxUser = org
 	}
 	}
 
 
 	if ctx.HasError() {
 	if ctx.HasError() {
-		ctx.JSON(422, map[string]interface{}{
-			"ok":    false,
-			"error": ctx.GetErrMsg(),
-		})
+		ctx.HandleAPI(422, ctx.GetErrMsg())
 		return
 		return
 	}
 	}
 
 
 	if ctxUser.IsOrganization() {
 	if ctxUser.IsOrganization() {
 		// Check ownership of organization.
 		// Check ownership of organization.
 		if !ctxUser.IsOwnedBy(u.Id) {
 		if !ctxUser.IsOwnedBy(u.Id) {
-			ctx.JSON(403, map[string]interface{}{
-				"ok":    false,
-				"error": "given user is not owner of organization",
-			})
+			ctx.HandleAPI(403, "Given user is not owner of organization.")
 			return
 			return
 		}
 		}
 	}
 	}
 
 
-	authStr := strings.Replace(fmt.Sprintf("://%s:%s",
-		form.AuthUserName, form.AuthPasswd), "@", "%40", -1)
-	url := strings.Replace(form.HttpsUrl, "://", authStr+"@", 1)
-	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private,
-		form.Mirror, url)
-	if err == nil {
-		log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
-		ctx.JSON(200, map[string]interface{}{
-			"ok":   true,
-			"data": "/" + ctxUser.Name + "/" + form.RepoName,
-		})
+	// Remote address can be HTTPS URL or local path.
+	remoteAddr := form.CloneAddr
+	if strings.HasPrefix(form.CloneAddr, "http") {
+		u, err := url.Parse(form.CloneAddr)
+		if err != nil {
+			ctx.HandleAPI(422, err)
+			return
+		}
+		if len(form.AuthUserName) > 0 || len(form.AuthPasswd) > 0 {
+			u.User = url.UserPassword(form.AuthUserName, form.AuthPasswd)
+		}
+		remoteAddr = u.String()
+	} else if !com.IsDir(remoteAddr) {
+		ctx.HandleAPI(422, "Invalid local path, it does not exist or not a directory.")
 		return
 		return
 	}
 	}
 
 
-	if repo != nil {
-		if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
-			log.Error(4, "DeleteRepository: %v", errDelete)
+	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
+	if err != nil {
+		if repo != nil {
+			if errDelete := models.DeleteRepository(ctxUser.Id, repo.Id, ctxUser.Name); errDelete != nil {
+				log.Error(4, "DeleteRepository: %v", errDelete)
+			}
 		}
 		}
+		ctx.HandleAPI(500, err)
+		return
 	}
 	}
 
 
-	ctx.JSON(500, map[string]interface{}{
-		"ok":    false,
-		"error": err.Error(),
-	})
+	log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
+	ctx.WriteHeader(200)
 }
 }
 
 
 // GET /user/repos
 // GET /user/repos

+ 1 - 1
routers/api/v1/repo_file.go

@@ -12,7 +12,7 @@ import (
 )
 )
 
 
 func GetRepoRawFile(ctx *middleware.Context) {
 func GetRepoRawFile(ctx *middleware.Context) {
-	if ctx.Repo.Repository.IsPrivate && !ctx.Repo.HasAccess {
+	if !ctx.Repo.HasAccess() {
 		ctx.Error(404)
 		ctx.Error(404)
 		return
 		return
 	}
 	}

+ 6 - 0
routers/install.go

@@ -189,6 +189,12 @@ func InstallPost(ctx *middleware.Context, form auth.InstallForm) {
 
 
 	// Save settings.
 	// Save settings.
 	cfg := ini.Empty()
 	cfg := ini.Empty()
+	if com.IsFile(setting.CustomConf) {
+		// Keeps custom settings if there is already something.
+		if err := cfg.Append(setting.CustomConf); err != nil {
+			log.Error(4, "Fail to load custom conf '%s': %v", setting.CustomConf, err)
+		}
+	}
 	cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type)
 	cfg.Section("database").Key("DB_TYPE").SetValue(models.DbCfg.Type)
 	cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host)
 	cfg.Section("database").Key("HOST").SetValue(models.DbCfg.Host)
 	cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name)
 	cfg.Section("database").Key("NAME").SetValue(models.DbCfg.Name)

+ 10 - 5
routers/org/setting.go

@@ -39,18 +39,18 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateOrgSettingForm) {
 
 
 	// Check if organization name has been changed.
 	// Check if organization name has been changed.
 	if org.Name != form.OrgUserName {
 	if org.Name != form.OrgUserName {
-		isExist, err := models.IsUserExist(form.OrgUserName)
+		isExist, err := models.IsUserExist(org.Id, form.OrgUserName)
 		if err != nil {
 		if err != nil {
 			ctx.Handle(500, "IsUserExist", err)
 			ctx.Handle(500, "IsUserExist", err)
 			return
 			return
 		} else if isExist {
 		} else if isExist {
+			ctx.Data["Err_UserName"] = true
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), SETTINGS_OPTIONS, &form)
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), SETTINGS_OPTIONS, &form)
 			return
 			return
 		} else if err = models.ChangeUserName(org, form.OrgUserName); err != nil {
 		} else if err = models.ChangeUserName(org, form.OrgUserName); err != nil {
 			if err == models.ErrUserNameIllegal {
 			if err == models.ErrUserNameIllegal {
-				ctx.Flash.Error(ctx.Tr("form.illegal_username"))
-				ctx.Redirect(setting.AppSubUrl + "/org/" + org.LowerName + "/settings")
-				return
+				ctx.Data["Err_UserName"] = true
+				ctx.RenderWithErr(ctx.Tr("form.illegal_username"), SETTINGS_OPTIONS, &form)
 			} else {
 			} else {
 				ctx.Handle(500, "ChangeUserName", err)
 				ctx.Handle(500, "ChangeUserName", err)
 			}
 			}
@@ -68,7 +68,12 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateOrgSettingForm) {
 	org.Avatar = base.EncodeMd5(form.Avatar)
 	org.Avatar = base.EncodeMd5(form.Avatar)
 	org.AvatarEmail = form.Avatar
 	org.AvatarEmail = form.Avatar
 	if err := models.UpdateUser(org); err != nil {
 	if err := models.UpdateUser(org); err != nil {
-		ctx.Handle(500, "UpdateUser", err)
+		if err == models.ErrEmailAlreadyUsed {
+			ctx.Data["Err_Email"] = true
+			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), SETTINGS_OPTIONS, &form)
+		} else {
+			ctx.Handle(500, "UpdateUser", err)
+		}
 		return
 		return
 	}
 	}
 	log.Trace("Organization setting updated: %s", org.Name)
 	log.Trace("Organization setting updated: %s", org.Name)

+ 1 - 1
routers/org/teams.go

@@ -181,7 +181,7 @@ func NewTeamPost(ctx *middleware.Context, form auth.CreateTeamForm) {
 	org := ctx.Org.Organization
 	org := ctx.Org.Organization
 
 
 	t := &models.Team{
 	t := &models.Team{
-		OrgId:       org.Id,
+		OrgID:       org.Id,
 		Name:        form.TeamName,
 		Name:        form.TeamName,
 		Description: form.Description,
 		Description: form.Description,
 		Authorize:   auth,
 		Authorize:   auth,

+ 5 - 0
routers/repo/http.go

@@ -158,6 +158,11 @@ func Http(ctx *middleware.Context) {
 					return
 					return
 				}
 				}
 			}
 			}
+
+			if !isPull && repo.IsMirror {
+				ctx.Handle(401, "can't push to mirror", nil)
+				return
+			}
 		}
 		}
 	}
 	}
 
 

+ 7 - 7
routers/repo/issue.go

@@ -230,7 +230,7 @@ func CreateIssuePost(ctx *middleware.Context, form auth.CreateIssueForm) {
 	}
 	}
 
 
 	// Only collaborators can assign.
 	// Only collaborators can assign.
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		form.AssigneeId = 0
 		form.AssigneeId = 0
 	}
 	}
 	issue := &models.Issue{
 	issue := &models.Issue{
@@ -434,7 +434,7 @@ func ViewIssue(ctx *middleware.Context) {
 	ctx.Data["Title"] = issue.Name
 	ctx.Data["Title"] = issue.Name
 	ctx.Data["Issue"] = issue
 	ctx.Data["Issue"] = issue
 	ctx.Data["Comments"] = comments
 	ctx.Data["Comments"] = comments
-	ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
+	ctx.Data["IsIssueOwner"] = ctx.Repo.IsOwner() || (ctx.IsSigned && issue.PosterId == ctx.User.Id)
 	ctx.Data["IsRepoToolbarIssues"] = true
 	ctx.Data["IsRepoToolbarIssues"] = true
 	ctx.Data["IsRepoToolbarIssuesList"] = false
 	ctx.Data["IsRepoToolbarIssuesList"] = false
 	ctx.HTML(200, ISSUE_VIEW)
 	ctx.HTML(200, ISSUE_VIEW)
@@ -457,7 +457,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
 		return
 		return
 	}
 	}
 
 
-	if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner {
+	if ctx.User.Id != issue.PosterId && !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		ctx.Error(403)
 		return
 		return
 	}
 	}
@@ -484,7 +484,7 @@ func UpdateIssue(ctx *middleware.Context, form auth.CreateIssueForm) {
 }
 }
 
 
 func UpdateIssueLabel(ctx *middleware.Context) {
 func UpdateIssueLabel(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		ctx.Error(403)
 		return
 		return
 	}
 	}
@@ -560,7 +560,7 @@ func UpdateIssueLabel(ctx *middleware.Context) {
 }
 }
 
 
 func UpdateIssueMilestone(ctx *middleware.Context) {
 func UpdateIssueMilestone(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		ctx.Error(403)
 		return
 		return
 	}
 	}
@@ -606,7 +606,7 @@ func UpdateIssueMilestone(ctx *middleware.Context) {
 }
 }
 
 
 func UpdateAssignee(ctx *middleware.Context) {
 func UpdateAssignee(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Error(403)
 		ctx.Error(403)
 		return
 		return
 	}
 	}
@@ -752,7 +752,7 @@ func Comment(ctx *middleware.Context) {
 
 
 	// Check if issue owner changes the status of issue.
 	// Check if issue owner changes the status of issue.
 	var newStatus string
 	var newStatus string
-	if ctx.Repo.IsOwner || issue.PosterId == ctx.User.Id {
+	if ctx.Repo.IsOwner() || issue.PosterId == ctx.User.Id {
 		newStatus = ctx.Query("change_status")
 		newStatus = ctx.Query("change_status")
 	}
 	}
 	if len(newStatus) > 0 {
 	if len(newStatus) > 0 {

+ 5 - 5
routers/repo/release.go

@@ -41,7 +41,7 @@ func Releases(ctx *middleware.Context) {
 	tags := make([]*models.Release, len(rawTags))
 	tags := make([]*models.Release, len(rawTags))
 	for i, rawTag := range rawTags {
 	for i, rawTag := range rawTags {
 		for j, rel := range rels {
 		for j, rel := range rels {
-			if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner) {
+			if rel == nil || (rel.IsDraft && !ctx.Repo.IsOwner()) {
 				continue
 				continue
 			}
 			}
 			if rel.TagName == rawTag {
 			if rel.TagName == rawTag {
@@ -140,7 +140,7 @@ func Releases(ctx *middleware.Context) {
 }
 }
 
 
 func NewRelease(ctx *middleware.Context) {
 func NewRelease(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.ReleasesNew", nil)
 		ctx.Handle(403, "release.ReleasesNew", nil)
 		return
 		return
 	}
 	}
@@ -153,7 +153,7 @@ func NewRelease(ctx *middleware.Context) {
 }
 }
 
 
 func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
 func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.ReleasesNew", nil)
 		ctx.Handle(403, "release.ReleasesNew", nil)
 		return
 		return
 	}
 	}
@@ -211,7 +211,7 @@ func NewReleasePost(ctx *middleware.Context, form auth.NewReleaseForm) {
 }
 }
 
 
 func EditRelease(ctx *middleware.Context) {
 func EditRelease(ctx *middleware.Context) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.ReleasesEdit", nil)
 		ctx.Handle(403, "release.ReleasesEdit", nil)
 		return
 		return
 	}
 	}
@@ -234,7 +234,7 @@ func EditRelease(ctx *middleware.Context) {
 }
 }
 
 
 func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
 func EditReleasePost(ctx *middleware.Context, form auth.EditReleaseForm) {
-	if !ctx.Repo.IsOwner {
+	if !ctx.Repo.IsOwner() {
 		ctx.Handle(403, "release.EditReleasePost", nil)
 		ctx.Handle(403, "release.EditReleasePost", nil)
 		return
 		return
 	}
 	}

+ 18 - 12
routers/repo/repo.go

@@ -181,20 +181,26 @@ func MigratePost(ctx *middleware.Context, form auth.MigrateRepoForm) {
 		}
 		}
 	}
 	}
 
 
-	u, err := url.Parse(form.HttpsUrl)
-
-	if err != nil || u.Scheme != "https" {
-		ctx.Data["Err_HttpsUrl"] = true
-		ctx.RenderWithErr(ctx.Tr("form.url_error"), MIGRATE, &form)
+	// Remote address can be HTTPS URL or local path.
+	remoteAddr := form.CloneAddr
+	if strings.HasPrefix(form.CloneAddr, "http") {
+		u, err := url.Parse(form.CloneAddr)
+		if err != nil {
+			ctx.Data["Err_CloneAddr"] = true
+			ctx.RenderWithErr(ctx.Tr("form.url_error"), MIGRATE, &form)
+			return
+		}
+		if len(form.AuthUserName) > 0 || len(form.AuthPasswd) > 0 {
+			u.User = url.UserPassword(form.AuthUserName, form.AuthPasswd)
+		}
+		remoteAddr = u.String()
+	} else if !com.IsDir(remoteAddr) {
+		ctx.Data["Err_CloneAddr"] = true
+		ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), MIGRATE, &form)
 		return
 		return
 	}
 	}
 
 
-	if len(form.AuthUserName) > 0 || len(form.AuthPasswd) > 0 {
-		u.User = url.UserPassword(form.AuthUserName, form.AuthPasswd)
-	}
-
-	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private,
-		form.Mirror, u.String())
+	repo, err := models.MigrateRepository(ctxUser, form.RepoName, form.Description, form.Private, form.Mirror, remoteAddr)
 	if err == nil {
 	if err == nil {
 		log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
 		log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
 		ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
 		ctx.Redirect(setting.AppSubUrl + "/" + ctxUser.Name + "/" + form.RepoName)
@@ -343,7 +349,7 @@ func Action(ctx *middleware.Context) {
 	case "unstar":
 	case "unstar":
 		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
 		err = models.StarRepo(ctx.User.Id, ctx.Repo.Repository.Id, false)
 	case "desc":
 	case "desc":
-		if !ctx.Repo.IsOwner {
+		if !ctx.Repo.IsOwner() {
 			ctx.Error(404)
 			ctx.Error(404)
 			return
 			return
 		}
 		}

+ 1 - 2
routers/repo/setting.go

@@ -83,7 +83,6 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 		ctx.Repo.Repository.Description = form.Description
 		ctx.Repo.Repository.Description = form.Description
 		ctx.Repo.Repository.Website = form.Website
 		ctx.Repo.Repository.Website = form.Website
 		ctx.Repo.Repository.IsPrivate = form.Private
 		ctx.Repo.Repository.IsPrivate = form.Private
-		ctx.Repo.Repository.IsGoget = form.GoGet
 		if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
 		if err := models.UpdateRepository(ctx.Repo.Repository); err != nil {
 			ctx.Handle(404, "UpdateRepository", err)
 			ctx.Handle(404, "UpdateRepository", err)
 			return
 			return
@@ -109,7 +108,7 @@ func SettingsPost(ctx *middleware.Context, form auth.RepoSettingForm) {
 		}
 		}
 
 
 		newOwner := ctx.Query("new_owner_name")
 		newOwner := ctx.Query("new_owner_name")
-		isExist, err := models.IsUserExist(newOwner)
+		isExist, err := models.IsUserExist(0, newOwner)
 		if err != nil {
 		if err != nil {
 			ctx.Handle(500, "IsUserExist", err)
 			ctx.Handle(500, "IsUserExist", err)
 			return
 			return

+ 1 - 4
routers/user/auth.go

@@ -351,15 +351,12 @@ func ActivateEmail(ctx *middleware.Context) {
 
 
 	// Verify code.
 	// Verify code.
 	if email := models.VerifyActiveEmailCode(code, email_string); email != nil {
 	if email := models.VerifyActiveEmailCode(code, email_string); email != nil {
-		err := email.Activate()
-		if err != nil {
+		if err := email.Activate(); err != nil {
 			ctx.Handle(500, "ActivateEmail", err)
 			ctx.Handle(500, "ActivateEmail", err)
 		}
 		}
 
 
 		log.Trace("Email activated: %s", email.Email)
 		log.Trace("Email activated: %s", email.Email)
-
 		ctx.Flash.Success(ctx.Tr("settings.activate_email_success"))
 		ctx.Flash.Success(ctx.Tr("settings.activate_email_success"))
-
 	}
 	}
 
 
 	ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
 	ctx.Redirect(setting.AppSubUrl + "/user/settings/email")

+ 44 - 47
routers/user/setting.go

@@ -50,7 +50,7 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
 
 
 	// Check if user name has been changed.
 	// Check if user name has been changed.
 	if ctx.User.Name != form.UserName {
 	if ctx.User.Name != form.UserName {
-		isExist, err := models.IsUserExist(form.UserName)
+		isExist, err := models.IsUserExist(ctx.User.Id, form.UserName)
 		if err != nil {
 		if err != nil {
 			ctx.Handle(500, "IsUserExist", err)
 			ctx.Handle(500, "IsUserExist", err)
 			return
 			return
@@ -58,11 +58,14 @@ func SettingsPost(ctx *middleware.Context, form auth.UpdateProfileForm) {
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), SETTINGS_PROFILE, &form)
 			ctx.RenderWithErr(ctx.Tr("form.username_been_taken"), SETTINGS_PROFILE, &form)
 			return
 			return
 		} else if err = models.ChangeUserName(ctx.User, form.UserName); err != nil {
 		} else if err = models.ChangeUserName(ctx.User, form.UserName); err != nil {
-			if err == models.ErrUserNameIllegal {
+			switch err {
+			case models.ErrUserNameIllegal:
 				ctx.Flash.Error(ctx.Tr("form.illegal_username"))
 				ctx.Flash.Error(ctx.Tr("form.illegal_username"))
 				ctx.Redirect(setting.AppSubUrl + "/user/settings")
 				ctx.Redirect(setting.AppSubUrl + "/user/settings")
-				return
-			} else {
+			case models.ErrEmailAlreadyUsed:
+				ctx.Flash.Error(ctx.Tr("form.email_been_used"))
+				ctx.Redirect(setting.AppSubUrl + "/user/settings")
+			default:
 				ctx.Handle(500, "ChangeUserName", err)
 				ctx.Handle(500, "ChangeUserName", err)
 			}
 			}
 			return
 			return
@@ -133,13 +136,12 @@ func SettingsEmails(ctx *middleware.Context) {
 	ctx.Data["PageIsUserSettings"] = true
 	ctx.Data["PageIsUserSettings"] = true
 	ctx.Data["PageIsSettingsEmails"] = true
 	ctx.Data["PageIsSettingsEmails"] = true
 
 
-	var err error
-	ctx.Data["Emails"], err = models.GetEmailAddresses(ctx.User.Id)
-
+	emails, err := models.GetEmailAddresses(ctx.User.Id)
 	if err != nil {
 	if err != nil {
-		ctx.Handle(500, "email.GetEmailAddresses", err)
+		ctx.Handle(500, "GetEmailAddresses", err)
 		return
 		return
 	}
 	}
+	ctx.Data["Emails"] = emails
 
 
 	ctx.HTML(200, SETTINGS_EMAILS)
 	ctx.HTML(200, SETTINGS_EMAILS)
 }
 }
@@ -149,16 +151,16 @@ func SettingsEmailPost(ctx *middleware.Context, form auth.AddEmailForm) {
 	ctx.Data["PageIsUserSettings"] = true
 	ctx.Data["PageIsUserSettings"] = true
 	ctx.Data["PageIsSettingsEmails"] = true
 	ctx.Data["PageIsSettingsEmails"] = true
 
 
-	var err error
-	ctx.Data["Emails"], err = models.GetEmailAddresses(ctx.User.Id)
+	emails, err := models.GetEmailAddresses(ctx.User.Id)
 	if err != nil {
 	if err != nil {
-		ctx.Handle(500, "email.GetEmailAddresses", err)
+		ctx.Handle(500, "GetEmailAddresses", err)
 		return
 		return
 	}
 	}
+	ctx.Data["Emails"] = emails
 
 
-	// Delete Email address.
+	// Delete E-mail address.
 	if ctx.Query("_method") == "DELETE" {
 	if ctx.Query("_method") == "DELETE" {
-		id := com.StrTo(ctx.Query("id")).MustInt64()
+		id := ctx.QueryInt64("id")
 		if id <= 0 {
 		if id <= 0 {
 			return
 			return
 		}
 		}
@@ -174,7 +176,7 @@ func SettingsEmailPost(ctx *middleware.Context, form auth.AddEmailForm) {
 
 
 	// Make emailaddress primary.
 	// Make emailaddress primary.
 	if ctx.Query("_method") == "PRIMARY" {
 	if ctx.Query("_method") == "PRIMARY" {
-		id := com.StrTo(ctx.Query("id")).MustInt64()
+		id := ctx.QueryInt64("id")
 		if id <= 0 {
 		if id <= 0 {
 			return
 			return
 		}
 		}
@@ -189,46 +191,41 @@ func SettingsEmailPost(ctx *middleware.Context, form auth.AddEmailForm) {
 	}
 	}
 
 
 	// Add Email address.
 	// Add Email address.
-	if ctx.Req.Method == "POST" {
-		if ctx.HasError() {
-			ctx.HTML(200, SETTINGS_EMAILS)
-			return
-		}
+	if ctx.HasError() {
+		ctx.HTML(200, SETTINGS_EMAILS)
+		return
+	}
 
 
-		cleanEmail := strings.Replace(form.Email, "\n", "", -1)
-		e := &models.EmailAddress{
-			Uid:         ctx.User.Id,
-			Email:       cleanEmail,
-			IsActivated: !setting.Service.RegisterEmailConfirm,
-		}
+	cleanEmail := strings.Replace(form.Email, "\n", "", -1)
+	e := &models.EmailAddress{
+		Uid:         ctx.User.Id,
+		Email:       cleanEmail,
+		IsActivated: !setting.Service.RegisterEmailConfirm,
+	}
 
 
-		if err := models.AddEmailAddress(e); err != nil {
-			if err == models.ErrEmailAlreadyUsed {
-				ctx.RenderWithErr(ctx.Tr("form.email_has_been_used"), SETTINGS_EMAILS, &form)
-				return
-			}
-			ctx.Handle(500, "email.AddEmailAddress", err)
+	if err := models.AddEmailAddress(e); err != nil {
+		if err == models.ErrEmailAlreadyUsed {
+			ctx.RenderWithErr(ctx.Tr("form.email_been_used"), SETTINGS_EMAILS, &form)
 			return
 			return
-		} else {
-
-			// Send confirmation e-mail
-			if setting.Service.RegisterEmailConfirm {
-				mailer.SendActivateEmail(ctx.Render, ctx.User, e)
+		}
+		ctx.Handle(500, "AddEmailAddress", err)
+		return
+	} else {
+		// Send confirmation e-mail
+		if setting.Service.RegisterEmailConfirm {
+			mailer.SendActivateEmail(ctx.Render, ctx.User, e)
 
 
-				if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
-					log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
-				}
-				ctx.Flash.Success(ctx.Tr("settings.add_email_success_confirmation_email_sent"))
-			} else {
-				ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
+			if err := ctx.Cache.Put("MailResendLimit_"+ctx.User.LowerName, ctx.User.LowerName, 180); err != nil {
+				log.Error(4, "Set cache(MailResendLimit) fail: %v", err)
 			}
 			}
-
-			log.Trace("Email address added: %s", e.Email)
-
-			ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
-			return
+			ctx.Flash.Success(ctx.Tr("settings.add_email_success_confirmation_email_sent"))
+		} else {
+			ctx.Flash.Success(ctx.Tr("settings.add_email_success"))
 		}
 		}
 
 
+		log.Trace("Email address added: %s", e.Email)
+		ctx.Redirect(setting.AppSubUrl + "/user/settings/email")
+		return
 	}
 	}
 
 
 	ctx.HTML(200, SETTINGS_EMAILS)
 	ctx.HTML(200, SETTINGS_EMAILS)

+ 5 - 0
scripts/less.sh

@@ -0,0 +1,5 @@
+#!/bin/sh
+echo "compiling LESS Files"
+lessc ../public/ng/less/gogs.less ../public/ng/css/gogs.css
+lessc ../public/ng/less/ui.less ../public/ng/css/ui.css
+echo "done"

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.5.14.0213 Beta
+0.5.15.0223 Beta

+ 2 - 2
templates/admin/auth/list.tmpl

@@ -34,8 +34,8 @@
 					                            <td><a href="{{AppSubUrl}}/admin/auths/{{.Id}}">{{.Name}}</a></td>
 					                            <td><a href="{{AppSubUrl}}/admin/auths/{{.Id}}">{{.Name}}</a></td>
 					                            <td>{{.TypeString}}</td>
 					                            <td>{{.TypeString}}</td>
 					                            <td><i class="fa fa{{if .IsActived}}-check{{end}}-square-o"></i></td>
 					                            <td><i class="fa fa{{if .IsActived}}-check{{end}}-square-o"></i></td>
-					                            <td><span title="{{DateFormat .Updated "r"}}">{{DateFormat .Updated "M d, Y"}}</span></td>
-					                            <td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
+					                            <td><span title="{{DateFmtLong .Updated}}">{{DateFmtShort .Updated}}</span></td>
+					                            <td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td>
 					                            <td><a href="{{AppSubUrl}}/admin/auths/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
 					                            <td><a href="{{AppSubUrl}}/admin/auths/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
 					                        </tr>
 					                        </tr>
 					                        {{end}}
 					                        {{end}}

+ 1 - 1
templates/admin/org/list.tmpl

@@ -35,7 +35,7 @@
 					                            <td>{{.NumTeams}}</td>
 					                            <td>{{.NumTeams}}</td>
 					                            <td>{{.NumMembers}}</td>
 					                            <td>{{.NumMembers}}</td>
 					                            <td>{{.NumRepos}}</td>
 					                            <td>{{.NumRepos}}</td>
-					                            <td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
+					                            <td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td>
 					                        </tr>
 					                        </tr>
 					                        {{end}}
 					                        {{end}}
 					                    </tbody>
 					                    </tbody>

+ 1 - 1
templates/admin/repo/list.tmpl

@@ -37,7 +37,7 @@
 					                            <td>{{.NumWatches}}</td>
 					                            <td>{{.NumWatches}}</td>
 					                            <td>{{.NumStars}}</td>
 					                            <td>{{.NumStars}}</td>
 					                            <td>{{.NumIssues}}</td>
 					                            <td>{{.NumIssues}}</td>
-					                            <td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
+					                            <td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span></td>
 					                        </tr>
 					                        </tr>
 					                        {{end}}
 					                        {{end}}
 					                    </tbody>
 					                    </tbody>

+ 1 - 1
templates/admin/user/list.tmpl

@@ -37,7 +37,7 @@
 					                            <td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
 					                            <td><i class="fa fa{{if .IsActive}}-check{{end}}-square-o"></i></td>
 					                            <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
 					                            <td><i class="fa fa{{if .IsAdmin}}-check{{end}}-square-o"></i></td>
 					                            <td>{{.NumRepos}}</td>
 					                            <td>{{.NumRepos}}</td>
-					                            <td><span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span></td>
+					                            <td><span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created }}</span></td>
 					                            <td><a href="{{AppSubUrl}}/admin/users/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
 					                            <td><a href="{{AppSubUrl}}/admin/users/{{.Id}}"><i class="fa fa-pencil-square-o"></i></a></td>
 					                        </tr>
 					                        </tr>
 					                        {{end}}
 					                        {{end}}

+ 1 - 1
templates/base/head.tmpl

@@ -9,7 +9,7 @@
 		<meta name="description" content="Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language" />
 		<meta name="description" content="Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language" />
 		<meta name="keywords" content="go, git">
 		<meta name="keywords" content="go, git">
 		<meta name="_csrf" content="{{.CsrfToken}}" />
 		<meta name="_csrf" content="{{.CsrfToken}}" />
-		{{if .Repository.IsGoget}}<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">{{end}}
+		{{if .GoGetImport}}<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">{{end}}
 
 
 		 <!-- Stylesheets -->
 		 <!-- Stylesheets -->
 		{{if CdnMode}}
 		{{if CdnMode}}

+ 1 - 1
templates/ng/base/head.tmpl

@@ -7,7 +7,7 @@
 		<meta name="description" content="Gogs(Go Git Service) a painless self-hosted Git Service written in Go" />
 		<meta name="description" content="Gogs(Go Git Service) a painless self-hosted Git Service written in Go" />
 		<meta name="keywords" content="go, git, self-hosted, gogs">
 		<meta name="keywords" content="go, git, self-hosted, gogs">
 		<meta name="_csrf" content="{{.CsrfToken}}" />
 		<meta name="_csrf" content="{{.CsrfToken}}" />
-		{{if .Repository.IsGoget}}<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">{{end}}
+		{{if .GoGetImport}}<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}">{{end}}
 
 
 		<link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" />
 		<link rel="shortcut icon" href="{{AppSubUrl}}/img/favicon.png" />
 
 

+ 1 - 1
templates/org/base/header.tmpl

@@ -2,7 +2,7 @@
 	<div class="container">
 	<div class="container">
 		<a class="text-black left" href="{{AppSubUrl}}/org/{{.Org.LowerName}}">
 		<a class="text-black left" href="{{AppSubUrl}}/org/{{.Org.LowerName}}">
 			<img class="avatar-48 left" src="{{.Org.AvatarLink}}?s=100">
 			<img class="avatar-48 left" src="{{.Org.AvatarLink}}?s=100">
-			<span class="org-name">{{.Org.FullName}}</span>
+			<span class="org-name">{{if .Org.FullName}}{{.Org.FullName}}{{else}}{{.Org.Name}}{{end}}</span>
 		</a>
 		</a>
 		<ul class="menu menu-line container">
 		<ul class="menu menu-line container">
 			<li class="right">
 			<li class="right">

+ 70 - 70
templates/org/home.tmpl

@@ -9,85 +9,85 @@
 				{{if .IsOrganizationOwner}}<a class="text-grey" href="{{.OrgLink}}/settings"><span class="octicon octicon-gear"></span></a>{{end}}
 				{{if .IsOrganizationOwner}}<a class="text-grey" href="{{.OrgLink}}/settings"><span class="octicon octicon-gear"></span></a>{{end}}
 			</h2>
 			</h2>
 			{{if .Org.Description}}<p>{{.Org.Description}}</p>{{end}}
 			{{if .Org.Description}}<p>{{.Org.Description}}</p>{{end}}
-            <ul class="text-grey">
-                {{if .Org.Location}}<li><span class="octicon octicon-location"></span> <span>{{.Org.Location}}</span></li>{{end}}
-                {{if .Org.Website}}<li><span class="octicon octicon-link"></span> <a target="_blank" href="{{.Org.Website}}">{{.Org.Website}}</a></li>{{end}}
-                {{if .Org.Email}}<li><span class="octicon octicon-mail"></span> <a href="mailto:{{.Org.Email}}">{{.Org.Email}}</a></li>{{end}}
-            </ul>
+      <ul class="text-grey">
+        {{if .Org.Location}}<li><span class="octicon octicon-location"></span> <span>{{.Org.Location}}</span></li>{{end}}
+        {{if .Org.Website}}<li><span class="octicon octicon-link"></span> <a target="_blank" href="{{.Org.Website}}">{{.Org.Website}}</a></li>{{end}}
+        {{if .Org.Email}}<li><span class="octicon octicon-mail"></span> <a href="mailto:{{.Org.Email}}">{{.Org.Email}}</a></li>{{end}}
+      </ul>
 		</div>
 		</div>
 	</div>
 	</div>
 </div>
 </div>
 <div class="container">
 <div class="container">
 	{{$isMember := .Org.IsOrgMember $.SignedUser.Id}}
 	{{$isMember := .Org.IsOrgMember $.SignedUser.Id}}
-    <div id="org-home-repo-list" class="left grid-2-3">
-        <div class="clear">
-        	{{if .IsOrganizationOwner}}
-            <a class="btn btn-green btn-large btn-link btn-radius right" href="{{AppSubUrl}}/repo/create?org={{.Org.Id}}"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "new_repo"}}</a>
-        	{{end}}
-        </div>
-        <div id="org-repo-list">
-			{{range .Repos}}
-				{{if .HasAccess $.SignedUser}}
-				<div class="org-repo-item">
-                    <ul class="org-repo-status right">
-                        <li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
-                        <li><i class="octicon octicon-git-branch"></i> {{.NumForks}}</li>
-                    </ul>
-					<h2><a href="{{AppSubUrl}}/{{$.Org.Name}}/{{.Name}}">{{.Name}}</a></h2>
-					<p class="org-repo-description">{{.Description}}</p>
-					<p class="org-repo-updated">{{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Updated $.i18n.Lang}}</p>
-				</div>
-				{{end}}
+  <div id="org-home-repo-list" class="left grid-2-3">
+    <div class="clear">
+	  	{{if .IsOrganizationOwner}}
+	     <a class="btn btn-green btn-large btn-link btn-radius right" href="{{AppSubUrl}}/repo/create?org={{.Org.Id}}"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "new_repo"}}</a>
+	  	{{end}}
+    </div>
+    <div id="org-repo-list">
+		{{range .Repos}}
+			{{if or (not .IsPrivate) (.HasAccess $.SignedUser)}}
+			<div class="org-repo-item">
+        <ul class="org-repo-status right">
+          <li><i class="octicon octicon-star"></i> {{.NumStars}}</li>
+          <li><i class="octicon octicon-git-branch"></i> {{.NumForks}}</li>
+        </ul>
+				<h2><a href="{{AppSubUrl}}/{{$.Org.Name}}/{{.Name}}">{{.Name}}</a></h2>
+				<p class="org-repo-description">{{.Description}}</p>
+				<p class="org-repo-updated">{{$.i18n.Tr "org.repo_updated"}} {{TimeSince .Updated $.i18n.Lang}}</p>
+			</div>
 			{{end}}
 			{{end}}
-        </div>
+		{{end}}
     </div>
     </div>
-    <div class="grid-1-3 right">
-    	<div class="org-sidebar">
-	    	<div class="panel panel-radius">
-	    		<div class="panel-header">
-	    			{{if $isMember}}
-	    			<a class="text-grey right" href="{{AppSubUrl}}/org/{{.Org.LowerName}}/members"><strong>{{.Org.NumMembers}}</strong><span class="octicon octicon-chevron-right"></span></a>
-	    			{{end}}
-	    			<strong>{{.i18n.Tr "org.people"}}</strong>
-	    		</div>
-	    		<div class="panel-body member-avatar-group">
-	    			{{range .Members}}
+  </div>
+  <div class="grid-1-3 right">
+  	<div class="org-sidebar">
+    	<div class="panel panel-radius">
+    		<div class="panel-header">
+    			{{if $isMember}}
+    			<a class="text-grey right" href="{{AppSubUrl}}/org/{{.Org.LowerName}}/members"><strong>{{.Org.NumMembers}}</strong><span class="octicon octicon-chevron-right"></span></a>
+    			{{end}}
+    			<strong>{{.i18n.Tr "org.people"}}</strong>
+    		</div>
+    		<div class="panel-body member-avatar-group">
+    			{{range .Members}}
 						{{if or $isMember (.IsPublicMember $.Org.Id)}}
 						{{if or $isMember (.IsPublicMember $.Org.Id)}}
-	    				<a href="{{AppSubUrl}}/{{.Name}}" title="{{.Name}}"><img src="{{.AvatarLink}}"></a>
-	    				{{end}}
+	    			<a href="{{AppSubUrl}}/{{.Name}}" title="{{.Name}}"><img src="{{.AvatarLink}}"></a>
 	    			{{end}}
 	    			{{end}}
-	    		</div>
-	    		{{if .IsOrganizationOwner}}
-	    		<div class="panel-footer">
-	    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="{{AppSubUrl}}/org/{{.Org.LowerName}}/invitations/new">{{.i18n.Tr "org.invite_someone"}}</a>
-	    		</div>
-	    		{{end}}
-	    	</div>
-	    	{{if $isMember}}
-	    	<br>
-	    	<div class="panel panel-radius">
-	    		<div class="panel-header">
-	    			<a class="text-grey right" href="{{AppSubUrl}}/org/{{.Org.LowerName}}/teams"><strong>{{.Org.NumTeams}}</strong><span class="octicon octicon-chevron-right"></span></a>
-	    			<strong>{{.i18n.Tr "org.teams"}}</strong>
-	    		</div>
-	    		<div class="panel-body" id="org-home-team-list">
-	    			<ul>
-		    			{{range .Teams}}
-	    				<li>
-	    					<a class="text-black" href="{{AppSubUrl}}/org/{{$.Org.LowerName}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a>
-	    					<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p>
-	    				</li>
-		    			{{end}}
-	    			</ul>
-	    		</div>
-	    		{{if .IsOrganizationOwner}}
-	    		<div class="panel-footer">
-	    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="{{AppSubUrl}}/org/{{$.Org.LowerName}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a>
-	    		</div>
-	    		{{end}}
-	    	</div>
-	    	{{end}}
+    			{{end}}
+    		</div>
+    		{{if .IsOrganizationOwner}}
+    		<div class="panel-footer">
+    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="{{AppSubUrl}}/org/{{.Org.LowerName}}/invitations/new">{{.i18n.Tr "org.invite_someone"}}</a>
+    		</div>
+    		{{end}}
     	</div>
     	</div>
-    </div>
+    	{{if $isMember}}
+    	<br>
+    	<div class="panel panel-radius">
+    		<div class="panel-header">
+    			<a class="text-grey right" href="{{AppSubUrl}}/org/{{.Org.LowerName}}/teams"><strong>{{.Org.NumTeams}}</strong><span class="octicon octicon-chevron-right"></span></a>
+    			<strong>{{.i18n.Tr "org.teams"}}</strong>
+    		</div>
+    		<div class="panel-body" id="org-home-team-list">
+    			<ul>
+	    			{{range .Teams}}
+    				<li>
+    					<a class="text-black" href="{{AppSubUrl}}/org/{{$.Org.LowerName}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a>
+    					<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p>
+    				</li>
+	    			{{end}}
+    			</ul>
+    		</div>
+    		{{if .IsOrganizationOwner}}
+    		<div class="panel-footer">
+    			<a class="btn btn-medium btn-blue btn-link btn-radius" href="{{AppSubUrl}}/org/{{$.Org.LowerName}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a>
+    		</div>
+    		{{end}}
+    	</div>
+    	{{end}}
+  	</div>
+  </div>
 </div>
 </div>
 {{template "ng/base/footer" .}}
 {{template "ng/base/footer" .}}

+ 10 - 10
templates/org/settings/nav.tmpl

@@ -1,12 +1,12 @@
 <div id="setting-menu" class="grid-1-5 panel panel-radius left">
 <div id="setting-menu" class="grid-1-5 panel panel-radius left">
-    <div class="panel-header">
-    	<strong>{{.i18n.Tr "org.settings"}}</strong>
-    </div>
-    <div class="panel-body">
-        <ul class="menu menu-vertical switching-list grid-1-5 left">
-            <li {{if .PageIsSettingsOptions}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.LowerName}}/settings">{{.i18n.Tr "org.settings.options"}}</a></li>
-            <li {{if .PageIsSettingsHooks}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.LowerName}}/settings/hooks">{{.i18n.Tr "repo.settings.hooks"}}</a></li>
-            <li {{if .PageIsSettingsDelete}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.LowerName}}/settings/delete">{{.i18n.Tr "org.settings.delete"}}</a></li>
-        </ul>
-    </div>
+  <div class="panel-header">
+  	<strong>{{.i18n.Tr "org.settings"}}</strong>
+  </div>
+  <div class="panel-body">
+    <ul class="menu menu-vertical switching-list grid-1-5 left">
+      <li {{if .PageIsSettingsOptions}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.Name}}/settings">{{.i18n.Tr "org.settings.options"}}</a></li>
+      <li {{if .PageIsSettingsHooks}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.Name}}/settings/hooks">{{.i18n.Tr "repo.settings.hooks"}}</a></li>
+      <li {{if .PageIsSettingsDelete}}class="current"{{end}}><a href="{{AppSubUrl}}/org/{{.Org.Name}}/settings/delete">{{.i18n.Tr "org.settings.delete"}}</a></li>
+    </ul>
+  </div>
 </div>
 </div>

+ 57 - 57
templates/org/settings/options.tmpl

@@ -2,63 +2,63 @@
 {{template "ng/base/header" .}}
 {{template "ng/base/header" .}}
 {{template "org/base/header" .}}
 {{template "org/base/header" .}}
 <div id="setting-wrapper" class="main-wrapper">
 <div id="setting-wrapper" class="main-wrapper">
-    <div id="org-setting" class="container clear">
-        {{template "org/settings/nav" .}}
-        <div class="grid-4-5 left">
-            <div class="setting-content">
-                {{template "ng/base/alert" .}}
-                <div id="setting-content">
-                    <div id="user-profile-setting-content" class="panel panel-radius">
-                        <div class="panel-header">
-                        	<strong>{{.i18n.Tr "org.settings.options"}}</strong>
-                        </div>
-                        <form class="form form-align panel-body" id="org-setting-form" action="{{AppSubUrl}}/org/{{.Org.LowerName}}/settings" method="post">
-                            {{.CsrfTokenHtml}}
-                			<input type="hidden" name="action" value="update">
-                            <div class="field">
-                                <label class="req" for="orgname">{{.i18n.Tr "username"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="orgname" name="uname" value="{{.Org.Name}}" data-orgname="{{.Org.Name}}" required />
-                            </div>
-                            <div class="white-popup-block mfp-hide" id="change-orgname-modal">
-                                <h1 class="text-red">{{.i18n.Tr "org.settings.change_orgname"}}</h1>
-                                <p>{{.i18n.Tr "org.settings.change_orgname_desc"}}</p>
-                                <br>
-                                <button class="btn btn-red btn-large btn-radius" id="change-orgname-submit">{{.i18n.Tr "settings.continue"}}</button>
-                                <button class="btn btn-large btn-radius popup-modal-dismiss">{{.i18n.Tr "settings.cancel"}}</button>
-                            </div>
-                            <div class="field">
-                                <label for="full-name">{{.i18n.Tr "org.settings.full_name"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_FullName}}ipt-error{{end}}" id="full-name" name="fullname" value="{{.Org.FullName}}" />
-                            </div>
-                            <div class="field">
-                                <label class="req" for="email">{{.i18n.Tr "email"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_Email}}ipt-error{{end}}" id="email" name="email" type="email" value="{{.Org.Email}}" required />
-                            </div>
-				            <div class="field clear">
-				                <label class="left" for="desc">{{.i18n.Tr "org.org_desc"}}</label>
-				                <textarea class="ipt ipt-large ipt-radius {{if .Err_Description}}ipt-error{{end}}" id="desc" name="desc">{{.Org.Description}}</textarea>
-				            </div>
-                            <div class="field">
-                                <label for="website">{{.i18n.Tr "org.settings.website"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_Website}}ipt-error{{end}}" id="website" name="website" type="url" value="{{.Org.Website}}" />
-                            </div>
-                            <div class="field">
-                                <label for="location">{{.i18n.Tr "org.settings.location"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_Location}}ipt-error{{end}}" id="location" name="location" type="text" value="{{.Org.Location}}" />
-                            </div>
-                            <div class="field">
-                                <label for="gravatar-email">Gravatar {{.i18n.Tr "email"}}</label>
-                                <input class="ipt ipt-large ipt-radius {{if .Err_Avatar}}ipt-error{{end}}" id="gravatar-email" name="avatar" type="text" value="{{.Org.AvatarEmail}}" />
-                            </div>
-                            <div class="field">
-                                <span class="form-label"></span>
-                                <button class="btn btn-green btn-large btn-radius" id="change-orgname-btn" href="#change-orgname-modal">{{.i18n.Tr "org.settings.update_settings"}}</button>
-                            </div>
-                        </form>
-                    </div>
-                </div>
+  <div id="org-setting" class="container clear">
+    {{template "org/settings/nav" .}}
+    <div class="grid-4-5 left">
+      <div class="setting-content">
+        {{template "ng/base/alert" .}}
+        <div id="setting-content">
+          <div id="user-profile-setting-content" class="panel panel-radius">
+            <div class="panel-header">
+              <strong>{{.i18n.Tr "org.settings.options"}}</strong>
             </div>
             </div>
-	    </div>
-	</div>
+            <form class="form form-align panel-body" id="org-setting-form" action="{{AppSubUrl}}/org/{{.Org.LowerName}}/settings" method="post">
+              {{.CsrfTokenHtml}}
+              <input type="hidden" name="action" value="update">
+              <div class="field">
+                <label class="req" for="orgname">{{.i18n.Tr "username"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_UserName}}ipt-error{{end}}" id="orgname" name="uname" value="{{.Org.Name}}" data-orgname="{{.Org.Name}}" required />
+              </div>
+              <div class="white-popup-block mfp-hide" id="change-orgname-modal">
+                <h1 class="text-red">{{.i18n.Tr "org.settings.change_orgname"}}</h1>
+                <p>{{.i18n.Tr "org.settings.change_orgname_desc"}}</p>
+                <br>
+                <button class="btn btn-red btn-large btn-radius" id="change-orgname-submit">{{.i18n.Tr "settings.continue"}}</button>
+                <button class="btn btn-large btn-radius popup-modal-dismiss">{{.i18n.Tr "settings.cancel"}}</button>
+              </div>
+              <div class="field">
+                <label for="full-name">{{.i18n.Tr "org.settings.full_name"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_FullName}}ipt-error{{end}}" id="full-name" name="fullname" value="{{.Org.FullName}}" />
+              </div>
+              <div class="field">
+                <label class="req" for="email">{{.i18n.Tr "email"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_Email}}ipt-error{{end}}" id="email" name="email" type="email" value="{{.Org.Email}}" required />
+              </div>
+              <div class="field clear">
+                <label class="left" for="desc">{{.i18n.Tr "org.org_desc"}}</label>
+                <textarea class="ipt ipt-large ipt-radius {{if .Err_Description}}ipt-error{{end}}" id="desc" name="desc">{{.Org.Description}}</textarea>
+              </div>
+              <div class="field">
+                <label for="website">{{.i18n.Tr "org.settings.website"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_Website}}ipt-error{{end}}" id="website" name="website" type="url" value="{{.Org.Website}}" />
+              </div>
+              <div class="field">
+                <label for="location">{{.i18n.Tr "org.settings.location"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_Location}}ipt-error{{end}}" id="location" name="location" type="text" value="{{.Org.Location}}" />
+              </div>
+              <div class="field">
+                <label for="gravatar-email">Gravatar {{.i18n.Tr "email"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_Avatar}}ipt-error{{end}}" id="gravatar-email" name="avatar" type="text" value="{{.Org.AvatarEmail}}" />
+              </div>
+              <div class="field">
+                <span class="form-label"></span>
+                <button class="btn btn-green btn-large btn-radius" id="change-orgname-btn" href="#change-orgname-modal">{{.i18n.Tr "org.settings.update_settings"}}</button>
+              </div>
+            </form>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
 </div>
 </div>
 {{template "ng/base/footer" .}}
 {{template "ng/base/footer" .}}

+ 2 - 2
templates/repo/bare.tmpl

@@ -23,7 +23,7 @@
                         <h2>{{.i18n.Tr "repo.clone_this_repo"}}</h2>
                         <h2>{{.i18n.Tr "repo.clone_this_repo"}}</h2>
                         <button class="btn btn-blue current left btn-left-radius" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}">SSH</button>
                         <button class="btn btn-blue current left btn-left-radius" id="repo-clone-ssh" data-link="{{.CloneLink.SSH}}">SSH</button>
                         <button class="btn btn-gray left" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}">HTTPS</button>
                         <button class="btn btn-gray left" id="repo-clone-https" data-link="{{.CloneLink.HTTPS}}">HTTPS</button>
-                        <input id="repo-clone-url" type="text" class="ipt ipt-disabled left" value="{{.CloneLink.SSH}}" readonly />
+                        <input id="repo-clone-url" type="text" class="ipt ipt-disabled left" value="{{.CloneLink.SSH}}" onclick="this.select()" readonly />
                         <button class="btn btn-black left btn-right-radius" id="repo-clone-copy" data-copy-val="val" data-copy-from="#repo-clone-url">{{.i18n.Tr "repo.copy_link"}}</button>
                         <button class="btn btn-black left btn-right-radius" id="repo-clone-copy" data-copy-val="val" data-copy-from="#repo-clone-url">{{.i18n.Tr "repo.copy_link"}}</button>
                         <p class="text-center" id="repo-clone-help">{{.i18n.Tr "repo.clone_helper" | Str2html}}</p>
                         <p class="text-center" id="repo-clone-help">{{.i18n.Tr "repo.clone_helper" | Str2html}}</p>
                         <hr/>
                         <hr/>
@@ -50,4 +50,4 @@ git push -u origin master</code></pre>
         </div>
         </div>
     </div>
     </div>
 </div>
 </div>
-{{template "ng/base/footer" .}}
+{{template "ng/base/footer" .}}

+ 5 - 5
templates/repo/diff.tmpl

@@ -105,14 +105,14 @@
                 {{else}}
                 {{else}}
                 <table>
                 <table>
                     <tbody>
                     <tbody>
-                        {{range $j, $section := $file.Sections}}
-                        {{range $k, $line := $section.Lines}}                        
-                        <tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$i}} ol-{{$i}}">
+                        {{range .Sections}}
+                        {{range $k, $line := .Lines}}
+                        <tr class="{{DiffLineTypeToStr .Type}}-code nl-{{$k}} ol-{{$k}}">
                             <td class="lines-num lines-num-old">
                             <td class="lines-num lines-num-old">
-                                <span rel="diff-{{Add $i 1}}L{{$j}}{{$k}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
+                                <span rel="{{if $line.LeftIdx}}diff-{{Sha1 $file.Name}}L{{$line.LeftIdx}}{{end}}">{{if $line.LeftIdx}}{{$line.LeftIdx}}{{end}}</span>
                             </td>
                             </td>
                             <td class="lines-num lines-num-new">
                             <td class="lines-num lines-num-new">
-                                <span rel="diff-{{Add $i 1}}L{{$j}}{{$k}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
+                                <span rel="{{if $line.RightIdx}}diff-{{Sha1 $file.Name}}R{{$line.RightIdx}}{{end}}">{{if $line.RightIdx}}{{$line.RightIdx}}{{end}}</span>
                             </td>
                             </td>
                             
                             
                             <td class="lines-code">
                             <td class="lines-code">

+ 1 - 1
templates/repo/header.tmpl

@@ -49,7 +49,7 @@
                 </a>
                 </a>
             </li>
             </li>
             <li id="repo-header-fork">
             <li id="repo-header-fork">
-                <a id="repo-header-fork-btn" {{if or (not $.IsRepositoryTrueOwner) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}>
+                <a id="repo-header-fork-btn" {{if or (not $.IsRepositoryAdmin) $.Owner.IsOrganization}}href="{{AppSubUrl}}/repo/fork?fork_id={{.Id}}"{{end}}>
                     <button class="btn btn-gray text-bold btn-radius">
                     <button class="btn btn-gray text-bold btn-radius">
                         <i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
                         <i class="octicon octicon-repo-forked"></i>{{$.i18n.Tr "repo.fork"}}
                         <span class="num">{{.NumForks}}</span>
                         <span class="num">{{.NumForks}}</span>

+ 2 - 2
templates/repo/migrate.tmpl

@@ -7,8 +7,8 @@
         <div class="panel-content">
         <div class="panel-content">
             {{template "ng/base/alert" .}}
             {{template "ng/base/alert" .}}
             <div class="field">
             <div class="field">
-                <label class="req" for="url">HTTPS URL</label>
-                <input class="ipt ipt-large ipt-radius {{if .Err_HttpsUrl}}ipt-error{{end}}" id="url" name="url" type="text" value="{{.url}}" required />
+                <label class="req" for="clone_addr">{{.i18n.Tr "repo.migrate.clone_address"}}</label>
+                <input class="ipt ipt-large ipt-radius {{if .Err_CloneAddr}}ipt-error{{end}}" id="clone_addr" name="clone_addr" type="text" value="{{.clone_addr}}" required />
             </div>
             </div>
             <div class="field">
             <div class="field">
                 <span class="form-label"></span>
                 <span class="form-label"></span>

+ 0 - 5
templates/repo/settings/options.tmpl

@@ -59,11 +59,6 @@
 					                <input class="ipt-chk" id="visibility" name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}} />
 					                <input class="ipt-chk" id="visibility" name="private" type="checkbox" {{if .Repository.IsPrivate}}checked{{end}} />
 					                <span>{{.i18n.Tr "repo.visiblity_helper" | Str2html}}</span>
 					                <span>{{.i18n.Tr "repo.visiblity_helper" | Str2html}}</span>
 					            </div>
 					            </div>
-					            <div class="field">
-					                <label for="goget">{{.i18n.Tr "repo.goget_meta"}}</label>
-					                <input class="ipt-chk" id="goget" name="goget" type="checkbox" {{if .Repository.IsGoget}}checked{{end}} />
-					                <span>{{.i18n.Tr "repo.goget_meta_helper" | Str2html}}</span>
-					            </div>
 	                            <div class="field">
 	                            <div class="field">
 	                                <span class="form-label"></span>
 	                                <span class="form-label"></span>
 	                                <button class="btn btn-green btn-large btn-radius" id="change-reponame-btn" href="#change-reponame-modal">{{.i18n.Tr "repo.settings.update_settings"}}</button>
 	                                <button class="btn btn-green btn-large btn-radius" id="change-reponame-btn" href="#change-reponame-modal">{{.i18n.Tr "repo.settings.update_settings"}}</button>

+ 1 - 1
templates/repo/sidebar.tmpl

@@ -20,7 +20,7 @@
         <!-- <li>
         <!-- <li>
             <a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a>
             <a class="radius" href="#"><i class="octicon octicon-organization"></i>contributors <span class="num right label label-gray label-radius">43</span></a>
         </li> -->
         </li> -->
-        {{if .IsRepositoryTrueOwner}}
+        {{if .IsRepositoryAdmin}}
         <li class="border-bottom"></li>
         <li class="border-bottom"></li>
         <li>
         <li>
             <a class="radius" href="{{.RepoLink}}/settings"><i class="octicon octicon-tools"></i>{{.i18n.Tr "repo.settings"}}</a>
             <a class="radius" href="{{.RepoLink}}/settings"><i class="octicon octicon-tools"></i>{{.i18n.Tr "repo.settings"}}</a>

+ 1 - 1
templates/repo/toolbar.tmpl

@@ -35,7 +35,7 @@
                             <li><a href="#">Pulse</a></li>
                             <li><a href="#">Pulse</a></li>
                             <li><a href="#">Network</a></li>
                             <li><a href="#">Network</a></li>
                         </ul>
                         </ul>
-                    </li> -->{{end}}{{if .IsRepositoryTrueOwner}}
+                    </li> -->{{end}}{{if .IsRepositoryAdmin}}
                     <li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a>
                     <li class="{{if .IsRepoToolbarSetting}}active{{end}}"><a href="{{.RepoLink}}/settings">Settings</a>
                     </li>{{end}}
                     </li>{{end}}
                 </ul>
                 </ul>

+ 2 - 0
templates/user/auth/signin.tmpl

@@ -26,10 +26,12 @@
                 <button class="btn btn-green btn-large btn-radius">{{.i18n.Tr "sign_in"}}</button>&nbsp;&nbsp;&nbsp;&nbsp;
                 <button class="btn btn-green btn-large btn-radius">{{.i18n.Tr "sign_in"}}</button>&nbsp;&nbsp;&nbsp;&nbsp;
                 {{if not .IsSocialLogin}}<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a>{{end}}
                 {{if not .IsSocialLogin}}<a href="{{AppSubUrl}}/user/forget_password">{{.i18n.Tr "auth.forget_password"}}</a>{{end}}
             </div>
             </div>
+            {{if .ShowRegistrationButton}}
             <div class="field">
             <div class="field">
                 <label></label>
                 <label></label>
                 <a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
                 <a href="{{AppSubUrl}}/user/sign_up">{{.i18n.Tr "auth.sign_up_now" | Str2html}}</a>
             </div>
             </div>
+            {{end}}
             {{if and (not .IsSocialLogin) .OauthEnabled}}
             {{if and (not .IsSocialLogin) .OauthEnabled}}
             <hr/>
             <hr/>
             <div id="sign-social" class="text-center social-buttons">
             <div id="sign-social" class="text-center social-buttons">

+ 1 - 1
templates/user/profile.tmpl

@@ -28,7 +28,7 @@
                     {{if .Owner.Website}}
                     {{if .Owner.Website}}
                     <li class="list-group-item"><i class="octicon octicon-link"></i>&nbsp;&nbsp;<a target="_blank" href="{{.Owner.Website}}">{{.Owner.Website}}</a></li>
                     <li class="list-group-item"><i class="octicon octicon-link"></i>&nbsp;&nbsp;<a target="_blank" href="{{.Owner.Website}}">{{.Owner.Website}}</a></li>
                     {{end}}
                     {{end}}
-                    <li class="list-group-item"><i class="octicon octicon-clock"></i>&nbsp;&nbsp;{{.i18n.Tr "user.join_on"}} {{DateFormat .Owner.Created "M d, Y"}}</li>
+                    <li class="list-group-item"><i class="octicon octicon-clock"></i>&nbsp;&nbsp;{{.i18n.Tr "user.join_on"}} {{DateFmtShort .Owner.Created}}</li>
                 </ul>
                 </ul>
                 <hr>
                 <hr>
                 <ul class="list-no-style">
                 <ul class="list-no-style">

+ 1 - 1
templates/user/settings/applications.tmpl

@@ -22,7 +22,7 @@
                                 <i class="fa fa-send fa-2x left"></i>
                                 <i class="fa fa-send fa-2x left"></i>
                                 <div class="ssh-content left">
                                 <div class="ssh-content left">
                                     <p><strong>{{.Name}}</strong></p>
                                     <p><strong>{{.Name}}</strong></p>
-                                    <p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span> —  <i class="octicon octicon-info"></i>{{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{DateFormat .Updated "M d, Y"}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i></p>
+                                    <p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span> —  <i class="octicon octicon-info"></i>{{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} {{DateFmtShort .Updated}}{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i></p>
                                 </div>
                                 </div>
                                 <a href="{{AppSubUrl}}/user/settings/applications?remove={{.Id}}">
                                 <a href="{{AppSubUrl}}/user/settings/applications?remove={{.Id}}">
                                 	<button class="btn btn-small btn-red btn-radius ssh-btn right">{{$.i18n.Tr "settings.delete_token"}}</button>
                                 	<button class="btn btn-small btn-red btn-radius ssh-btn right">{{$.i18n.Tr "settings.delete_token"}}</button>

+ 3 - 3
templates/user/settings/email.tmpl

@@ -16,7 +16,7 @@
                             {{range .Emails}}
                             {{range .Emails}}
                             <li class="email clear">
                             <li class="email clear">
                                 <div class="email-content left">
                                 <div class="email-content left">
-									<p><strong>{{.Email}}</strong> {{if .IsPrimary}} <span class="email-primary">{{$.i18n.Tr "settings.primary"}}</span> {{end}}</p>
+									<p><strong>{{.Email}}</strong> {{if .IsPrimary}} <span class="text-red">{{$.i18n.Tr "settings.primary"}}</span> {{end}}</p>
 							   </div>
 							   </div>
 							   {{if not .IsPrimary}}
 							   {{if not .IsPrimary}}
 							   {{if .IsActivated}}
 							   {{if .IsActivated}}
@@ -24,14 +24,14 @@
                                     {{$.CsrfTokenHtml}}
                                     {{$.CsrfTokenHtml}}
                                     <input name="_method" type="hidden" value="PRIMARY">
                                     <input name="_method" type="hidden" value="PRIMARY">
                                     <input name="id" type="hidden" value="{{.Id}}">
                                     <input name="id" type="hidden" value="{{.Id}}">
-                                    <button class="right email-btn btn btn-green btn-radius btn-small">{{$.i18n.Tr "settings.primary_email"}}</button>
+                                    <button class="right email-btn btn btn-small btn-green btn-radius">{{$.i18n.Tr "settings.primary_email"}}</button>
                                 </form>
                                 </form>
 								{{end}}
 								{{end}}
                                 <form action="{{AppSubUrl}}/user/settings/email" method="post">
                                 <form action="{{AppSubUrl}}/user/settings/email" method="post">
                                     {{$.CsrfTokenHtml}}
                                     {{$.CsrfTokenHtml}}
                                     <input name="_method" type="hidden" value="DELETE">
                                     <input name="_method" type="hidden" value="DELETE">
                                     <input name="id" type="hidden" value="{{.Id}}">
                                     <input name="id" type="hidden" value="{{.Id}}">
-                                    <button class="right email-btn btn btn-red btn-radius btn-small">{{$.i18n.Tr "settings.delete_email"}}</button>
+                                    <button class="right email-btn btn btn-small btn-red btn-radius" style="margin-right: 5px">{{$.i18n.Tr "settings.delete_email"}}</button>
                                 </form>
                                 </form>
 							   {{end}}
 							   {{end}}
                             </li>
                             </li>

+ 1 - 1
templates/user/settings/social.tmpl

@@ -18,7 +18,7 @@
                                 <div class="ssh-content left">
                                 <div class="ssh-content left">
                                     <p><strong>{{Oauth2Name .Type}}</strong></p>
                                     <p><strong>{{Oauth2Name .Type}}</strong></p>
                                     <p class="print">{{.Identity}}</p>
                                     <p class="print">{{.Identity}}</p>
-                                    <p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span> —  <i class="octicon octicon-info"></i>{{$.i18n.Tr "settings.last_used"}} {{DateFormat .Updated "M d, Y"}}</i></p>
+                                    <p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span> —  <i class="octicon octicon-info"></i>{{$.i18n.Tr "settings.last_used"}} {{DateFmtShort .Updated}}</i></p>
                                 </div>
                                 </div>
                                 <a class="right btn btn-small btn-red btn-header btn-radius" href="{{AppSubUrl}}/user/settings/social?remove={{.Id}}">{{$.i18n.Tr "settings.unbind"}}</a>
                                 <a class="right btn btn-small btn-red btn-header btn-radius" href="{{AppSubUrl}}/user/settings/social?remove={{.Id}}">{{$.i18n.Tr "settings.unbind"}}</a>
                             </li>
                             </li>

+ 1 - 1
templates/user/settings/sshkeys.tmpl

@@ -23,7 +23,7 @@
                                 <div class="ssh-content left">
                                 <div class="ssh-content left">
                                     <p><strong>{{.Name}}</strong></p>
                                     <p><strong>{{.Name}}</strong></p>
                                     <p class="print">{{.Fingerprint}}</p>
                                     <p class="print">{{.Fingerprint}}</p>
-                                    <p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFormat .Created "r"}}">{{DateFormat .Created "M d, Y"}}</span> —  <i class="octicon octicon-info"></i>{{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} <span title="{{DateFormat .Updated "r"}}">{{DateFormat .Updated "M d, Y"}}</span>{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i></p>
+                                    <p class="activity"><i>{{$.i18n.Tr "settings.add_on"}} <span title="{{DateFmtLong .Created}}">{{DateFmtShort .Created}}</span> —  <i class="octicon octicon-info"></i>{{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} <span title="{{DateFmtLong .Updated}}">{{DateFmtShort .Updated}}</span>{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i></p>
                                 </div>
                                 </div>
                                 <form action="{{AppSubUrl}}/user/settings/ssh" method="post">
                                 <form action="{{AppSubUrl}}/user/settings/ssh" method="post">
                                     {{$.CsrfTokenHtml}}
                                     {{$.CsrfTokenHtml}}