Browse Source

Merge branch 'dev' of github.com:gogits/gogs into access

Unknwon 10 years ago
parent
commit
c5c467a9cd

+ 2 - 1
.gitignore

@@ -1,6 +1,7 @@
 .DS_Store
 *.db
 *.log
+log/
 custom/
 data/
 .vendor/
@@ -34,4 +35,4 @@ docker/fig.yml
 docker/docker/Dockerfile
 docker/docker/init_gogs.sh
 gogs.sublime-project
-gogs.sublime-workspace
+gogs.sublime-workspace

+ 3 - 0
.gopmfile

@@ -3,6 +3,7 @@ path = github.com/gogits/gogs
 
 [deps]
 github.com/beego/memcache = commit:2aea774416
+github.com/bradfitz/gomemcache = 
 github.com/Unknwon/cae = commit:2e70a1351b
 github.com/Unknwon/com = commit:d9bcf409c8
 github.com/Unknwon/i18n = commit:1e88666229
@@ -27,6 +28,8 @@ github.com/microcosm-cc/bluemonday =
 github.com/nfnt/resize = commit:8f44931448
 github.com/russross/blackfriday = commit:05b8cefd6a
 github.com/shurcooL/go = commit:48293cbc7a
+golang.org/x/net = 
+golang.org/x/text = 
 gopkg.in/ini.v1 = commit:28ad8c408b
 gopkg.in/redis.v2 = commit:e617904962
 

+ 2 - 2
README.md

@@ -28,7 +28,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
 
 - 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/Unknown/gogs) or go down to **Installation -> Install from binary** section!
+- 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)!
 
@@ -52,7 +52,7 @@ The goal of this project is to make the easiest, fastest and most painless way t
 - Drone CI integration
 - Supports MySQL, PostgreSQL and SQLite3
 - Social account login(GitHub, Google, QQ, Weibo)
-- Multi-language support([9 languages](https://crowdin.com/project/gogs))
+- Multi-language support([10 languages](https://crowdin.com/project/gogs))
 
 ## System Requirements
 

+ 3 - 3
README_ZH.md

@@ -1,4 +1,4 @@
-Gogs - Go Git Service [![wercker status](https://app.wercker.com/status/ad0bdb0bc450ac6f09bc56b9640a50aa/s/ "wercker status")](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
+Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?branch=master)](https://travis-ci.org/gogits/gogs)
 =====================
 
 Gogs(Go Git Service) 是一个基于 Go 语言的自助 Git 服务。
@@ -15,7 +15,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 
 - 有关项目设计、已知问题和变更日志,请通过  [使用手册](http://gogs.io/docs/intro/) 查看。
 - 您可以到 [Trello Board](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。
-- 想要先睹为快?通过 [在线体验](https://try.gogs.io/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
+- 想要先睹为快?通过 [在线体验](https://try.gogs.io/unknwon/gogs) 或查看 **安装部署 -> 二进制安装** 小节。
 - 使用过程中遇到问题?尝试从 [故障排查](http://gogs.io/docs/intro/troubleshooting.md) 页面获取帮助。
 - 希望帮助多国语言界面的翻译吗?请立即访问 [Crowdin](https://crowdin.com/project/gogs)!
 
@@ -39,7 +39,7 @@ Gogs 的目标是打造一个最简单、最快速和最轻松的方式搭建自
 - Drone CI 持续部署集成
 - 支持 MySQL、PostgreSQL 以及 SQLite3 数据库
 - 社交帐号登录(GitHub、Google、QQ、微博)
-- 多语言支持([9 种语言]([more](https://crowdin.com/project/gogs)))
+- 多语言支持([10 种语言]([more](https://crowdin.com/project/gogs)))
 
 ## 系统要求
 

+ 2 - 0
conf/app.ini

@@ -89,6 +89,8 @@ ENABLE_REVERSE_PROXY_AUTO_REGISTERATION = false
 TASK_INTERVAL = 1
 ; Deliver timeout in seconds
 DELIVER_TIMEOUT = 5
+; Allow insecure certification
+SKIP_TLS_VERIFY = false
 
 [mailer]
 ENABLED = false

+ 1 - 0
conf/locale/locale_en-US.ini

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

+ 2 - 2
conf/locale/locale_ja-JP.ini

@@ -516,8 +516,8 @@ dashboard.git_gc_repos=リポジトリでのガベージコレクションを実
 dashboard.git_gc_repos_success=すべてのリポジトリは正常にガベージ コレクションを行いました。
 dashboard.resync_all_sshkeys='.ssh/ autorized_key' ファイルを再生成します。(警告:Gogsキー以外は失われます)
 dashboard.resync_all_sshkeys_success=すべての公開鍵が正常に書き換えられました。
-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=リポジトリの update フックをすべて再更新する(カスタムの設定パスが変更されたときに必要)
+dashboard.resync_all_update_hooks_success=リポジトリの update フックがすべて正常に再更新されました。
 
 dashboard.server_uptime=サーバーの稼働時間
 dashboard.current_goroutine=現在のGoroutine

+ 1 - 1
gogs.go

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

+ 3 - 0
models/models.go

@@ -24,7 +24,10 @@ import (
 type Engine interface {
 	Delete(interface{}) (int64, error)
 	Exec(string, ...interface{}) (sql.Result, error)
+	Get(interface{}) (bool, error)
 	Insert(...interface{}) (int64, error)
+	Id(interface{}) *xorm.Session
+	Where(string, ...interface{}) *xorm.Session
 }
 
 var (

+ 51 - 43
models/org.go

@@ -12,7 +12,6 @@ import (
 	"strings"
 
 	"github.com/Unknwon/com"
-	"github.com/go-xorm/xorm"
 
 	"github.com/gogits/gogs/modules/base"
 )
@@ -391,7 +390,7 @@ func RemoveOrgUser(orgId, uid int64) error {
 		return err
 	}
 	for _, t := range ts {
-		if err = removeTeamMemberWithSess(org.Id, t.Id, u.Id, sess); err != nil {
+		if err = removeTeamMember(sess, org.Id, t.Id, u.Id); err != nil {
 			return err
 		}
 	}
@@ -486,18 +485,18 @@ func (t *Team) RemoveMember(uid int64) error {
 }
 
 // addAccessWithAuthorize inserts or updates access with given mode.
-func addAccessWithAuthorize(sess *xorm.Session, access *Access, mode AccessType) error {
-	has, err := x.Get(access)
+func addAccessWithAuthorize(e Engine, access *Access, mode AccessType) error {
+	has, err := e.Get(access)
 	if err != nil {
 		return fmt.Errorf("fail to get access: %v", err)
 	}
 	access.Mode = mode
 	if has {
-		if _, err = sess.Id(access.Id).Update(access); err != nil {
+		if _, err = e.Id(access.Id).Update(access); err != nil {
 			return fmt.Errorf("fail to update access: %v", err)
 		}
 	} else {
-		if _, err = sess.Insert(access); err != nil {
+		if _, err = e.Insert(access); err != nil {
 			return fmt.Errorf("fail to insert access: %v", err)
 		}
 	}
@@ -536,7 +535,7 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
 	mode := AuthorizeToAccessType(t.Authorize)
 
 	for _, u := range t.Members {
-		auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id)
+		auth, err := getHighestAuthorize(sess, t.OrgId, u.Id, repo.Id, t.Id)
 		if err != nil {
 			sess.Rollback()
 			return err
@@ -552,7 +551,7 @@ func (t *Team) AddRepository(repo *Repository) (err error) {
 				return err
 			}
 		}
-		if err = WatchRepo(u.Id, repo.Id, true); err != nil {
+		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
 			sess.Rollback()
 			return err
 		}
@@ -593,7 +592,7 @@ func (t *Team) RemoveRepository(repoId int64) error {
 
 	// Remove access to team members.
 	for _, u := range t.Members {
-		auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, t.Id)
+		auth, err := getHighestAuthorize(sess, t.OrgId, u.Id, repo.Id, t.Id)
 		if err != nil {
 			sess.Rollback()
 			return err
@@ -607,7 +606,7 @@ func (t *Team) RemoveRepository(repoId int64) error {
 			if _, err = sess.Delete(access); err != nil {
 				sess.Rollback()
 				return fmt.Errorf("fail to delete access: %v", err)
-			} else if err = WatchRepo(u.Id, repo.Id, false); err != nil {
+			} else if err = watchRepo(sess, u.Id, repo.Id, false); err != nil {
 				sess.Rollback()
 				return err
 			}
@@ -678,10 +677,9 @@ func GetTeam(orgId int64, name string) (*Team, error) {
 	return t, nil
 }
 
-// GetTeamById returns team by given ID.
-func GetTeamById(teamId int64) (*Team, error) {
+func getTeamById(e Engine, teamId int64) (*Team, error) {
 	t := new(Team)
-	has, err := x.Id(teamId).Get(t)
+	has, err := e.Id(teamId).Get(t)
 	if err != nil {
 		return nil, err
 	} else if !has {
@@ -690,9 +688,13 @@ func GetTeamById(teamId int64) (*Team, error) {
 	return t, nil
 }
 
-// GetHighestAuthorize returns highest repository authorize level for given user and team.
-func GetHighestAuthorize(orgId, uid, repoId, teamId int64) (AuthorizeType, error) {
-	ts, err := GetUserTeams(orgId, uid)
+// GetTeamById returns team by given ID.
+func GetTeamById(teamId int64) (*Team, error) {
+	return getTeamById(x, teamId)
+}
+
+func getHighestAuthorize(e Engine, orgId, uid, repoId, teamId int64) (AuthorizeType, error) {
+	ts, err := getUserTeams(e, orgId, uid)
 	if err != nil {
 		return 0, err
 	}
@@ -714,6 +716,11 @@ func GetHighestAuthorize(orgId, uid, repoId, teamId int64) (AuthorizeType, error
 	return auth, nil
 }
 
+// GetHighestAuthorize returns highest repository authorize level for given user and team.
+func GetHighestAuthorize(orgId, uid, repoId, teamId int64) (AuthorizeType, error) {
+	return getHighestAuthorize(x, orgId, uid, repoId, teamId)
+}
+
 // UpdateTeam updates information of team.
 func UpdateTeam(t *Team, authChanged bool) (err error) {
 	if !IsLegalName(t.Name) {
@@ -866,10 +873,14 @@ type TeamUser struct {
 	TeamId int64
 }
 
+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))
+	return has
+}
+
 // IsTeamMember returns true if given user is a member of team.
 func IsTeamMember(orgId, teamId, uid int64) bool {
-	has, _ := x.Where("uid=?", uid).And("org_id=?", orgId).And("team_id=?", teamId).Get(new(TeamUser))
-	return has
+	return isTeamMember(x, orgId, teamId, uid)
 }
 
 // GetTeamMembers returns all members in given team of organization.
@@ -879,17 +890,16 @@ func GetTeamMembers(orgId, teamId int64) ([]*User, error) {
 	return us, err
 }
 
-// GetUserTeams returns all teams that user belongs to in given organization.
-func GetUserTeams(orgId, uid int64) ([]*Team, error) {
+func getUserTeams(e Engine, orgId, uid int64) ([]*Team, error) {
 	tus := make([]*TeamUser, 0, 5)
-	if err := x.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
+	if err := e.Where("uid=?", uid).And("org_id=?", orgId).Find(&tus); err != nil {
 		return nil, err
 	}
 
 	ts := make([]*Team, len(tus))
 	for i, tu := range tus {
 		t := new(Team)
-		has, err := x.Id(tu.TeamId).Get(t)
+		has, err := e.Id(tu.TeamId).Get(t)
 		if err != nil {
 			return nil, err
 		} else if !has {
@@ -900,6 +910,11 @@ func GetUserTeams(orgId, uid int64) ([]*Team, error) {
 	return ts, nil
 }
 
+// GetUserTeams returns all teams that user belongs to in given organization.
+func GetUserTeams(orgId, uid int64) ([]*Team, error) {
+	return getUserTeams(x, orgId, uid)
+}
+
 // AddTeamMember adds new member to given team of given organization.
 func AddTeamMember(orgId, teamId, uid int64) error {
 	if IsTeamMember(orgId, teamId, uid) {
@@ -956,7 +971,7 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 	// Give access to team repositories.
 	mode := AuthorizeToAccessType(t.Authorize)
 	for _, repo := range t.Repos {
-		auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId)
+		auth, err := getHighestAuthorize(sess, t.OrgId, u.Id, repo.Id, teamId)
 		if err != nil {
 			sess.Rollback()
 			return err
@@ -993,13 +1008,13 @@ func AddTeamMember(orgId, teamId, uid int64) error {
 	return sess.Commit()
 }
 
-func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) error {
-	if !IsTeamMember(orgId, teamId, uid) {
+func removeTeamMember(e Engine, orgId, teamId, uid int64) error {
+	if !isTeamMember(e, orgId, teamId, uid) {
 		return nil
 	}
 
 	// Get team and its repositories.
-	t, err := GetTeamById(teamId)
+	t, err := getTeamById(e, teamId)
 	if err != nil {
 		return err
 	}
@@ -1033,19 +1048,16 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro
 		TeamId: teamId,
 	}
 
-	if _, err := sess.Delete(tu); err != nil {
-		sess.Rollback()
+	if _, err := e.Delete(tu); err != nil {
 		return err
-	} else if _, err = sess.Id(t.Id).AllCols().Update(t); err != nil {
-		sess.Rollback()
+	} else if _, err = e.Id(t.Id).AllCols().Update(t); err != nil {
 		return err
 	}
 
 	// Delete access to team repositories.
 	for _, repo := range t.Repos {
-		auth, err := GetHighestAuthorize(t.OrgId, u.Id, repo.Id, teamId)
+		auth, err := getHighestAuthorize(e, t.OrgId, u.Id, repo.Id, teamId)
 		if err != nil {
-			sess.Rollback()
 			return err
 		}
 
@@ -1055,17 +1067,14 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro
 		}
 		// Delete access if this is the last team user belongs to.
 		if auth == 0 {
-			if _, err = sess.Delete(access); err != nil {
-				sess.Rollback()
+			if _, err = e.Delete(access); err != nil {
 				return fmt.Errorf("fail to delete access: %v", err)
-			} else if err = WatchRepo(u.Id, repo.Id, false); err != nil {
-				sess.Rollback()
+			} else if err = watchRepo(e, u.Id, repo.Id, false); err != nil {
 				return err
 			}
 		} else if auth < t.Authorize {
 			// Downgrade authorize level.
-			if err = addAccessWithAuthorize(sess, access, AuthorizeToAccessType(auth)); err != nil {
-				sess.Rollback()
+			if err = addAccessWithAuthorize(e, access, AuthorizeToAccessType(auth)); err != nil {
 				return err
 			}
 		}
@@ -1073,17 +1082,15 @@ func removeTeamMemberWithSess(orgId, teamId, uid int64, sess *xorm.Session) erro
 
 	// This must exist.
 	ou := new(OrgUser)
-	_, err = sess.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou)
+	_, err = e.Where("uid=?", uid).And("org_id=?", org.Id).Get(ou)
 	if err != nil {
-		sess.Rollback()
 		return err
 	}
 	ou.NumTeams--
 	if t.IsOwnerTeam() {
 		ou.IsOwner = false
 	}
-	if _, err = sess.Id(ou.Id).AllCols().Update(ou); err != nil {
-		sess.Rollback()
+	if _, err = e.Id(ou.Id).AllCols().Update(ou); err != nil {
 		return err
 	}
 	return nil
@@ -1096,7 +1103,8 @@ func RemoveTeamMember(orgId, teamId, uid int64) error {
 	if err := sess.Begin(); err != nil {
 		return err
 	}
-	if err := removeTeamMemberWithSess(orgId, teamId, uid, sess); err != nil {
+	if err := removeTeamMember(sess, orgId, teamId, uid); err != nil {
+		sess.Rollback()
 		return err
 	}
 	return sess.Commit()

+ 12 - 10
models/repo.go

@@ -357,13 +357,16 @@ func MigrateRepository(u *User, name, desc string, private, mirror bool, url str
 		os.RemoveAll(repoPath)
 	}
 
-	// this command could for both migrate and mirror
+	// FIXME: this command could for both migrate and mirror
 	_, stderr, err := process.ExecTimeout(10*time.Minute,
 		fmt.Sprintf("MigrateRepository: %s", repoPath),
 		"git", "clone", "--mirror", "--bare", url, repoPath)
 	if err != nil {
-		return repo, errors.New("git clone: " + stderr)
+		return repo, fmt.Errorf("git clone --mirror --bare: %v", stderr)
+	} else if err = createUpdateHook(repoPath); err != nil {
+		return repo, fmt.Errorf("create update hook: %v", err)
 	}
+
 	return repo, UpdateRepository(repo)
 }
 
@@ -402,7 +405,7 @@ func initRepoCommit(tmpPath string, sig *git.Signature) (err error) {
 	return nil
 }
 
-func createHookUpdate(repoPath string) error {
+func createUpdateHook(repoPath string) error {
 	return ioutil.WriteFile(path.Join(repoPath, "hooks/update"),
 		[]byte(fmt.Sprintf(_TPL_UPDATE_HOOK, setting.ScriptType, "\""+appPath+"\"", setting.CustomConf)), 0777)
 }
@@ -416,8 +419,7 @@ func initRepository(f string, u *User, repo *Repository, initReadme bool, repoLa
 		return err
 	}
 
-	// hook/post-update
-	if err := createHookUpdate(repoPath); err != nil {
+	if err := createUpdateHook(repoPath); err != nil {
 		return err
 	}
 
@@ -1178,7 +1180,7 @@ func RewriteRepositoryUpdateHook() error {
 			if err := repo.GetOwner(); err != nil {
 				return err
 			}
-			return createHookUpdate(RepoPath(repo.Owner.Name, repo.Name))
+			return createUpdateHook(RepoPath(repo.Owner.Name, repo.Name))
 		})
 }
 
@@ -1297,7 +1299,7 @@ func IsWatching(uid, repoId int64) bool {
 	return has
 }
 
-func watchRepoWithEngine(e Engine, uid, repoId int64, watch bool) (err error) {
+func watchRepo(e Engine, uid, repoId int64, watch bool) (err error) {
 	if watch {
 		if IsWatching(uid, repoId) {
 			return nil
@@ -1320,7 +1322,7 @@ func watchRepoWithEngine(e Engine, uid, repoId int64, watch bool) (err error) {
 
 // Watch or unwatch repository.
 func WatchRepo(uid, repoId int64, watch bool) (err error) {
-	return watchRepoWithEngine(x, uid, repoId, watch)
+	return watchRepo(x, uid, repoId, watch)
 }
 
 // GetWatchers returns all watchers of given repository.
@@ -1508,14 +1510,14 @@ func ForkRepository(u *User, oldRepo *Repository, name, desc string) (*Repositor
 				log.Error(4, "GetMembers: %v", err)
 			} else {
 				for _, u := range t.Members {
-					if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil {
+					if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
 						log.Error(4, "WatchRepo2: %v", err)
 					}
 				}
 			}
 		}
 	} else {
-		if err = watchRepoWithEngine(sess, u.Id, repo.Id, true); err != nil {
+		if err = watchRepo(sess, u.Id, repo.Id, true); err != nil {
 			log.Error(4, "WatchRepo3: %v", err)
 		}
 	}

+ 8 - 6
models/webhook.go

@@ -5,6 +5,7 @@
 package models
 
 import (
+	"crypto/tls"
 	"encoding/json"
 	"errors"
 	"io/ioutil"
@@ -307,13 +308,14 @@ func DeliverHooks() {
 	defer func() { isShooting = false }()
 
 	tasks := make([]*HookTask, 0, 10)
-	timeout := time.Duration(setting.WebhookDeliverTimeout) * time.Second
+	timeout := time.Duration(setting.Webhook.DeliverTimeout) * time.Second
 	x.Where("is_delivered=?", false).Iterate(new(HookTask),
 		func(idx int, bean interface{}) error {
 			t := bean.(*HookTask)
 			req := httplib.Post(t.Url).SetTimeout(timeout, timeout).
 				Header("X-Gogs-Delivery", t.Uuid).
-				Header("X-Gogs-Event", string(t.EventType))
+				Header("X-Gogs-Event", string(t.EventType)).
+				SetTLSClientConfig(&tls.Config{InsecureSkipVerify: setting.Webhook.SkipTLSVerify})
 
 			switch t.ContentType {
 			case JSON:
@@ -329,7 +331,7 @@ func DeliverHooks() {
 			case GOGS:
 				{
 					if _, err := req.Response(); err != nil {
-						log.Error(4, "Delivery: %v", err)
+						log.Error(5, "Delivery: %v", err)
 					} else {
 						t.IsSucceed = true
 					}
@@ -337,15 +339,15 @@ func DeliverHooks() {
 			case SLACK:
 				{
 					if res, err := req.Response(); err != nil {
-						log.Error(4, "Delivery: %v", err)
+						log.Error(5, "Delivery: %v", err)
 					} else {
 						defer res.Body.Close()
 						contents, err := ioutil.ReadAll(res.Body)
 						if err != nil {
-							log.Error(4, "%s", err)
+							log.Error(5, "%s", err)
 						} else {
 							if string(contents) != "ok" {
-								log.Error(4, "slack failed with: %s", string(contents))
+								log.Error(5, "slack failed with: %s", string(contents))
 							} else {
 								t.IsSucceed = true
 							}

+ 3 - 2
modules/base/markdown.go

@@ -106,7 +106,7 @@ func (options *CustomRender) Image(out *bytes.Buffer, link []byte, title []byte,
 }
 
 var (
-	MentionPattern     = regexp.MustCompile(`((^|\s)@)[0-9a-zA-Z_]{1,}`)
+	MentionPattern     = regexp.MustCompile(`(\s|^)@[0-9a-zA-Z_]+`)
 	commitPattern      = regexp.MustCompile(`(\s|^)https?.*commit/[0-9a-zA-Z]+(#+[0-9a-zA-Z-]*)?`)
 	issueFullPattern   = regexp.MustCompile(`(\s|^)https?.*issues/[0-9]+(#+[0-9a-zA-Z-]*)?`)
 	issueIndexPattern  = regexp.MustCompile(`( |^)#[0-9]+`)
@@ -128,8 +128,9 @@ func RenderSpecialLink(rawBytes []byte, urlPrefix string) []byte {
 		if !inCodeBlock && !bytes.HasPrefix(line, tab) {
 			ms := MentionPattern.FindAll(line, -1)
 			for _, m := range ms {
+				m = bytes.TrimSpace(m)
 				line = bytes.Replace(line, m,
-					[]byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, setting.AppSubUrl, m[2:], m)), -1)
+					[]byte(fmt.Sprintf(`<a href="%s/%s">%s</a>`, setting.AppSubUrl, m[1:], m)), -1)
 			}
 		}
 

+ 1 - 1
modules/cron/manager.go

@@ -15,7 +15,7 @@ var c = New()
 
 func NewCronContext() {
 	c.AddFunc("Update mirrors", "@every 1h", models.MirrorUpdate)
-	c.AddFunc("Deliver hooks", fmt.Sprintf("@every %dm", setting.WebhookTaskInterval), models.DeliverHooks)
+	c.AddFunc("Deliver hooks", fmt.Sprintf("@every %dm", setting.Webhook.TaskInterval), models.DeliverHooks)
 	if setting.Git.Fsck.Enable {
 		c.AddFunc("Repository health check", fmt.Sprintf("@every %dh", setting.Git.Fsck.Interval), models.GitFsck)
 	}

+ 0 - 5
modules/middleware/auth.go

@@ -6,7 +6,6 @@ package middleware
 
 import (
 	"net/url"
-	"strings"
 
 	"github.com/Unknwon/macaron"
 	"github.com/macaron-contrib/csrf"
@@ -50,10 +49,6 @@ func Toggle(options *ToggleOptions) macaron.Handler {
 
 		if options.SignInRequire {
 			if !ctx.IsSigned {
-				// Ignore watch repository operation.
-				if strings.HasSuffix(ctx.Req.RequestURI, "watch") {
-					return
-				}
 				ctx.SetCookie("redirect_to", url.QueryEscape(setting.AppSubUrl+ctx.Req.RequestURI), 0, setting.AppSubUrl)
 				ctx.Redirect(setting.AppSubUrl + "/user/login")
 				return

+ 9 - 4
modules/setting/setting.go

@@ -68,8 +68,11 @@ var (
 	ReverseProxyAuthUser string
 
 	// Webhook settings.
-	WebhookTaskInterval   int
-	WebhookDeliverTimeout int
+	Webhook struct {
+		TaskInterval   int
+		DeliverTimeout int
+		SkipTLSVerify  bool
+	}
 
 	// Repository settings.
 	RepoRootPath string
@@ -508,8 +511,10 @@ func newNotifyMailService() {
 }
 
 func newWebhookService() {
-	WebhookTaskInterval = Cfg.Section("webhook").Key("TASK_INTERVAL").MustInt(1)
-	WebhookDeliverTimeout = Cfg.Section("webhook").Key("DELIVER_TIMEOUT").MustInt(5)
+	sec := Cfg.Section("webhook")
+	Webhook.TaskInterval = sec.Key("TASK_INTERVAL").MustInt(1)
+	Webhook.DeliverTimeout = sec.Key("DELIVER_TIMEOUT").MustInt(5)
+	Webhook.SkipTLSVerify = sec.Key("SKIP_TLS_VERIFY").MustBool()
 }
 
 func NewServices() {

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

@@ -1066,6 +1066,9 @@ The register and sign-in page style
 #repo-header-download-drop .btn > i {
   margin-right: 6px;
 }
+#repo-header-download-drop input {
+  cursor: default;
+}
 #repo-header-download-drop button,
 #repo-header-download-drop input {
   font-size: 11px;

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

@@ -81,6 +81,9 @@
     .btn>i {
         margin-right: 6px;
     }
+    input {
+        cursor: default;
+    }
     button, input {
         font-size: 11px;
     }

+ 1 - 4
routers/admin/admin.go

@@ -188,11 +188,8 @@ func Config(ctx *middleware.Context) {
 	ctx.Data["ReverseProxyAuthUser"] = setting.ReverseProxyAuthUser
 
 	ctx.Data["Service"] = setting.Service
-
 	ctx.Data["DbCfg"] = models.DbCfg
-
-	ctx.Data["WebhookTaskInterval"] = setting.WebhookTaskInterval
-	ctx.Data["WebhookDeliverTimeout"] = setting.WebhookDeliverTimeout
+	ctx.Data["Webhook"] = setting.Webhook
 
 	ctx.Data["MailerEnabled"] = false
 	if setting.MailService != nil {

+ 2 - 5
routers/org/teams.go

@@ -138,11 +138,8 @@ func TeamsRepoAction(ctx *middleware.Context) {
 	}
 
 	if err != nil {
-		log.Error(3, "Action(%s): %v", ctx.Params(":action"), err)
-		ctx.JSON(200, map[string]interface{}{
-			"ok":  false,
-			"err": err.Error(),
-		})
+		log.Error(3, "Action(%s): '%s' %v", ctx.Params(":action"), ctx.Org.Team.Name, err)
+		ctx.Handle(500, "TeamsRepoAction", err)
 		return
 	}
 	ctx.Redirect(ctx.Org.OrgLink + "/teams/" + ctx.Org.Team.LowerName + "/repositories")

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.5.13.0208 Beta
+0.5.13.0211 Beta

+ 4 - 2
templates/admin/config.tmpl

@@ -102,9 +102,11 @@
                             <div class="panel-body">
                                 <dl class="dl-horizontal admin-dl-horizontal">
                                     <dt>{{.i18n.Tr "admin.config.task_interval"}}</dt>
-                                    <dd>{{.WebhookTaskInterval}} {{.i18n.Tr "tool.raw_minutes"}}</dd>
+                                    <dd>{{.Webhook.TaskInterval}} {{.i18n.Tr "tool.raw_minutes"}}</dd>
                                     <dt>{{.i18n.Tr "admin.config.deliver_timeout"}}</dt>
-                                    <dd>{{.WebhookDeliverTimeout}} {{.i18n.Tr "tool.raw_seconds"}}</dd>
+                                    <dd>{{.Webhook.DeliverTimeout}} {{.i18n.Tr "tool.raw_seconds"}}</dd>
+                                    <dt>{{.i18n.Tr "admin.config.skip_tls_verify"}}</dt>
+                                    <dd><i class="fa fa{{if .Webhook.SkipTLSVerify}}-check{{end}}-square-o"></i></dd>
                                 </dl>
                             </div>
                         </div>

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

@@ -1,7 +1,7 @@
 		</div>
 		<footer id="footer">
 		    <div class="container clear">
-		        <p class="left" id="footer-rights">© 2014 GoGits · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> ·
+		        <p class="left" id="footer-rights">© 2015 GoGits · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> ·
 		            {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong></p>
 
 		        <div class="right" id="footer-links">

+ 2 - 2
templates/repo/header.tmpl

@@ -22,7 +22,7 @@
                         <button class="btn btn-blue left btn-left-radius" id="repo-clone-ssh" data-link="{{$.CloneLink.SSH}}">SSH</button>
                         {{end}}
                         <button class="btn {{if $.DisableSSH}}btn-blue{{else}}btn-gray{{end}} left" id="repo-clone-https" data-link="{{$.CloneLink.HTTPS}}">HTTPS</button>
-                        <input id="repo-clone-url" class="ipt ipt-disabled left" value="{{if $.DisableSSH}}{{$.CloneLink.HTTPS}}{{else}}{{$.CloneLink.SSH}}{{end}}" readonly />
+                        <input id="repo-clone-url" class="ipt ipt-disabled left" value="{{if $.DisableSSH}}{{$.CloneLink.HTTPS}}{{else}}{{$.CloneLink.SSH}}{{end}}" onclick="this.select();" readonly />
                         <button id="repo-clone-copy" class="btn btn-black left btn-right-radius" data-copy-val="val" data-copy-from="#repo-clone-url" original-title="{{$.i18n.Tr "repo.click_to_copy"}}" data-original-title="{{$.i18n.Tr "repo.click_to_copy"}}" data-after-title="{{$.i18n.Tr "repo.copied"}}">{{$.i18n.Tr "repo.copy_link"}}</button>
                         <p class="text-center" id="repo-clone-help">{{$.i18n.Tr "repo.clone_helper" "http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository" | Str2html}}</p>
                         <hr/>
@@ -60,4 +60,4 @@
         </ul>
     </div>
 </div>
-{{end}}
+{{end}}