mirror of
				https://github.com/go-gitea/gitea.git
				synced 2025-10-31 21:28:11 +09:00 
			
		
		
		
	merge
This commit is contained in:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -5,6 +5,7 @@ gogs | |||||||
| *.db | *.db | ||||||
| *.log | *.log | ||||||
| custom/ | custom/ | ||||||
|  | data/ | ||||||
| .vendor/ | .vendor/ | ||||||
| .idea/ | .idea/ | ||||||
| *.iml | *.iml | ||||||
| @@ -4,7 +4,6 @@ path=github.com/gogits/gogs | |||||||
| [deps] | [deps] | ||||||
| github.com/codegangsta/cli= | github.com/codegangsta/cli= | ||||||
| github.com/codegangsta/martini= | github.com/codegangsta/martini= | ||||||
| github.com/martini-contrib/sessions= |  | ||||||
| github.com/Unknwon/com= | github.com/Unknwon/com= | ||||||
| github.com/Unknwon/cae= | github.com/Unknwon/cae= | ||||||
| github.com/Unknwon/goconfig= | github.com/Unknwon/goconfig= | ||||||
|   | |||||||
							
								
								
									
										19
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										19
									
								
								README.md
									
									
									
									
									
								
							| @@ -1,15 +1,19 @@ | |||||||
| Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://gowalker.org/github.com/gogits/gogs) | Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | ||||||
| ===================== | ===================== | ||||||
|  |  | ||||||
| Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language. | Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | ||||||
|  |  | ||||||
| Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms**  that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency. |  | ||||||
|  |  | ||||||
| ##### Current version: 0.1.5 Alpha | ##### Current version: 0.1.6 Alpha | ||||||
|  |  | ||||||
|  | [简体中文](README_ZH.md) | ||||||
|  |  | ||||||
| ## Purpose | ## Purpose | ||||||
|  |  | ||||||
| There are some very good products in this category such as [gitlab](http://gitlab.com), but the environment setup steps often make us crazy. So our goal of Gogs is to build a GitHub-like clone with very easy setup steps, which take advantages of the Go Programming Language. | Since we choose to use pure Go implementation of Git manipulation, Gogs certainly supports **ALL platforms**  that Go supports, including Linux, Mac OS X, and Windows with **ZERO** dependency.  | ||||||
|  |  | ||||||
|  | More importantly, Gogs only needs one binary to setup your own project hosting on the fly! | ||||||
|  |  | ||||||
| ## Overview | ## Overview | ||||||
|  |  | ||||||
| @@ -23,7 +27,7 @@ There are some very good products in this category such as [gitlab](http://gitla | |||||||
| - Activity timeline | - Activity timeline | ||||||
| - SSH protocol support. | - SSH protocol support. | ||||||
| - Register/delete account. | - Register/delete account. | ||||||
| - Create/delete public repository. | - Create/delete/watch public repository. | ||||||
| - User profile page. | - User profile page. | ||||||
| - Repository viewer. | - Repository viewer. | ||||||
| - Gravatar support. | - Gravatar support. | ||||||
| @@ -42,8 +46,9 @@ There are two ways to install Gogs: | |||||||
|  |  | ||||||
| ## Acknowledgments | ## Acknowledgments | ||||||
|  |  | ||||||
| - Mail service is based on [WeTalk](https://github.com/beego/wetalk). |  | ||||||
| - Logo is inspired by [martini](https://github.com/martini-contrib). | - Logo is inspired by [martini](https://github.com/martini-contrib). | ||||||
|  | - Mail Service, modules design is inspired by [WeTalk](https://github.com/beego/wetalk). | ||||||
|  | - System Monitor Status is inspired by [GoBlog](https://github.com/fuxiaohei/goblog). | ||||||
|  |  | ||||||
| ## Contributors | ## Contributors | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										53
									
								
								README_ZH.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								README_ZH.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | |||||||
|  | Gogs - Go Git Service [](https://app.wercker.com/project/bykey/ad0bdb0bc450ac6f09bc56b9640a50aa) [](https://drone.io/github.com/gogits/gogs/latest) | ||||||
|  | ===================== | ||||||
|  |  | ||||||
|  | Gogs(Go Git Service) 是一个由 Go 语言编写的自助 Git 托管服务。 | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | ##### 当前版本:0.1.6 Alpha | ||||||
|  |  | ||||||
|  | ## 开发目的 | ||||||
|  |  | ||||||
|  | Gogs 完全使用 Go 语言来实现对 Git 数据的操作,实现 **零** 依赖,并且支持 Go 语言所支持的 **所有平台**,包括 Linux、Mac OS X 以及 Windows。 | ||||||
|  |  | ||||||
|  | 更重要的是,您只需要一个可执行文件就能借助 Gogs 快速搭建属于您自己的代码托管服务! | ||||||
|  |  | ||||||
|  | ## 项目概览 | ||||||
|  |  | ||||||
|  | - 有关项目设计、开发说明、变更日志和路线图,请通过  [Wiki](https://github.com/gogits/gogs/wiki) 查看。 | ||||||
|  | - 您可以到 [Trello Broad](https://trello.com/b/uxAoeLUl/gogs-go-git-service) 跟随开发团队的脚步。 | ||||||
|  | - 想要先睹为快?通过 [在线体验](http://try.gogits.org/Unknown/gogs) 或查看 **安装部署 -> 二进制安装** 小节。 | ||||||
|  | - 使用过程中遇到问题?尝试从 [故障排查](https://github.com/gogits/gogs/wiki/Troubleshooting) 页面获取帮助。 | ||||||
|  |  | ||||||
|  | ## 功能特性 | ||||||
|  |  | ||||||
|  | - 活动时间线 | ||||||
|  | - SSH 协议支持 | ||||||
|  | - 注册/删除用户 | ||||||
|  | - 创建/删除/关注公开仓库 | ||||||
|  | - 用户个人信息页面 | ||||||
|  | - 仓库浏览器 | ||||||
|  | - Gravatar 支持 | ||||||
|  | - 邮件服务(注册) | ||||||
|  | - 管理员面板 | ||||||
|  | - 支持 MySQL、PostgreSQL 以及 SQLite3(仅限二进制版本) | ||||||
|  |  | ||||||
|  | ## 安装部署 | ||||||
|  |  | ||||||
|  | 在安装 Gogs 之前,您需要先安装 [基本环境](https://github.com/gogits/gogs/wiki/Prerequirements)。 | ||||||
|  |  | ||||||
|  | 然后,您可以通过以下两种方式来安装 Gogs: | ||||||
|  |  | ||||||
|  | - [二进制安装](https://github.com/gogits/gogs/wiki/Install-from-binary): **强烈推荐** 适合体验者和实际部署 | ||||||
|  | - [源码安装](https://github.com/gogits/gogs/wiki/Install-from-source) | ||||||
|  |  | ||||||
|  | ## 特别鸣谢 | ||||||
|  |  | ||||||
|  | - Logo 基于 [martini](https://github.com/martini-contrib) 修改而来。 | ||||||
|  | - 邮件服务、模块设计基于 [WeTalk](https://github.com/beego/wetalk) 修改而来。 | ||||||
|  | - 系统监视状态基于 [GoBlog](https://github.com/fuxiaohei/goblog) 修改而来。 | ||||||
|  |  | ||||||
|  | ## 贡献成员 | ||||||
|  |  | ||||||
|  | 本项目最初由 [Unknown](https://github.com/Unknwon) 和 [lunny](https://github.com/lunny) 发起,随后 [fuxiaohei](https://github.com/fuxiaohei) 与 [slene](https://github.com/slene) 加入到开发团队。您可以通过查看 [贡献者页面](https://github.com/gogits/gogs/graphs/contributors) 获取完整的贡献者列表。 | ||||||
							
								
								
									
										44
									
								
								conf/app.ini
									
									
									
									
									
								
							
							
						
						
									
										44
									
								
								conf/app.ini
									
									
									
									
									
								
							| @@ -34,6 +34,10 @@ PATH = data/gogs.db | |||||||
| [security] | [security] | ||||||
| ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | ; !!CHANGE THIS TO KEEP YOUR USER DATA SAFE!! | ||||||
| SECRET_KEY = !#@FDEWREWR&*( | SECRET_KEY = !#@FDEWREWR&*( | ||||||
|  | ; Auto-login remember days | ||||||
|  | LOGIN_REMEMBER_DAYS = 7 | ||||||
|  | COOKIE_USERNAME = gogs_awesome | ||||||
|  | COOKIE_REMEMBER_NAME = gogs_incredible | ||||||
|  |  | ||||||
| [service] | [service] | ||||||
| ACTIVE_CODE_LIVE_MINUTES = 180 | ACTIVE_CODE_LIVE_MINUTES = 180 | ||||||
| @@ -44,6 +48,8 @@ REGISTER_EMAIL_CONFIRM = false | |||||||
| DISENABLE_REGISTERATION = false | DISENABLE_REGISTERATION = false | ||||||
| ; User must sign in to view anything. | ; User must sign in to view anything. | ||||||
| REQUIRE_SIGNIN_VIEW = false | REQUIRE_SIGNIN_VIEW = false | ||||||
|  | ; Cache avatar as picture | ||||||
|  | ENABLE_CACHE_AVATAR = false | ||||||
|  |  | ||||||
| [mailer] | [mailer] | ||||||
| ENABLED = false | ENABLED = false | ||||||
| @@ -70,8 +76,38 @@ INTERVAL = 60 | |||||||
| ; memcache: "127.0.0.1:11211" | ; memcache: "127.0.0.1:11211" | ||||||
| HOST = | HOST = | ||||||
|  |  | ||||||
|  | [session] | ||||||
|  | ; Either "memory", "file", "redis" or "mysql", default is "memory" | ||||||
|  | PROVIDER = file | ||||||
|  | ; Provider config options | ||||||
|  | ; memory: not have any config yet | ||||||
|  | ; file: session file path, e.g. data/sessions | ||||||
|  | ; redis: config like redis server addr, poolSize, password, e.g. 127.0.0.1:6379,100,astaxie | ||||||
|  | ; mysql: go-sql-driver/mysql dsn config string, e.g. root:password@/session_table | ||||||
|  | PROVIDER_CONFIG = data/sessions | ||||||
|  | ; Session cookie name | ||||||
|  | COOKIE_NAME = i_like_gogits | ||||||
|  | ; If you use session in https only, default is false | ||||||
|  | COOKIE_SECURE = false | ||||||
|  | ; Enable set cookie, default is true | ||||||
|  | ENABLE_SET_COOKIE = true | ||||||
|  | ; Session GC time interval, default is 86400 | ||||||
|  | GC_INTERVAL_TIME = 86400 | ||||||
|  | ; Session life time, default is 86400 | ||||||
|  | SESSION_LIFE_TIME = 86400 | ||||||
|  | ; session id hash func, Either "sha1", "sha256" or "md5" default is sha1 | ||||||
|  | SESSION_ID_HASHFUNC = sha1 | ||||||
|  | ; Session hash key, default is use random string | ||||||
|  | SESSION_ID_HASHKEY = | ||||||
|  |  | ||||||
|  | [picture] | ||||||
|  | ; The place to picture data, either "server" or "qiniu", default is "server" | ||||||
|  | SERVICE = server | ||||||
|  | ; For "server" only, root path of picture data, default is "data/pictures" | ||||||
|  | PATH = data/pictures | ||||||
|  |  | ||||||
| [log] | [log] | ||||||
| ; Either "console", "file", "conn" or "smtp", default is "console" | ; Either "console", "file", "conn", "smtp" or "database", default is "console" | ||||||
| MODE = console | MODE = console | ||||||
| ; Buffer length of channel, keep it as it is if you don't know what it is. | ; Buffer length of channel, keep it as it is if you don't know what it is. | ||||||
| BUFFER_LEN = 10000 | BUFFER_LEN = 10000 | ||||||
| @@ -121,3 +157,9 @@ USER = | |||||||
| PASSWD = | PASSWD = | ||||||
| ; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"] | ; Receivers, can be one or more, e.g. ["1@example.com","2@example.com"] | ||||||
| RECEIVERS =  | RECEIVERS =  | ||||||
|  |  | ||||||
|  | ; For "database" mode only | ||||||
|  | [log.database] | ||||||
|  | LEVEL =  | ||||||
|  | Driver =  | ||||||
|  | CONN =  | ||||||
							
								
								
									
										4
									
								
								gogs.go
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								gogs.go
									
									
									
									
									
								
							| @@ -2,7 +2,7 @@ | |||||||
| // Use of this source code is governed by a MIT-style | // Use of this source code is governed by a MIT-style | ||||||
| // license that can be found in the LICENSE file. | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
| // gogs(Go Git Service) is a Go clone of Github. | // Gogs(Go Git Service) is a Self Hosted Git Service in the Go Programming Language. | ||||||
| package main | package main | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| @@ -20,7 +20,7 @@ import ( | |||||||
| // Test that go1.2 tag above is included in builds. main.go refers to this definition. | // Test that go1.2 tag above is included in builds. main.go refers to this definition. | ||||||
| const go12tag = true | const go12tag = true | ||||||
|  |  | ||||||
| const APP_VER = "0.1.5.0321" | const APP_VER = "0.1.6.0323.1" | ||||||
|  |  | ||||||
| func init() { | func init() { | ||||||
| 	base.AppVer = APP_VER | 	base.AppVer = APP_VER | ||||||
|   | |||||||
| @@ -7,6 +7,8 @@ package models | |||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // Operation types of user action. | // Operation types of user action. | ||||||
| @@ -28,7 +30,7 @@ type Action struct { | |||||||
| 	ActUserName string // Action user name. | 	ActUserName string // Action user name. | ||||||
| 	RepoId      int64 | 	RepoId      int64 | ||||||
| 	RepoName    string | 	RepoName    string | ||||||
| 	Content     string    `xorm:"varchar(1000)"` | 	Content     string    `xorm:"TEXT"` | ||||||
| 	Created     time.Time `xorm:"created"` | 	Created     time.Time `xorm:"created"` | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -79,6 +81,18 @@ func CommitRepoAction(userId int64, userName string, | |||||||
| 		}) | 		}) | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Update repository last update time. | ||||||
|  | 	repo, err := GetRepositoryByName(userId, repoName) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	repo.IsBare = false | ||||||
|  | 	if err = UpdateRepository(repo); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Trace("action.CommitRepoAction: %d/%s", userId, repo.LowerName) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -92,6 +106,8 @@ func NewRepoAction(user *User, repo *Repository) error { | |||||||
| 		RepoId:      repo.Id, | 		RepoId:      repo.Id, | ||||||
| 		RepoName:    repo.Name, | 		RepoName:    repo.Name, | ||||||
| 	}) | 	}) | ||||||
|  |  | ||||||
|  | 	log.Trace("action.NewRepoAction: %s/%s", user.LowerName, repo.LowerName) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										151
									
								
								models/issue.go
									
									
									
									
									
								
							
							
						
						
									
										151
									
								
								models/issue.go
									
									
									
									
									
								
							| @@ -4,16 +4,155 @@ | |||||||
|  |  | ||||||
| package models | package models | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	ErrIssueNotExist = errors.New("Issue does not exist") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Issue represents an issue or pull request of repository. | ||||||
| type Issue struct { | type Issue struct { | ||||||
| 	Id       int64 | 	Id          int64 | ||||||
| 	RepoId   int64 `xorm:"index"` | 	Index       int64 // Index in one repository. | ||||||
| 	PosterId int64 | 	Name        string | ||||||
|  | 	RepoId      int64 `xorm:"index"` | ||||||
|  | 	PosterId    int64 | ||||||
|  | 	MilestoneId int64 | ||||||
|  | 	AssigneeId  int64 | ||||||
|  | 	IsPull      bool // Indicates whether is a pull request or not. | ||||||
|  | 	IsClosed    bool | ||||||
|  | 	Labels      string `xorm:"TEXT"` | ||||||
|  | 	Mentions    string `xorm:"TEXT"` | ||||||
|  | 	Content     string `xorm:"TEXT"` | ||||||
|  | 	NumComments int | ||||||
|  | 	Created     time.Time `xorm:"created"` | ||||||
|  | 	Updated     time.Time `xorm:"updated"` | ||||||
| } | } | ||||||
|  |  | ||||||
| type PullRequest struct { | // CreateIssue creates new issue for repository. | ||||||
| 	Id int64 | func CreateIssue(userId, repoId, milestoneId, assigneeId int64, name, labels, content string, isPull bool) (*Issue, error) { | ||||||
|  | 	count, err := GetIssueCount(repoId) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// TODO: find out mentions | ||||||
|  | 	mentions := "" | ||||||
|  |  | ||||||
|  | 	issue := &Issue{ | ||||||
|  | 		Index:       count + 1, | ||||||
|  | 		Name:        name, | ||||||
|  | 		RepoId:      repoId, | ||||||
|  | 		PosterId:    userId, | ||||||
|  | 		MilestoneId: milestoneId, | ||||||
|  | 		AssigneeId:  assigneeId, | ||||||
|  | 		IsPull:      isPull, | ||||||
|  | 		Labels:      labels, | ||||||
|  | 		Mentions:    mentions, | ||||||
|  | 		Content:     content, | ||||||
|  | 	} | ||||||
|  | 	_, err = orm.Insert(issue) | ||||||
|  | 	return issue, err | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // GetIssueCount returns count of issues in the repository. | ||||||
|  | func GetIssueCount(repoId int64) (int64, error) { | ||||||
|  | 	return orm.Count(&Issue{RepoId: repoId}) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetIssueById returns issue object by given id. | ||||||
|  | func GetIssueById(id int64) (*Issue, error) { | ||||||
|  | 	issue := new(Issue) | ||||||
|  | 	has, err := orm.Id(id).Get(issue) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} else if !has { | ||||||
|  | 		return nil, ErrIssueNotExist | ||||||
|  | 	} | ||||||
|  | 	return issue, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetIssues returns a list of issues by given conditions. | ||||||
|  | func GetIssues(userId, repoId, posterId, milestoneId int64, page int, isClosed, isMention bool, labels, sortType string) ([]Issue, error) { | ||||||
|  | 	sess := orm.Limit(20, (page-1)*20) | ||||||
|  |  | ||||||
|  | 	if repoId > 0 { | ||||||
|  | 		sess = sess.Where("repo_id=?", repoId).And("is_closed=?", isClosed) | ||||||
|  | 	} else { | ||||||
|  | 		sess = sess.Where("is_closed=?", isClosed) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if userId > 0 { | ||||||
|  | 		sess = sess.And("assignee_id=?", userId) | ||||||
|  | 	} else if posterId > 0 { | ||||||
|  | 		sess = sess.And("poster_id=?", posterId) | ||||||
|  | 	} else if isMention { | ||||||
|  | 		sess = sess.And("mentions like '%$" + base.ToStr(userId) + "|%'") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if milestoneId > 0 { | ||||||
|  | 		sess = sess.And("milestone_id=?", milestoneId) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(labels) > 0 { | ||||||
|  | 		for _, label := range strings.Split(labels, ",") { | ||||||
|  | 			sess = sess.And("mentions like '%$" + label + "|%'") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	switch sortType { | ||||||
|  | 	case "oldest": | ||||||
|  | 		sess = sess.Asc("created") | ||||||
|  | 	case "recentupdate": | ||||||
|  | 		sess = sess.Desc("updated") | ||||||
|  | 	case "leastupdate": | ||||||
|  | 		sess = sess.Asc("updated") | ||||||
|  | 	case "mostcomment": | ||||||
|  | 		sess = sess.Desc("num_comments") | ||||||
|  | 	case "leastcomment": | ||||||
|  | 		sess = sess.Asc("num_comments") | ||||||
|  | 	default: | ||||||
|  | 		sess = sess.Desc("created") | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var issues []Issue | ||||||
|  | 	err := sess.Find(&issues) | ||||||
|  | 	return issues, err | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Label represents a list of labels of repository for issues. | ||||||
|  | type Label struct { | ||||||
|  | 	Id     int64 | ||||||
|  | 	RepoId int64 `xorm:"index"` | ||||||
|  | 	Names  string | ||||||
|  | 	Colors string | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Milestone represents a milestone of repository. | ||||||
|  | type Milestone struct { | ||||||
|  | 	Id        int64 | ||||||
|  | 	Name      string | ||||||
|  | 	RepoId    int64 `xorm:"index"` | ||||||
|  | 	IsClosed  bool | ||||||
|  | 	Content   string | ||||||
|  | 	NumIssues int | ||||||
|  | 	DueDate   time.Time | ||||||
|  | 	Created   time.Time `xorm:"created"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Comment represents a comment in commit and issue page. | ||||||
| type Comment struct { | type Comment struct { | ||||||
| 	Id int64 | 	Id       int64 | ||||||
|  | 	PosterId int64 | ||||||
|  | 	IssueId  int64 | ||||||
|  | 	CommitId int64 | ||||||
|  | 	Line     int | ||||||
|  | 	Content  string | ||||||
|  | 	Created  time.Time `xorm:"created"` | ||||||
| } | } | ||||||
|   | |||||||
| @@ -72,7 +72,7 @@ func setEngine() { | |||||||
| func NewEngine() { | func NewEngine() { | ||||||
| 	setEngine() | 	setEngine() | ||||||
| 	if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), | 	if err := orm.Sync(new(User), new(PublicKey), new(Repository), new(Watch), | ||||||
| 		new(Action), new(Access)); err != nil { | 		new(Action), new(Access), new(Issue)); err != nil { | ||||||
| 		fmt.Printf("sync database struct error: %v\n", err) | 		fmt.Printf("sync database struct error: %v\n", err) | ||||||
| 		os.Exit(2) | 		os.Exit(2) | ||||||
| 	} | 	} | ||||||
|   | |||||||
| @@ -19,6 +19,8 @@ import ( | |||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/Unknwon/com" | 	"github.com/Unknwon/com" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| const ( | const ( | ||||||
| @@ -78,7 +80,7 @@ type PublicKey struct { | |||||||
| 	OwnerId     int64  `xorm:"index"` | 	OwnerId     int64  `xorm:"index"` | ||||||
| 	Name        string `xorm:"unique not null"` | 	Name        string `xorm:"unique not null"` | ||||||
| 	Fingerprint string | 	Fingerprint string | ||||||
| 	Content     string    `xorm:"text not null"` | 	Content     string    `xorm:"TEXT not null"` | ||||||
| 	Created     time.Time `xorm:"created"` | 	Created     time.Time `xorm:"created"` | ||||||
| 	Updated     time.Time `xorm:"updated"` | 	Updated     time.Time `xorm:"updated"` | ||||||
| } | } | ||||||
| @@ -99,8 +101,8 @@ func AddPublicKey(key *PublicKey) (err error) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// Calculate fingerprint. | 	// Calculate fingerprint. | ||||||
| 	tmpPath := filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), | 	tmpPath := strings.Replace(filepath.Join(os.TempDir(), fmt.Sprintf("%d", time.Now().Nanosecond()), | ||||||
| 		"id_rsa.pub") | 		"id_rsa.pub"), "\\", "/", -1) | ||||||
| 	os.MkdirAll(path.Dir(tmpPath), os.ModePerm) | 	os.MkdirAll(path.Dir(tmpPath), os.ModePerm) | ||||||
| 	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil { | 	if err = ioutil.WriteFile(tmpPath, []byte(key.Content), os.ModePerm); err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -127,25 +129,11 @@ func AddPublicKey(key *PublicKey) (err error) { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
| // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | func rewriteAuthorizedKeys(key *PublicKey, p, tmpP string) error { | ||||||
| func DeletePublicKey(key *PublicKey) (err error) { |  | ||||||
| 	// Delete SSH key in database. |  | ||||||
| 	has, err := orm.Id(key.Id).Get(key) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} else if !has { |  | ||||||
| 		return errors.New("Public key does not exist") |  | ||||||
| 	} |  | ||||||
| 	if _, err = orm.Delete(key); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Delete SSH key in SSH key file. | 	// Delete SSH key in SSH key file. | ||||||
| 	sshOpLocker.Lock() | 	sshOpLocker.Lock() | ||||||
| 	defer sshOpLocker.Unlock() | 	defer sshOpLocker.Unlock() | ||||||
|  |  | ||||||
| 	p := filepath.Join(sshPath, "authorized_keys") |  | ||||||
| 	tmpP := filepath.Join(sshPath, "authorized_keys.tmp") |  | ||||||
| 	fr, err := os.Open(p) | 	fr, err := os.Open(p) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| @@ -188,8 +176,29 @@ func DeletePublicKey(key *PublicKey) (err error) { | |||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
| 	if err = os.Remove(p); err != nil { | // DeletePublicKey deletes SSH key information both in database and authorized_keys file. | ||||||
|  | func DeletePublicKey(key *PublicKey) (err error) { | ||||||
|  | 	// Delete SSH key in database. | ||||||
|  | 	has, err := orm.Id(key.Id).Get(key) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if !has { | ||||||
|  | 		return errors.New("Public key does not exist") | ||||||
|  | 	} | ||||||
|  | 	if _, err = orm.Delete(key); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	p := filepath.Join(sshPath, "authorized_keys") | ||||||
|  | 	tmpP := filepath.Join(sshPath, "authorized_keys.tmp") | ||||||
|  | 	log.Trace("ssh.DeletePublicKey(authorized_keys): %s", p) | ||||||
|  |  | ||||||
|  | 	if err = rewriteAuthorizedKeys(key, p, tmpP); err != nil { | ||||||
|  | 		return err | ||||||
|  | 	} else if err = os.Remove(p); err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 	return os.Rename(tmpP, p) | 	return os.Rename(tmpP, p) | ||||||
|   | |||||||
| @@ -83,10 +83,11 @@ type Repository struct { | |||||||
| 	Name        string `xorm:"index not null"` | 	Name        string `xorm:"index not null"` | ||||||
| 	Description string | 	Description string | ||||||
| 	Website     string | 	Website     string | ||||||
| 	Private     bool |  | ||||||
| 	NumWatches  int | 	NumWatches  int | ||||||
| 	NumStars    int | 	NumStars    int | ||||||
| 	NumForks    int | 	NumForks    int | ||||||
|  | 	IsPrivate   bool | ||||||
|  | 	IsBare      bool | ||||||
| 	Created     time.Time `xorm:"created"` | 	Created     time.Time `xorm:"created"` | ||||||
| 	Updated     time.Time `xorm:"updated"` | 	Updated     time.Time `xorm:"updated"` | ||||||
| } | } | ||||||
| @@ -139,7 +140,8 @@ func CreateRepository(user *User, repoName, desc, repoLang, license string, priv | |||||||
| 		Name:        repoName, | 		Name:        repoName, | ||||||
| 		LowerName:   strings.ToLower(repoName), | 		LowerName:   strings.ToLower(repoName), | ||||||
| 		Description: desc, | 		Description: desc, | ||||||
| 		Private:     private, | 		IsPrivate:   private, | ||||||
|  | 		IsBare:      repoLang == "" && license == "" && !initReadme, | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	repoPath := RepoPath(user.Name, repoName) | 	repoPath := RepoPath(user.Name, repoName) | ||||||
| @@ -369,6 +371,18 @@ func RepoPath(userName, repoName string) string { | |||||||
| 	return filepath.Join(UserPath(userName), repoName+".git") | 	return filepath.Join(UserPath(userName), repoName+".git") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func UpdateRepository(repo *Repository) error { | ||||||
|  | 	if len(repo.Description) > 255 { | ||||||
|  | 		repo.Description = repo.Description[:255] | ||||||
|  | 	} | ||||||
|  | 	if len(repo.Website) > 255 { | ||||||
|  | 		repo.Website = repo.Website[:255] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err := orm.Id(repo.Id).UseBool().Cols("description", "website").Update(repo) | ||||||
|  | 	return err | ||||||
|  | } | ||||||
|  |  | ||||||
| // DeleteRepository deletes a repository for a user or orgnaztion. | // DeleteRepository deletes a repository for a user or orgnaztion. | ||||||
| func DeleteRepository(userId, repoId int64, userName string) (err error) { | func DeleteRepository(userId, repoId int64, userName string) (err error) { | ||||||
| 	repo := &Repository{Id: repoId, OwnerId: userId} | 	repo := &Repository{Id: repoId, OwnerId: userId} | ||||||
| @@ -413,9 +427,9 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) { | |||||||
| } | } | ||||||
|  |  | ||||||
| // GetRepositoryByName returns the repository by given name under user if exists. | // GetRepositoryByName returns the repository by given name under user if exists. | ||||||
| func GetRepositoryByName(user *User, repoName string) (*Repository, error) { | func GetRepositoryByName(userId int64, repoName string) (*Repository, error) { | ||||||
| 	repo := &Repository{ | 	repo := &Repository{ | ||||||
| 		OwnerId:   user.Id, | 		OwnerId:   userId, | ||||||
| 		LowerName: strings.ToLower(repoName), | 		LowerName: strings.ToLower(repoName), | ||||||
| 	} | 	} | ||||||
| 	has, err := orm.Get(repo) | 	has, err := orm.Get(repo) | ||||||
|   | |||||||
| @@ -201,7 +201,14 @@ func VerifyUserActiveCode(code string) (user *User) { | |||||||
|  |  | ||||||
| // UpdateUser updates user's information. | // UpdateUser updates user's information. | ||||||
| func UpdateUser(user *User) (err error) { | func UpdateUser(user *User) (err error) { | ||||||
| 	_, err = orm.Id(user.Id).UseBool().Update(user) | 	if len(user.Location) > 255 { | ||||||
|  | 		user.Location = user.Location[:255] | ||||||
|  | 	} | ||||||
|  | 	if len(user.Website) > 255 { | ||||||
|  | 		user.Website = user.Website[:255] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	_, err = orm.Id(user.Id).UseBool().Cols("website", "location").Update(user) | ||||||
| 	return err | 	return err | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -279,9 +286,7 @@ func GetUserByName(name string) (*User, error) { | |||||||
| 	if len(name) == 0 { | 	if len(name) == 0 { | ||||||
| 		return nil, ErrUserNotExist | 		return nil, ErrUserNotExist | ||||||
| 	} | 	} | ||||||
| 	user := &User{ | 	user := &User{LowerName: strings.ToLower(name)} | ||||||
| 		LowerName: strings.ToLower(name), |  | ||||||
| 	} |  | ||||||
| 	has, err := orm.Get(user) | 	has, err := orm.Get(user) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
|   | |||||||
| @@ -61,6 +61,7 @@ func (f *RegisterForm) Validate(errors *binding.Errors, req *http.Request, conte | |||||||
| type LogInForm struct { | type LogInForm struct { | ||||||
| 	UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | 	UserName string `form:"username" binding:"Required;AlphaDash;MaxSize(30)"` | ||||||
| 	Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | 	Password string `form:"passwd" binding:"Required;MinSize(6);MaxSize(30)"` | ||||||
|  | 	Remember string `form:"remember"` | ||||||
| } | } | ||||||
|  |  | ||||||
| func (f *LogInForm) Name(field string) string { | func (f *LogInForm) Name(field string) string { | ||||||
|   | |||||||
							
								
								
									
										54
									
								
								modules/auth/issue.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										54
									
								
								modules/auth/issue.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,54 @@ | |||||||
|  | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package auth | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"reflect" | ||||||
|  |  | ||||||
|  | 	"github.com/codegangsta/martini" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/binding" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type CreateIssueForm struct { | ||||||
|  | 	IssueName   string `form:"name" binding:"Required;MaxSize(50)"` | ||||||
|  | 	RepoId      int64  `form:"repoid" binding:"Required"` | ||||||
|  | 	MilestoneId int64  `form:"milestoneid" binding:"Required"` | ||||||
|  | 	AssigneeId  int64  `form:"assigneeid"` | ||||||
|  | 	Labels      string `form:"labels"` | ||||||
|  | 	Content     string `form:"content"` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *CreateIssueForm) Name(field string) string { | ||||||
|  | 	names := map[string]string{ | ||||||
|  | 		"IssueName":   "Issue name", | ||||||
|  | 		"RepoId":      "Repository ID", | ||||||
|  | 		"MilestoneId": "Milestone ID", | ||||||
|  | 	} | ||||||
|  | 	return names[field] | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (f *CreateIssueForm) Validate(errors *binding.Errors, req *http.Request, context martini.Context) { | ||||||
|  | 	if req.Method == "GET" || errors.Count() == 0 { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	data := context.Get(reflect.TypeOf(base.TmplData{})).Interface().(base.TmplData) | ||||||
|  | 	data["HasError"] = true | ||||||
|  | 	AssignForm(f, data) | ||||||
|  |  | ||||||
|  | 	if len(errors.Overall) > 0 { | ||||||
|  | 		for _, err := range errors.Overall { | ||||||
|  | 			log.Error("CreateIssueForm.Validate: %v", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	validate(errors, data, f) | ||||||
|  | } | ||||||
| @@ -9,7 +9,8 @@ import ( | |||||||
| 	"reflect" | 	"reflect" | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
| 	"github.com/martini-contrib/sessions" |  | ||||||
|  | 	"github.com/gogits/session" | ||||||
|  |  | ||||||
| 	"github.com/gogits/binding" | 	"github.com/gogits/binding" | ||||||
|  |  | ||||||
| @@ -19,7 +20,7 @@ import ( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| // SignedInId returns the id of signed in user. | // SignedInId returns the id of signed in user. | ||||||
| func SignedInId(session sessions.Session) int64 { | func SignedInId(session session.SessionStore) int64 { | ||||||
| 	userId := session.Get("userId") | 	userId := session.Get("userId") | ||||||
| 	if userId == nil { | 	if userId == nil { | ||||||
| 		return 0 | 		return 0 | ||||||
| @@ -34,7 +35,7 @@ func SignedInId(session sessions.Session) int64 { | |||||||
| } | } | ||||||
|  |  | ||||||
| // SignedInName returns the name of signed in user. | // SignedInName returns the name of signed in user. | ||||||
| func SignedInName(session sessions.Session) string { | func SignedInName(session session.SessionStore) string { | ||||||
| 	userName := session.Get("userName") | 	userName := session.Get("userName") | ||||||
| 	if userName == nil { | 	if userName == nil { | ||||||
| 		return "" | 		return "" | ||||||
| @@ -46,7 +47,7 @@ func SignedInName(session sessions.Session) string { | |||||||
| } | } | ||||||
|  |  | ||||||
| // SignedInUser returns the user object of signed user. | // SignedInUser returns the user object of signed user. | ||||||
| func SignedInUser(session sessions.Session) *models.User { | func SignedInUser(session session.SessionStore) *models.User { | ||||||
| 	id := SignedInId(session) | 	id := SignedInId(session) | ||||||
| 	if id <= 0 { | 	if id <= 0 { | ||||||
| 		return nil | 		return nil | ||||||
| @@ -61,7 +62,7 @@ func SignedInUser(session sessions.Session) *models.User { | |||||||
| } | } | ||||||
|  |  | ||||||
| // IsSignedIn check if any user has signed in. | // IsSignedIn check if any user has signed in. | ||||||
| func IsSignedIn(session sessions.Session) bool { | func IsSignedIn(session session.SessionStore) bool { | ||||||
| 	return SignedInId(session) > 0 | 	return SignedInId(session) > 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import ( | |||||||
| 	"github.com/Unknwon/goconfig" | 	"github.com/Unknwon/goconfig" | ||||||
|  |  | ||||||
| 	"github.com/gogits/cache" | 	"github.com/gogits/cache" | ||||||
|  | 	"github.com/gogits/session" | ||||||
|  |  | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| ) | ) | ||||||
| @@ -37,21 +38,33 @@ var ( | |||||||
| 	RunUser      string | 	RunUser      string | ||||||
| 	RepoRootPath string | 	RepoRootPath string | ||||||
|  |  | ||||||
|  | 	LogInRememberDays  int | ||||||
|  | 	CookieUserName     string | ||||||
|  | 	CookieRememberName string | ||||||
|  |  | ||||||
| 	Cfg         *goconfig.ConfigFile | 	Cfg         *goconfig.ConfigFile | ||||||
| 	MailService *Mailer | 	MailService *Mailer | ||||||
|  |  | ||||||
|  | 	LogMode   string | ||||||
|  | 	LogConfig string | ||||||
|  |  | ||||||
| 	Cache        cache.Cache | 	Cache        cache.Cache | ||||||
| 	CacheAdapter string | 	CacheAdapter string | ||||||
| 	CacheConfig  string | 	CacheConfig  string | ||||||
|  |  | ||||||
| 	LogMode   string | 	SessionProvider string | ||||||
| 	LogConfig string | 	SessionConfig   *session.Config | ||||||
|  | 	SessionManager  *session.Manager | ||||||
|  |  | ||||||
|  | 	PictureService  string | ||||||
|  | 	PictureRootPath string | ||||||
| ) | ) | ||||||
|  |  | ||||||
| var Service struct { | var Service struct { | ||||||
| 	RegisterEmailConfirm   bool | 	RegisterEmailConfirm   bool | ||||||
| 	DisenableRegisteration bool | 	DisenableRegisteration bool | ||||||
| 	RequireSignInView      bool | 	RequireSignInView      bool | ||||||
|  | 	EnableCacheAvatar      bool | ||||||
| 	ActiveCodeLives        int | 	ActiveCodeLives        int | ||||||
| 	ResetPwdCodeLives      int | 	ResetPwdCodeLives      int | ||||||
| } | } | ||||||
| @@ -82,6 +95,7 @@ func newService() { | |||||||
| 	Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | 	Service.ResetPwdCodeLives = Cfg.MustInt("service", "RESET_PASSWD_CODE_LIVE_MINUTES", 180) | ||||||
| 	Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false) | 	Service.DisenableRegisteration = Cfg.MustBool("service", "DISENABLE_REGISTERATION", false) | ||||||
| 	Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW", false) | 	Service.RequireSignInView = Cfg.MustBool("service", "REQUIRE_SIGNIN_VIEW", false) | ||||||
|  | 	Service.EnableCacheAvatar = Cfg.MustBool("service", "ENABLE_CACHE_AVATAR", false) | ||||||
| } | } | ||||||
|  |  | ||||||
| func newLogService() { | func newLogService() { | ||||||
| @@ -129,6 +143,10 @@ func newLogService() { | |||||||
| 			Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"), | 			Cfg.MustValue(modeSec, "HOST", "127.0.0.1:25"), | ||||||
| 			Cfg.MustValue(modeSec, "RECEIVERS", "[]"), | 			Cfg.MustValue(modeSec, "RECEIVERS", "[]"), | ||||||
| 			Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve")) | 			Cfg.MustValue(modeSec, "SUBJECT", "Diagnostic message from serve")) | ||||||
|  | 	case "database": | ||||||
|  | 		LogConfig = fmt.Sprintf(`{"level":%s,"driver":%s,"conn":%s}`, level, | ||||||
|  | 			Cfg.MustValue(modeSec, "Driver"), | ||||||
|  | 			Cfg.MustValue(modeSec, "CONN")) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), LogMode, LogConfig) | 	log.NewLogger(Cfg.MustInt64("log", "BUFFER_LEN", 10000), LogMode, LogConfig) | ||||||
| @@ -159,6 +177,34 @@ func newCacheService() { | |||||||
| 	log.Info("Cache Service Enabled") | 	log.Info("Cache Service Enabled") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func newSessionService() { | ||||||
|  | 	SessionProvider = Cfg.MustValue("session", "PROVIDER", "memory") | ||||||
|  |  | ||||||
|  | 	SessionConfig = new(session.Config) | ||||||
|  | 	SessionConfig.ProviderConfig = Cfg.MustValue("session", "PROVIDER_CONFIG") | ||||||
|  | 	SessionConfig.CookieName = Cfg.MustValue("session", "COOKIE_NAME", "i_like_gogits") | ||||||
|  | 	SessionConfig.CookieSecure = Cfg.MustBool("session", "COOKIE_SECURE") | ||||||
|  | 	SessionConfig.EnableSetCookie = Cfg.MustBool("session", "ENABLE_SET_COOKIE", true) | ||||||
|  | 	SessionConfig.GcIntervalTime = Cfg.MustInt64("session", "GC_INTERVAL_TIME", 86400) | ||||||
|  | 	SessionConfig.SessionLifeTime = Cfg.MustInt64("session", "SESSION_LIFE_TIME", 86400) | ||||||
|  | 	SessionConfig.SessionIDHashFunc = Cfg.MustValue("session", "SESSION_ID_HASHFUNC", "sha1") | ||||||
|  | 	SessionConfig.SessionIDHashKey = Cfg.MustValue("session", "SESSION_ID_HASHKEY") | ||||||
|  |  | ||||||
|  | 	if SessionProvider == "file" { | ||||||
|  | 		os.MkdirAll(path.Dir(SessionConfig.ProviderConfig), os.ModePerm) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	SessionManager, err = session.NewManager(SessionProvider, *SessionConfig) | ||||||
|  | 	if err != nil { | ||||||
|  | 		fmt.Printf("Init session system failed, provider: %s, %v\n", | ||||||
|  | 			SessionProvider, err) | ||||||
|  | 		os.Exit(2) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Info("Session Service Enabled") | ||||||
|  | } | ||||||
|  |  | ||||||
| func newMailService() { | func newMailService() { | ||||||
| 	// Check mailer setting. | 	// Check mailer setting. | ||||||
| 	if Cfg.MustBool("mailer", "ENABLED") { | 	if Cfg.MustBool("mailer", "ENABLED") { | ||||||
| @@ -214,6 +260,13 @@ func NewConfigContext() { | |||||||
| 	SecretKey = Cfg.MustValue("security", "SECRET_KEY") | 	SecretKey = Cfg.MustValue("security", "SECRET_KEY") | ||||||
| 	RunUser = Cfg.MustValue("", "RUN_USER") | 	RunUser = Cfg.MustValue("", "RUN_USER") | ||||||
|  |  | ||||||
|  | 	LogInRememberDays = Cfg.MustInt("security", "LOGIN_REMEMBER_DAYS") | ||||||
|  | 	CookieUserName = Cfg.MustValue("security", "COOKIE_USERNAME") | ||||||
|  | 	CookieRememberName = Cfg.MustValue("security", "COOKIE_REMEMBER_NAME") | ||||||
|  |  | ||||||
|  | 	PictureService = Cfg.MustValue("picture", "SERVICE") | ||||||
|  | 	PictureRootPath = Cfg.MustValue("picture", "PATH") | ||||||
|  |  | ||||||
| 	// Determine and create root git reposiroty path. | 	// Determine and create root git reposiroty path. | ||||||
| 	RepoRootPath = Cfg.MustValue("repository", "ROOT") | 	RepoRootPath = Cfg.MustValue("repository", "ROOT") | ||||||
| 	if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | 	if err = os.MkdirAll(RepoRootPath, os.ModePerm); err != nil { | ||||||
| @@ -226,6 +279,7 @@ func NewServices() { | |||||||
| 	newService() | 	newService() | ||||||
| 	newLogService() | 	newLogService() | ||||||
| 	newCacheService() | 	newCacheService() | ||||||
|  | 	newSessionService() | ||||||
| 	newMailService() | 	newMailService() | ||||||
| 	newRegisterMailService() | 	newRegisterMailService() | ||||||
| } | } | ||||||
|   | |||||||
| @@ -25,13 +25,17 @@ func EncodeMd5(str string) string { | |||||||
| 	return hex.EncodeToString(m.Sum(nil)) | 	return hex.EncodeToString(m.Sum(nil)) | ||||||
| } | } | ||||||
|  |  | ||||||
| // Random generate string | // GetRandomString generate random string by specify chars. | ||||||
| func GetRandomString(n int) string { | func GetRandomString(n int, alphabets ...byte) string { | ||||||
| 	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | 	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" | ||||||
| 	var bytes = make([]byte, n) | 	var bytes = make([]byte, n) | ||||||
| 	rand.Read(bytes) | 	rand.Read(bytes) | ||||||
| 	for i, b := range bytes { | 	for i, b := range bytes { | ||||||
| 		bytes[i] = alphanum[b%byte(len(alphanum))] | 		if len(alphabets) == 0 { | ||||||
|  | 			bytes[i] = alphanum[b%byte(len(alphanum))] | ||||||
|  | 		} else { | ||||||
|  | 			bytes[i] = alphabets[b%byte(len(alphabets))] | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	return string(bytes) | 	return string(bytes) | ||||||
| } | } | ||||||
| @@ -111,6 +115,85 @@ const ( | |||||||
| 	Year   = 12 * Month | 	Year   = 12 * Month | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | func computeTimeDiff(diff int64) (int64, string) { | ||||||
|  | 	diffStr := "" | ||||||
|  | 	switch { | ||||||
|  | 	case diff <= 0: | ||||||
|  | 		diff = 0 | ||||||
|  | 		diffStr = "now" | ||||||
|  | 	case diff < 2: | ||||||
|  | 		diff = 0 | ||||||
|  | 		diffStr = "1 second" | ||||||
|  | 	case diff < 1*Minute: | ||||||
|  | 		diffStr = fmt.Sprintf("%d seconds", diff) | ||||||
|  | 		diff = 0 | ||||||
|  |  | ||||||
|  | 	case diff < 2*Minute: | ||||||
|  | 		diff -= 1 * Minute | ||||||
|  | 		diffStr = "1 minute" | ||||||
|  | 	case diff < 1*Hour: | ||||||
|  | 		diffStr = fmt.Sprintf("%d minutes", diff/Minute) | ||||||
|  | 		diff -= diff / Minute * Minute | ||||||
|  |  | ||||||
|  | 	case diff < 2*Hour: | ||||||
|  | 		diff -= 1 * Hour | ||||||
|  | 		diffStr = "1 hour" | ||||||
|  | 	case diff < 1*Day: | ||||||
|  | 		diffStr = fmt.Sprintf("%d hours", diff/Hour) | ||||||
|  | 		diff -= diff / Hour * Hour | ||||||
|  |  | ||||||
|  | 	case diff < 2*Day: | ||||||
|  | 		diff -= 1 * Day | ||||||
|  | 		diffStr = "1 day" | ||||||
|  | 	case diff < 1*Week: | ||||||
|  | 		diffStr = fmt.Sprintf("%d days", diff/Day) | ||||||
|  | 		diff -= diff / Day * Day | ||||||
|  |  | ||||||
|  | 	case diff < 2*Week: | ||||||
|  | 		diff -= 1 * Week | ||||||
|  | 		diffStr = "1 week" | ||||||
|  | 	case diff < 1*Month: | ||||||
|  | 		diffStr = fmt.Sprintf("%d weeks", diff/Week) | ||||||
|  | 		diff -= diff / Week * Week | ||||||
|  |  | ||||||
|  | 	case diff < 2*Month: | ||||||
|  | 		diff -= 1 * Month | ||||||
|  | 		diffStr = "1 month" | ||||||
|  | 	case diff < 1*Year: | ||||||
|  | 		diffStr = fmt.Sprintf("%d months", diff/Month) | ||||||
|  | 		diff -= diff / Month * Month | ||||||
|  |  | ||||||
|  | 	case diff < 2*Year: | ||||||
|  | 		diff -= 1 * Year | ||||||
|  | 		diffStr = "1 year" | ||||||
|  | 	default: | ||||||
|  | 		diffStr = fmt.Sprintf("%d years", diff/Year) | ||||||
|  | 		diff = 0 | ||||||
|  | 	} | ||||||
|  | 	return diff, diffStr | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TimeSincePro calculates the time interval and generate full user-friendly string. | ||||||
|  | func TimeSincePro(then time.Time) string { | ||||||
|  | 	now := time.Now() | ||||||
|  | 	diff := now.Unix() - then.Unix() | ||||||
|  |  | ||||||
|  | 	if then.After(now) { | ||||||
|  | 		return "future" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var timeStr, diffStr string | ||||||
|  | 	for { | ||||||
|  | 		if diff == 0 { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		diff, diffStr = computeTimeDiff(diff) | ||||||
|  | 		timeStr += ", " + diffStr | ||||||
|  | 	} | ||||||
|  | 	return strings.TrimPrefix(timeStr, ", ") | ||||||
|  | } | ||||||
|  |  | ||||||
| // TimeSince calculates the time interval and generate user-friendly string. | // TimeSince calculates the time interval and generate user-friendly string. | ||||||
| func TimeSince(then time.Time) string { | func TimeSince(then time.Time) string { | ||||||
| 	now := time.Now() | 	now := time.Now() | ||||||
| @@ -123,7 +206,6 @@ func TimeSince(then time.Time) string { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	switch { | 	switch { | ||||||
|  |  | ||||||
| 	case diff <= 0: | 	case diff <= 0: | ||||||
| 		return "now" | 		return "now" | ||||||
| 	case diff <= 2: | 	case diff <= 2: | ||||||
| @@ -156,8 +238,10 @@ func TimeSince(then time.Time) string { | |||||||
| 	case diff < 1*Year: | 	case diff < 1*Year: | ||||||
| 		return fmt.Sprintf("%d months %s", diff/Month, lbl) | 		return fmt.Sprintf("%d months %s", diff/Month, lbl) | ||||||
|  |  | ||||||
| 	case diff < 18*Month: | 	case diff < 2*Year: | ||||||
| 		return fmt.Sprintf("1 year %s", lbl) | 		return fmt.Sprintf("1 year %s", lbl) | ||||||
|  | 	default: | ||||||
|  | 		return fmt.Sprintf("%d years %s", diff/Year, lbl) | ||||||
| 	} | 	} | ||||||
| 	return then.String() | 	return then.String() | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,44 +5,54 @@ | |||||||
| package middleware | package middleware | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"net/url" | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
|  |  | ||||||
| 	"github.com/gogits/gogs/modules/base" | 	"github.com/gogits/gogs/modules/base" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // SignInRequire requires user to sign in. | type ToggleOptions struct { | ||||||
| func SignInRequire(redirect bool) martini.Handler { | 	SignInRequire  bool | ||||||
| 	return func(ctx *Context) { | 	SignOutRequire bool | ||||||
| 		if !ctx.IsSigned { | 	AdminRequire   bool | ||||||
| 			if redirect { | 	DisableCsrf    bool | ||||||
| 				ctx.Redirect("/user/login") |  | ||||||
| 			} |  | ||||||
| 			return |  | ||||||
| 		} else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { |  | ||||||
| 			ctx.Data["Title"] = "Activate Your Account" |  | ||||||
| 			ctx.HTML(200, "user/active") |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
|  |  | ||||||
| // SignOutRequire requires user to sign out. | func Toggle(options *ToggleOptions) martini.Handler { | ||||||
| func SignOutRequire() martini.Handler { |  | ||||||
| 	return func(ctx *Context) { | 	return func(ctx *Context) { | ||||||
| 		if ctx.IsSigned { | 		if options.SignOutRequire && ctx.IsSigned { | ||||||
| 			ctx.Redirect("/") | 			ctx.Redirect("/") | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // AdminRequire requires user signed in as administor. | 		if !options.DisableCsrf { | ||||||
| func AdminRequire() martini.Handler { | 			if ctx.Req.Method == "POST" { | ||||||
| 	return func(ctx *Context) { | 				if !ctx.CsrfTokenValid() { | ||||||
| 		if !ctx.User.IsAdmin { | 					ctx.Error(403, "CSRF token does not match") | ||||||
| 			ctx.Error(403) | 					return | ||||||
| 			return | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if options.SignInRequire { | ||||||
|  | 			if !ctx.IsSigned { | ||||||
|  | 				ctx.SetCookie("redirect_to", "/"+url.QueryEscape(ctx.Req.RequestURI)) | ||||||
|  | 				ctx.Redirect("/user/login") | ||||||
|  | 				return | ||||||
|  | 			} else if !ctx.User.IsActive && base.Service.RegisterEmailConfirm { | ||||||
|  | 				ctx.Data["Title"] = "Activate Your Account" | ||||||
|  | 				ctx.HTML(200, "user/active") | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if options.AdminRequire { | ||||||
|  | 			if !ctx.User.IsAdmin { | ||||||
|  | 				ctx.Error(403) | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			ctx.Data["PageIsAdmin"] = true | ||||||
| 		} | 		} | ||||||
| 		ctx.Data["PageIsAdmin"] = true |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,14 +5,20 @@ | |||||||
| package middleware | package middleware | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/hmac" | ||||||
|  | 	"crypto/sha1" | ||||||
|  | 	"encoding/base64" | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"html/template" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
| 	"github.com/martini-contrib/sessions" |  | ||||||
|  |  | ||||||
| 	"github.com/gogits/cache" | 	"github.com/gogits/cache" | ||||||
|  | 	"github.com/gogits/session" | ||||||
|  |  | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
| @@ -27,11 +33,13 @@ type Context struct { | |||||||
| 	p        martini.Params | 	p        martini.Params | ||||||
| 	Req      *http.Request | 	Req      *http.Request | ||||||
| 	Res      http.ResponseWriter | 	Res      http.ResponseWriter | ||||||
| 	Session  sessions.Session | 	Session  session.SessionStore | ||||||
| 	Cache    cache.Cache | 	Cache    cache.Cache | ||||||
| 	User     *models.User | 	User     *models.User | ||||||
| 	IsSigned bool | 	IsSigned bool | ||||||
|  |  | ||||||
|  | 	csrfToken string | ||||||
|  |  | ||||||
| 	Repo struct { | 	Repo struct { | ||||||
| 		IsValid    bool | 		IsValid    bool | ||||||
| 		IsOwner    bool | 		IsOwner    bool | ||||||
| @@ -90,23 +98,157 @@ func (ctx *Context) Handle(status int, title string, err error) { | |||||||
| 	ctx.HTML(status, fmt.Sprintf("status/%d", status)) | 	ctx.HTML(status, fmt.Sprintf("status/%d", status)) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func (ctx *Context) GetCookie(name string) string { | ||||||
|  | 	cookie, err := ctx.Req.Cookie(name) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return "" | ||||||
|  | 	} | ||||||
|  | 	return cookie.Value | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ctx *Context) SetCookie(name string, value string, others ...interface{}) { | ||||||
|  | 	cookie := http.Cookie{} | ||||||
|  | 	cookie.Name = name | ||||||
|  | 	cookie.Value = value | ||||||
|  |  | ||||||
|  | 	if len(others) > 0 { | ||||||
|  | 		switch v := others[0].(type) { | ||||||
|  | 		case int: | ||||||
|  | 			cookie.MaxAge = v | ||||||
|  | 		case int64: | ||||||
|  | 			cookie.MaxAge = int(v) | ||||||
|  | 		case int32: | ||||||
|  | 			cookie.MaxAge = int(v) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// default "/" | ||||||
|  | 	if len(others) > 1 { | ||||||
|  | 		if v, ok := others[1].(string); ok && len(v) > 0 { | ||||||
|  | 			cookie.Path = v | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		cookie.Path = "/" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// default empty | ||||||
|  | 	if len(others) > 2 { | ||||||
|  | 		if v, ok := others[2].(string); ok && len(v) > 0 { | ||||||
|  | 			cookie.Domain = v | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// default empty | ||||||
|  | 	if len(others) > 3 { | ||||||
|  | 		switch v := others[3].(type) { | ||||||
|  | 		case bool: | ||||||
|  | 			cookie.Secure = v | ||||||
|  | 		default: | ||||||
|  | 			if others[3] != nil { | ||||||
|  | 				cookie.Secure = true | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// default false. for session cookie default true | ||||||
|  | 	if len(others) > 4 { | ||||||
|  | 		if v, ok := others[4].(bool); ok && v { | ||||||
|  | 			cookie.HttpOnly = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Res.Header().Add("Set-Cookie", cookie.String()) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Get secure cookie from request by a given key. | ||||||
|  | func (ctx *Context) GetSecureCookie(Secret, key string) (string, bool) { | ||||||
|  | 	val := ctx.GetCookie(key) | ||||||
|  | 	if val == "" { | ||||||
|  | 		return "", false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	parts := strings.SplitN(val, "|", 3) | ||||||
|  |  | ||||||
|  | 	if len(parts) != 3 { | ||||||
|  | 		return "", false | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	vs := parts[0] | ||||||
|  | 	timestamp := parts[1] | ||||||
|  | 	sig := parts[2] | ||||||
|  |  | ||||||
|  | 	h := hmac.New(sha1.New, []byte(Secret)) | ||||||
|  | 	fmt.Fprintf(h, "%s%s", vs, timestamp) | ||||||
|  |  | ||||||
|  | 	if fmt.Sprintf("%02x", h.Sum(nil)) != sig { | ||||||
|  | 		return "", false | ||||||
|  | 	} | ||||||
|  | 	res, _ := base64.URLEncoding.DecodeString(vs) | ||||||
|  | 	return string(res), true | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Set Secure cookie for response. | ||||||
|  | func (ctx *Context) SetSecureCookie(Secret, name, value string, others ...interface{}) { | ||||||
|  | 	vs := base64.URLEncoding.EncodeToString([]byte(value)) | ||||||
|  | 	timestamp := strconv.FormatInt(time.Now().UnixNano(), 10) | ||||||
|  | 	h := hmac.New(sha1.New, []byte(Secret)) | ||||||
|  | 	fmt.Fprintf(h, "%s%s", vs, timestamp) | ||||||
|  | 	sig := fmt.Sprintf("%02x", h.Sum(nil)) | ||||||
|  | 	cookie := strings.Join([]string{vs, timestamp, sig}, "|") | ||||||
|  | 	ctx.SetCookie(name, cookie, others...) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ctx *Context) CsrfToken() string { | ||||||
|  | 	if len(ctx.csrfToken) > 0 { | ||||||
|  | 		return ctx.csrfToken | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	token := ctx.GetCookie("_csrf") | ||||||
|  | 	if len(token) == 0 { | ||||||
|  | 		token = base.GetRandomString(30) | ||||||
|  | 		ctx.SetCookie("_csrf", token) | ||||||
|  | 	} | ||||||
|  | 	ctx.csrfToken = token | ||||||
|  | 	return token | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (ctx *Context) CsrfTokenValid() bool { | ||||||
|  | 	token := ctx.Query("_csrf") | ||||||
|  | 	if token == "" { | ||||||
|  | 		token = ctx.Req.Header.Get("X-Csrf-Token") | ||||||
|  | 	} | ||||||
|  | 	if token == "" { | ||||||
|  | 		return false | ||||||
|  | 	} else if ctx.csrfToken != token { | ||||||
|  | 		return false | ||||||
|  | 	} | ||||||
|  | 	return true | ||||||
|  | } | ||||||
|  |  | ||||||
| // InitContext initializes a classic context for a request. | // InitContext initializes a classic context for a request. | ||||||
| func InitContext() martini.Handler { | func InitContext() martini.Handler { | ||||||
| 	return func(res http.ResponseWriter, r *http.Request, c martini.Context, | 	return func(res http.ResponseWriter, r *http.Request, c martini.Context, rd *Render) { | ||||||
| 		session sessions.Session, rd *Render) { |  | ||||||
|  |  | ||||||
| 		ctx := &Context{ | 		ctx := &Context{ | ||||||
| 			c: c, | 			c: c, | ||||||
| 			// p:      p, | 			// p:      p, | ||||||
| 			Req:     r, | 			Req:    r, | ||||||
| 			Res:     res, | 			Res:    res, | ||||||
| 			Session: session, | 			Cache:  base.Cache, | ||||||
| 			Cache:   base.Cache, | 			Render: rd, | ||||||
| 			Render:  rd, |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | 		ctx.Data["PageStartTime"] = time.Now() | ||||||
|  |  | ||||||
|  | 		// start session | ||||||
|  | 		ctx.Session = base.SessionManager.SessionStart(res, r) | ||||||
|  | 		rw := res.(martini.ResponseWriter) | ||||||
|  | 		rw.Before(func(martini.ResponseWriter) { | ||||||
|  | 			ctx.Session.SessionRelease(res) | ||||||
|  | 		}) | ||||||
|  |  | ||||||
| 		// Get user from session if logined. | 		// Get user from session if logined. | ||||||
| 		user := auth.SignedInUser(session) | 		user := auth.SignedInUser(ctx.Session) | ||||||
| 		ctx.User = user | 		ctx.User = user | ||||||
| 		ctx.IsSigned = user != nil | 		ctx.IsSigned = user != nil | ||||||
|  |  | ||||||
| @@ -119,7 +261,9 @@ func InitContext() martini.Handler { | |||||||
| 			ctx.Data["IsAdmin"] = ctx.User.IsAdmin | 			ctx.Data["IsAdmin"] = ctx.User.IsAdmin | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		ctx.Data["PageStartTime"] = time.Now() | 		// get or create csrf token | ||||||
|  | 		ctx.Data["CsrfToken"] = ctx.CsrfToken() | ||||||
|  | 		ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.csrfToken + `">`) | ||||||
|  |  | ||||||
| 		c.Map(ctx) | 		c.Map(ctx) | ||||||
|  |  | ||||||
|   | |||||||
| @@ -242,8 +242,11 @@ func (r *Render) HTMLString(name string, binding interface{}, htmlOpt ...HTMLOpt | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Render) Error(status int) { | func (r *Render) Error(status int, message ...string) { | ||||||
| 	r.WriteHeader(status) | 	r.WriteHeader(status) | ||||||
|  | 	if len(message) > 0 { | ||||||
|  | 		r.Write([]byte(message[0])) | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func (r *Render) Redirect(location string, status ...int) { | func (r *Render) Redirect(location string, status ...int) { | ||||||
|   | |||||||
| @@ -54,7 +54,7 @@ func RepoAssignment(redirect bool) martini.Handler { | |||||||
| 		ctx.Repo.Owner = user | 		ctx.Repo.Owner = user | ||||||
|  |  | ||||||
| 		// get repository | 		// get repository | ||||||
| 		repo, err := models.GetRepositoryByName(user, params["reponame"]) | 		repo, err := models.GetRepositoryByName(user.Id, params["reponame"]) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			if redirect { | 			if redirect { | ||||||
| 				ctx.Redirect("/") | 				ctx.Redirect("/") | ||||||
|   | |||||||
| @@ -346,6 +346,10 @@ html, body { | |||||||
|     border-left: 4px solid #DD4B39; |     border-left: 4px solid #DD4B39; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | #gogs-repo-setting-container .form-horizontal label { | ||||||
|  |     line-height: 30px; | ||||||
|  | } | ||||||
|  |  | ||||||
| /* gogits user ssh keys */ | /* gogits user ssh keys */ | ||||||
|  |  | ||||||
| #gogs-ssh-keys .list-group-item { | #gogs-ssh-keys .list-group-item { | ||||||
| @@ -575,12 +579,12 @@ html, body { | |||||||
|     min-width: 200px; |     min-width: 200px; | ||||||
| } | } | ||||||
|  |  | ||||||
| #gogs-repo-clone .dropdown-menu{ | #gogs-repo-clone .dropdown-menu { | ||||||
|     width: 400px; |     width: 400px; | ||||||
|     padding: 20px; |     padding: 20px; | ||||||
| } | } | ||||||
|  |  | ||||||
| #gogs-repo-clone .input-group{ | #gogs-repo-clone .input-group { | ||||||
|     margin-bottom: 15px; |     margin-bottom: 15px; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -2,6 +2,39 @@ var Gogits = { | |||||||
|     "PageIsSignup": false |     "PageIsSignup": false | ||||||
| }; | }; | ||||||
|  |  | ||||||
|  | (function($){ | ||||||
|  |     // extend jQuery ajax, set csrf token value | ||||||
|  |     var ajax = $.ajax; | ||||||
|  |     $.extend({ | ||||||
|  |         ajax: function(url, options) { | ||||||
|  |             if (typeof url === 'object') { | ||||||
|  |                 options = url; | ||||||
|  |                 url = undefined; | ||||||
|  |             } | ||||||
|  |             options = options || {}; | ||||||
|  |             url = options.url; | ||||||
|  |             var csrftoken = $('meta[name=_csrf]').attr('content'); | ||||||
|  |             var headers = options.headers || {}; | ||||||
|  |             var domain = document.domain.replace(/\./ig, '\\.'); | ||||||
|  |             if (!/^(http:|https:).*/.test(url) || eval('/^(http:|https:)\\/\\/(.+\\.)*' + domain + '.*/').test(url)) { | ||||||
|  |                 headers = $.extend(headers, {'X-Csrf-Token':csrftoken}); | ||||||
|  |             } | ||||||
|  |             options.headers = headers; | ||||||
|  |             var callback = options.success; | ||||||
|  |             options.success = function(data){ | ||||||
|  |                 if(data.once){ | ||||||
|  |                     // change all _once value if ajax data.once exist | ||||||
|  |                     $('[name=_once]').val(data.once); | ||||||
|  |                 } | ||||||
|  |                 if(callback){ | ||||||
|  |                     callback.apply(this, arguments); | ||||||
|  |                 } | ||||||
|  |             }; | ||||||
|  |             return ajax(url, options); | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | }(jQuery)); | ||||||
|  |  | ||||||
| (function ($) { | (function ($) { | ||||||
|  |  | ||||||
|     Gogits.showTab = function (selector, index) { |     Gogits.showTab = function (selector, index) { | ||||||
|   | |||||||
| @@ -5,7 +5,10 @@ | |||||||
| package admin | package admin | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"runtime" | ||||||
| 	"strings" | 	"strings" | ||||||
|  | 	"time" | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
|  |  | ||||||
| @@ -14,10 +17,93 @@ import ( | |||||||
| 	"github.com/gogits/gogs/modules/middleware" | 	"github.com/gogits/gogs/modules/middleware" | ||||||
| ) | ) | ||||||
|  |  | ||||||
|  | var startTime = time.Now() | ||||||
|  |  | ||||||
|  | var sysStatus struct { | ||||||
|  | 	Uptime       string | ||||||
|  | 	NumGoroutine int | ||||||
|  |  | ||||||
|  | 	// General statistics. | ||||||
|  | 	MemAllocated string // bytes allocated and still in use | ||||||
|  | 	MemTotal     string // bytes allocated (even if freed) | ||||||
|  | 	MemSys       string // bytes obtained from system (sum of XxxSys below) | ||||||
|  | 	Lookups      uint64 // number of pointer lookups | ||||||
|  | 	MemMallocs   uint64 // number of mallocs | ||||||
|  | 	MemFrees     uint64 // number of frees | ||||||
|  |  | ||||||
|  | 	// Main allocation heap statistics. | ||||||
|  | 	HeapAlloc    string // bytes allocated and still in use | ||||||
|  | 	HeapSys      string // bytes obtained from system | ||||||
|  | 	HeapIdle     string // bytes in idle spans | ||||||
|  | 	HeapInuse    string // bytes in non-idle span | ||||||
|  | 	HeapReleased string // bytes released to the OS | ||||||
|  | 	HeapObjects  uint64 // total number of allocated objects | ||||||
|  |  | ||||||
|  | 	// Low-level fixed-size structure allocator statistics. | ||||||
|  | 	//	Inuse is bytes used now. | ||||||
|  | 	//	Sys is bytes obtained from system. | ||||||
|  | 	StackInuse  string // bootstrap stacks | ||||||
|  | 	StackSys    string | ||||||
|  | 	MSpanInuse  string // mspan structures | ||||||
|  | 	MSpanSys    string | ||||||
|  | 	MCacheInuse string // mcache structures | ||||||
|  | 	MCacheSys   string | ||||||
|  | 	BuckHashSys string // profiling bucket hash table | ||||||
|  | 	GCSys       string // GC metadata | ||||||
|  | 	OtherSys    string // other system allocations | ||||||
|  |  | ||||||
|  | 	// Garbage collector statistics. | ||||||
|  | 	NextGC       string // next run in HeapAlloc time (bytes) | ||||||
|  | 	LastGC       string // last run in absolute time (ns) | ||||||
|  | 	PauseTotalNs string | ||||||
|  | 	PauseNs      string // circular buffer of recent GC pause times, most recent at [(NumGC+255)%256] | ||||||
|  | 	NumGC        uint32 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func updateSystemStatus() { | ||||||
|  | 	sysStatus.Uptime = base.TimeSincePro(startTime) | ||||||
|  |  | ||||||
|  | 	m := new(runtime.MemStats) | ||||||
|  | 	runtime.ReadMemStats(m) | ||||||
|  | 	sysStatus.NumGoroutine = runtime.NumGoroutine() | ||||||
|  |  | ||||||
|  | 	sysStatus.MemAllocated = base.FileSize(int64(m.Alloc)) | ||||||
|  | 	sysStatus.MemTotal = base.FileSize(int64(m.TotalAlloc)) | ||||||
|  | 	sysStatus.MemSys = base.FileSize(int64(m.Sys)) | ||||||
|  | 	sysStatus.Lookups = m.Lookups | ||||||
|  | 	sysStatus.MemMallocs = m.Mallocs | ||||||
|  | 	sysStatus.MemFrees = m.Frees | ||||||
|  |  | ||||||
|  | 	sysStatus.HeapAlloc = base.FileSize(int64(m.HeapAlloc)) | ||||||
|  | 	sysStatus.HeapSys = base.FileSize(int64(m.HeapSys)) | ||||||
|  | 	sysStatus.HeapIdle = base.FileSize(int64(m.HeapIdle)) | ||||||
|  | 	sysStatus.HeapInuse = base.FileSize(int64(m.HeapInuse)) | ||||||
|  | 	sysStatus.HeapReleased = base.FileSize(int64(m.HeapReleased)) | ||||||
|  | 	sysStatus.HeapObjects = m.HeapObjects | ||||||
|  |  | ||||||
|  | 	sysStatus.StackInuse = base.FileSize(int64(m.StackInuse)) | ||||||
|  | 	sysStatus.StackSys = base.FileSize(int64(m.StackSys)) | ||||||
|  | 	sysStatus.MSpanInuse = base.FileSize(int64(m.MSpanInuse)) | ||||||
|  | 	sysStatus.MSpanSys = base.FileSize(int64(m.MSpanSys)) | ||||||
|  | 	sysStatus.MCacheInuse = base.FileSize(int64(m.MCacheInuse)) | ||||||
|  | 	sysStatus.MCacheSys = base.FileSize(int64(m.MCacheSys)) | ||||||
|  | 	sysStatus.BuckHashSys = base.FileSize(int64(m.BuckHashSys)) | ||||||
|  | 	sysStatus.GCSys = base.FileSize(int64(m.GCSys)) | ||||||
|  | 	sysStatus.OtherSys = base.FileSize(int64(m.OtherSys)) | ||||||
|  |  | ||||||
|  | 	sysStatus.NextGC = base.FileSize(int64(m.NextGC)) | ||||||
|  | 	sysStatus.LastGC = fmt.Sprintf("%.1fs", float64(time.Now().UnixNano()-int64(m.LastGC))/1000/1000/1000) | ||||||
|  | 	sysStatus.PauseTotalNs = fmt.Sprintf("%.1fs", float64(m.PauseTotalNs)/1000/1000/1000) | ||||||
|  | 	sysStatus.PauseNs = fmt.Sprintf("%.3fs", float64(m.PauseNs[(m.NumGC+255)%256])/1000/1000/1000) | ||||||
|  | 	sysStatus.NumGC = m.NumGC | ||||||
|  | } | ||||||
|  |  | ||||||
| func Dashboard(ctx *middleware.Context) { | func Dashboard(ctx *middleware.Context) { | ||||||
| 	ctx.Data["Title"] = "Admin Dashboard" | 	ctx.Data["Title"] = "Admin Dashboard" | ||||||
| 	ctx.Data["PageIsDashboard"] = true | 	ctx.Data["PageIsDashboard"] = true | ||||||
| 	ctx.Data["Stats"] = models.GetStatistic() | 	ctx.Data["Stats"] = models.GetStatistic() | ||||||
|  | 	updateSystemStatus() | ||||||
|  | 	ctx.Data["SysStatus"] = sysStatus | ||||||
| 	ctx.HTML(200, "admin/dashboard") | 	ctx.HTML(200, "admin/dashboard") | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -70,6 +156,12 @@ func Config(ctx *middleware.Context) { | |||||||
| 	ctx.Data["CacheAdapter"] = base.CacheAdapter | 	ctx.Data["CacheAdapter"] = base.CacheAdapter | ||||||
| 	ctx.Data["CacheConfig"] = base.CacheConfig | 	ctx.Data["CacheConfig"] = base.CacheConfig | ||||||
|  |  | ||||||
|  | 	ctx.Data["SessionProvider"] = base.SessionProvider | ||||||
|  | 	ctx.Data["SessionConfig"] = base.SessionConfig | ||||||
|  |  | ||||||
|  | 	ctx.Data["PictureService"] = base.PictureService | ||||||
|  | 	ctx.Data["PictureRootPath"] = base.PictureRootPath | ||||||
|  |  | ||||||
| 	ctx.Data["LogMode"] = base.LogMode | 	ctx.Data["LogMode"] = base.LogMode | ||||||
| 	ctx.Data["LogConfig"] = base.LogConfig | 	ctx.Data["LogConfig"] = base.LogConfig | ||||||
|  |  | ||||||
|   | |||||||
| @@ -107,3 +107,38 @@ func EditUser(ctx *middleware.Context, params martini.Params, form auth.AdminEdi | |||||||
| 	log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI, | 	log.Trace("%s User profile updated by admin(%s): %s", ctx.Req.RequestURI, | ||||||
| 		ctx.User.LowerName, ctx.User.LowerName) | 		ctx.User.LowerName, ctx.User.LowerName) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func DeleteUser(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	ctx.Data["Title"] = "Edit Account" | ||||||
|  | 	ctx.Data["PageIsUsers"] = true | ||||||
|  |  | ||||||
|  | 	uid, err := base.StrTo(params["userid"]).Int() | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(200, "admin.user.EditUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	u, err := models.GetUserById(int64(uid)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(200, "admin.user.EditUser", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err = models.DeleteUser(u); err != nil { | ||||||
|  | 		ctx.Data["HasError"] = true | ||||||
|  | 		switch err { | ||||||
|  | 		case models.ErrUserOwnRepos: | ||||||
|  | 			ctx.Data["ErrorMsg"] = "This account still has ownership of repository, owner has to delete or transfer them first." | ||||||
|  | 			ctx.Data["User"] = u | ||||||
|  | 			ctx.HTML(200, "admin/users/edit") | ||||||
|  | 		default: | ||||||
|  | 			ctx.Handle(200, "admin.user.DeleteUser", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Trace("%s User deleted by admin(%s): %s", ctx.Req.RequestURI, | ||||||
|  | 		ctx.User.LowerName, ctx.User.LowerName) | ||||||
|  |  | ||||||
|  | 	ctx.Redirect("/admin/users") | ||||||
|  | } | ||||||
|   | |||||||
| @@ -20,5 +20,12 @@ func Home(ctx *middleware.Context) { | |||||||
|  |  | ||||||
| func Help(ctx *middleware.Context) { | func Help(ctx *middleware.Context) { | ||||||
| 	ctx.Data["PageIsHelp"] = true | 	ctx.Data["PageIsHelp"] = true | ||||||
|  | 	ctx.Data["Title"] = "Help" | ||||||
| 	ctx.HTML(200, "help") | 	ctx.HTML(200, "help") | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func NotFound(ctx *middleware.Context) { | ||||||
|  | 	ctx.Data["PageIsNotFound"] = true | ||||||
|  | 	ctx.Data["Title"] = 404 | ||||||
|  | 	ctx.Handle(404, "home.NotFound", nil) | ||||||
|  | } | ||||||
|   | |||||||
							
								
								
									
										85
									
								
								routers/repo/issue.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								routers/repo/issue.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,85 @@ | |||||||
|  | // Copyright 2014 The Gogs Authors. All rights reserved. | ||||||
|  | // Use of this source code is governed by a MIT-style | ||||||
|  | // license that can be found in the LICENSE file. | ||||||
|  |  | ||||||
|  | package repo | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  |  | ||||||
|  | 	"github.com/codegangsta/martini" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/gogs/models" | ||||||
|  | 	"github.com/gogits/gogs/modules/auth" | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
|  | 	"github.com/gogits/gogs/modules/log" | ||||||
|  | 	"github.com/gogits/gogs/modules/middleware" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Issues(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	ctx.Data["Title"] = "Issues" | ||||||
|  | 	ctx.Data["IsRepoToolbarIssues"] = true | ||||||
|  |  | ||||||
|  | 	milestoneId, _ := base.StrTo(params["milestone"]).Int() | ||||||
|  | 	page, _ := base.StrTo(params["page"]).Int() | ||||||
|  |  | ||||||
|  | 	var err error | ||||||
|  | 	ctx.Data["Issues"], err = models.GetIssues(0, ctx.Repo.Repository.Id, 0, | ||||||
|  | 		int64(milestoneId), page, params["state"] == "closed", false, params["labels"], params["sortType"]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(200, "issue.Issues: %v", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.HTML(200, "repo/issues") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func CreateIssue(ctx *middleware.Context, params martini.Params, form auth.CreateIssueForm) { | ||||||
|  | 	if !ctx.Repo.IsOwner { | ||||||
|  | 		ctx.Handle(404, "issue.CreateIssue", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Title"] = "Create issue" | ||||||
|  |  | ||||||
|  | 	if ctx.Req.Method == "GET" { | ||||||
|  | 		ctx.HTML(200, "issue/create") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(200, "issue/create") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	issue, err := models.CreateIssue(ctx.User.Id, form.RepoId, form.MilestoneId, form.AssigneeId, | ||||||
|  | 		form.IssueName, form.Labels, form.Content, false) | ||||||
|  | 	if err == nil { | ||||||
|  | 		log.Trace("%s Issue created: %d", form.RepoId, issue.Id) | ||||||
|  | 		ctx.Redirect(fmt.Sprintf("/%s/%s/issues/%d", params["username"], params["reponame"], issue.Index)) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Handle(200, "issue.CreateIssue", err) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func ViewIssue(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	issueid, err := base.StrTo(params["issueid"]).Int() | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(404, "issue.ViewIssue", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	issue, err := models.GetIssueById(int64(issueid)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		if err == models.ErrIssueNotExist { | ||||||
|  | 			ctx.Handle(404, "issue.ViewIssue", err) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Handle(200, "issue.ViewIssue", err) | ||||||
|  | 		} | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Title"] = issue.Name | ||||||
|  | 	ctx.Data["Issue"] = issue | ||||||
|  | 	ctx.HTML(200, "issue/view") | ||||||
|  | } | ||||||
| @@ -5,8 +5,17 @@ | |||||||
| package repo | package repo | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
|  | 	"path" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/codegangsta/martini" | ||||||
|  |  | ||||||
|  | 	"github.com/gogits/git" | ||||||
|  | 	"github.com/gogits/webdav" | ||||||
|  |  | ||||||
| 	"github.com/gogits/gogs/models" | 	"github.com/gogits/gogs/models" | ||||||
| 	"github.com/gogits/gogs/modules/auth" | 	"github.com/gogits/gogs/modules/auth" | ||||||
|  | 	"github.com/gogits/gogs/modules/base" | ||||||
| 	"github.com/gogits/gogs/modules/log" | 	"github.com/gogits/gogs/modules/log" | ||||||
| 	"github.com/gogits/gogs/modules/middleware" | 	"github.com/gogits/gogs/modules/middleware" | ||||||
| ) | ) | ||||||
| @@ -22,11 +31,16 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if ctx.HasError() { | ||||||
|  | 		ctx.HTML(200, "repo/create") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	_, err := models.CreateRepository(ctx.User, form.RepoName, form.Description, | 	_, err := models.CreateRepository(ctx.User, form.RepoName, form.Description, | ||||||
| 		form.Language, form.License, form.Visibility == "private", form.InitReadme == "on") | 		form.Language, form.License, form.Visibility == "private", form.InitReadme == "on") | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName) | 		log.Trace("%s Repository created: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, form.RepoName) | ||||||
| 		ctx.Redirect("/"+ctx.User.Name+"/"+form.RepoName, 302) | 		ctx.Redirect("/" + ctx.User.Name + "/" + form.RepoName) | ||||||
| 		return | 		return | ||||||
| 	} else if err == models.ErrRepoAlreadyExist { | 	} else if err == models.ErrRepoAlreadyExist { | ||||||
| 		ctx.RenderWithErr("Repository name has already been used", "repo/create", &form) | 		ctx.RenderWithErr("Repository name has already been used", "repo/create", &form) | ||||||
| @@ -59,5 +73,290 @@ func SettingPost(ctx *middleware.Context) { | |||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) | 	log.Trace("%s Repository deleted: %s/%s", ctx.Req.RequestURI, ctx.User.LowerName, ctx.Repo.Repository.LowerName) | ||||||
| 	ctx.Redirect("/", 302) | 	ctx.Redirect("/") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Branches(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	if !ctx.Repo.IsValid { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	brs, err := models.GetBranches(params["username"], params["reponame"]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(200, "repo.Branches", err) | ||||||
|  | 		return | ||||||
|  | 	} else if len(brs) == 0 { | ||||||
|  | 		ctx.Handle(404, "repo.Branches", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Username"] = params["username"] | ||||||
|  | 	ctx.Data["Reponame"] = params["reponame"] | ||||||
|  |  | ||||||
|  | 	ctx.Data["Branchname"] = brs[0] | ||||||
|  | 	ctx.Data["Branches"] = brs | ||||||
|  | 	ctx.Data["IsRepoToolbarBranches"] = true | ||||||
|  |  | ||||||
|  | 	ctx.HTML(200, "repo/branches") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Single(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	if !ctx.Repo.IsValid { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(params["branchname"]) == 0 { | ||||||
|  | 		params["branchname"] = "master" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Get tree path | ||||||
|  | 	treename := params["_1"] | ||||||
|  |  | ||||||
|  | 	if len(treename) > 0 && treename[len(treename)-1] == '/' { | ||||||
|  | 		ctx.Redirect("/" + ctx.Repo.Owner.LowerName + "/" + | ||||||
|  | 			ctx.Repo.Repository.Name + "/src/" + params["branchname"] + "/" + treename[:len(treename)-1]) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["IsRepoToolbarSource"] = true | ||||||
|  |  | ||||||
|  | 	// Branches. | ||||||
|  | 	brs, err := models.GetBranches(params["username"], params["reponame"]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		//log.Error("repo.Single(GetBranches): %v", err) | ||||||
|  | 		ctx.Handle(404, "repo.Single(GetBranches)", err) | ||||||
|  | 		return | ||||||
|  | 	} else if ctx.Repo.Repository.IsBare { | ||||||
|  | 		ctx.Data["IsBareRepo"] = true | ||||||
|  | 		ctx.HTML(200, "repo/single") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Branches"] = brs | ||||||
|  |  | ||||||
|  | 	repoFile, err := models.GetTargetFile(params["username"], params["reponame"], | ||||||
|  | 		params["branchname"], params["commitid"], treename) | ||||||
|  |  | ||||||
|  | 	if err != nil && err != models.ErrRepoFileNotExist { | ||||||
|  | 		//log.Error("repo.Single(GetTargetFile): %v", err) | ||||||
|  | 		ctx.Handle(404, "repo.Single(GetTargetFile)", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] | ||||||
|  |  | ||||||
|  | 	if len(treename) != 0 && repoFile == nil { | ||||||
|  | 		ctx.Handle(404, "repo.Single", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if repoFile != nil && repoFile.IsFile() { | ||||||
|  | 		if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { | ||||||
|  | 			ctx.Data["FileIsLarge"] = true | ||||||
|  | 		} else if blob, err := repoFile.LookupBlob(); err != nil { | ||||||
|  | 			//log.Error("repo.Single(repoFile.LookupBlob): %v", err) | ||||||
|  | 			ctx.Handle(404, "repo.Single(repoFile.LookupBlob)", err) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Data["IsFile"] = true | ||||||
|  | 			ctx.Data["FileName"] = repoFile.Name | ||||||
|  | 			ext := path.Ext(repoFile.Name) | ||||||
|  | 			if len(ext) > 0 { | ||||||
|  | 				ext = ext[1:] | ||||||
|  | 			} | ||||||
|  | 			ctx.Data["FileExt"] = ext | ||||||
|  |  | ||||||
|  | 			readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) | ||||||
|  | 			ctx.Data["ReadmeExist"] = readmeExist | ||||||
|  | 			if readmeExist { | ||||||
|  | 				ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) | ||||||
|  | 			} else { | ||||||
|  | 				ctx.Data["FileContent"] = string(blob.Contents()) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 	} else { | ||||||
|  | 		// Directory and file list. | ||||||
|  | 		files, err := models.GetReposFiles(params["username"], params["reponame"], | ||||||
|  | 			params["branchname"], params["commitid"], treename) | ||||||
|  | 		if err != nil { | ||||||
|  | 			//log.Error("repo.Single(GetReposFiles): %v", err) | ||||||
|  | 			ctx.Handle(404, "repo.Single(GetReposFiles)", err) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ctx.Data["Files"] = files | ||||||
|  |  | ||||||
|  | 		var readmeFile *models.RepoFile | ||||||
|  |  | ||||||
|  | 		for _, f := range files { | ||||||
|  | 			if !f.IsFile() || !base.IsReadmeFile(f.Name) { | ||||||
|  | 				continue | ||||||
|  | 			} else { | ||||||
|  | 				readmeFile = f | ||||||
|  | 				break | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		if readmeFile != nil { | ||||||
|  | 			ctx.Data["ReadmeExist"] = true | ||||||
|  | 			// if file large than 1M not show it | ||||||
|  | 			if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { | ||||||
|  | 				ctx.Data["FileIsLarge"] = true | ||||||
|  | 			} else if blob, err := readmeFile.LookupBlob(); err != nil { | ||||||
|  | 				//log.Error("repo.Single(readmeFile.LookupBlob): %v", err) | ||||||
|  | 				ctx.Handle(404, "repo.Single(readmeFile.LookupBlob)", err) | ||||||
|  | 				return | ||||||
|  | 			} else { | ||||||
|  | 				// current repo branch link | ||||||
|  |  | ||||||
|  | 				ctx.Data["FileName"] = readmeFile.Name | ||||||
|  | 				ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Username"] = params["username"] | ||||||
|  | 	ctx.Data["Reponame"] = params["reponame"] | ||||||
|  | 	ctx.Data["Branchname"] = params["branchname"] | ||||||
|  |  | ||||||
|  | 	var treenames []string | ||||||
|  | 	Paths := make([]string, 0) | ||||||
|  |  | ||||||
|  | 	if len(treename) > 0 { | ||||||
|  | 		treenames = strings.Split(treename, "/") | ||||||
|  | 		for i, _ := range treenames { | ||||||
|  | 			Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ctx.Data["HasParentPath"] = true | ||||||
|  | 		if len(Paths)-2 >= 0 { | ||||||
|  | 			ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// Get latest commit according username and repo name | ||||||
|  | 	commit, err := models.GetCommit(params["username"], params["reponame"], | ||||||
|  | 		params["branchname"], params["commitid"]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("repo.Single(GetCommit): %v", err) | ||||||
|  | 		ctx.Handle(404, "repo.Single(GetCommit)", err) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["LastCommit"] = commit | ||||||
|  |  | ||||||
|  | 	ctx.Data["Paths"] = Paths | ||||||
|  | 	ctx.Data["Treenames"] = treenames | ||||||
|  | 	ctx.Data["BranchLink"] = branchLink | ||||||
|  | 	ctx.HTML(200, "repo/single") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Http(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	/*if !ctx.Repo.IsValid { | ||||||
|  | 		return | ||||||
|  | 	}*/ | ||||||
|  |  | ||||||
|  | 	// TODO: access check | ||||||
|  |  | ||||||
|  | 	username := params["username"] | ||||||
|  | 	reponame := params["reponame"] | ||||||
|  | 	if strings.HasSuffix(reponame, ".git") { | ||||||
|  | 		reponame = reponame[:len(reponame)-4] | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	prefix := path.Join("/", username, params["reponame"]) | ||||||
|  | 	server := &webdav.Server{ | ||||||
|  | 		Fs:         webdav.Dir(models.RepoPath(username, reponame)), | ||||||
|  | 		TrimPrefix: prefix, | ||||||
|  | 		Listings:   true, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	server.ServeHTTP(ctx.ResponseWriter, ctx.Req) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Setting(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	if !ctx.Repo.IsOwner { | ||||||
|  | 		ctx.Handle(404, "repo.Setting", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["IsRepoToolbarSetting"] = true | ||||||
|  |  | ||||||
|  | 	if ctx.Repo.Repository.IsBare { | ||||||
|  | 		ctx.Data["IsBareRepo"] = true | ||||||
|  | 		ctx.HTML(200, "repo/setting") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	var title string | ||||||
|  | 	if t, ok := ctx.Data["Title"].(string); ok { | ||||||
|  | 		title = t | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if len(params["branchname"]) == 0 { | ||||||
|  | 		params["branchname"] = "master" | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["Branchname"] = params["branchname"] | ||||||
|  | 	ctx.Data["Title"] = title + " - settings" | ||||||
|  | 	ctx.HTML(200, "repo/setting") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Commits(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	brs, err := models.GetBranches(params["username"], params["reponame"]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(200, "repo.Commits", err) | ||||||
|  | 		return | ||||||
|  | 	} else if len(brs) == 0 { | ||||||
|  | 		ctx.Handle(404, "repo.Commits", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	ctx.Data["IsRepoToolbarCommits"] = true | ||||||
|  | 	commits, err := models.GetCommits(params["username"], | ||||||
|  | 		params["reponame"], params["branchname"]) | ||||||
|  | 	if err != nil { | ||||||
|  | 		ctx.Handle(404, "repo.Commits", nil) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.Data["Username"] = params["username"] | ||||||
|  | 	ctx.Data["Reponame"] = params["reponame"] | ||||||
|  | 	ctx.Data["CommitCount"] = commits.Len() | ||||||
|  | 	ctx.Data["Commits"] = commits | ||||||
|  | 	ctx.HTML(200, "repo/commits") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Pulls(ctx *middleware.Context) { | ||||||
|  | 	ctx.Data["IsRepoToolbarPulls"] = true | ||||||
|  | 	ctx.HTML(200, "repo/pulls") | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func Action(ctx *middleware.Context, params martini.Params) { | ||||||
|  | 	var err error | ||||||
|  | 	switch params["action"] { | ||||||
|  | 	case "watch": | ||||||
|  | 		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) | ||||||
|  | 	case "unwatch": | ||||||
|  | 		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) | ||||||
|  | 	case "desc": | ||||||
|  | 		if !ctx.Repo.IsOwner { | ||||||
|  | 			ctx.Error(404) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		ctx.Repo.Repository.Description = ctx.Query("desc") | ||||||
|  | 		ctx.Repo.Repository.Website = ctx.Query("site") | ||||||
|  | 		err = models.UpdateRepository(ctx.Repo.Repository) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Error("repo.Action(%s): %v", params["action"], err) | ||||||
|  | 		ctx.JSON(200, map[string]interface{}{ | ||||||
|  | 			"ok":  false, | ||||||
|  | 			"err": err.Error(), | ||||||
|  | 		}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	ctx.JSON(200, map[string]interface{}{ | ||||||
|  | 		"ok": true, | ||||||
|  | 	}) | ||||||
| } | } | ||||||
|   | |||||||
| @@ -1,307 +0,0 @@ | |||||||
| // Copyright 2014 The Gogs Authors. All rights reserved. |  | ||||||
| // Use of this source code is governed by a MIT-style |  | ||||||
| // license that can be found in the LICENSE file. |  | ||||||
|  |  | ||||||
| package repo |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"path" |  | ||||||
| 	"strings" |  | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" |  | ||||||
|  |  | ||||||
| 	"github.com/gogits/git" |  | ||||||
| 	"github.com/gogits/webdav" |  | ||||||
|  |  | ||||||
| 	"github.com/gogits/gogs/models" |  | ||||||
| 	"github.com/gogits/gogs/modules/base" |  | ||||||
| 	"github.com/gogits/gogs/modules/log" |  | ||||||
| 	"github.com/gogits/gogs/modules/middleware" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func Branches(ctx *middleware.Context, params martini.Params) { |  | ||||||
| 	if !ctx.Repo.IsValid { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	brs, err := models.GetBranches(params["username"], params["reponame"]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		ctx.Handle(200, "repo.Branches", err) |  | ||||||
| 		return |  | ||||||
| 	} else if len(brs) == 0 { |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["Username"] = params["username"] |  | ||||||
| 	ctx.Data["Reponame"] = params["reponame"] |  | ||||||
|  |  | ||||||
| 	ctx.Data["Branchname"] = brs[0] |  | ||||||
| 	ctx.Data["Branches"] = brs |  | ||||||
| 	ctx.Data["IsRepoToolbarBranches"] = true |  | ||||||
|  |  | ||||||
| 	ctx.HTML(200, "repo/branches") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Single(ctx *middleware.Context, params martini.Params) { |  | ||||||
| 	if !ctx.Repo.IsValid { |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(params["branchname"]) == 0 { |  | ||||||
| 		params["branchname"] = "master" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Get tree path |  | ||||||
| 	treename := params["_1"] |  | ||||||
|  |  | ||||||
| 	if len(treename) > 0 && treename[len(treename)-1] == '/' { |  | ||||||
| 		ctx.Redirect("/"+ctx.Repo.Owner.LowerName+"/"+ |  | ||||||
| 			ctx.Repo.Repository.Name+"/src/"+params["branchname"]+"/"+treename[:len(treename)-1], 302) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["IsRepoToolbarSource"] = true |  | ||||||
|  |  | ||||||
| 	// Branches. |  | ||||||
| 	brs, err := models.GetBranches(params["username"], params["reponame"]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("repo.Single(GetBranches): %v", err) |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} else if len(brs) == 0 { |  | ||||||
| 		ctx.Data["IsBareRepo"] = true |  | ||||||
| 		ctx.HTML(200, "repo/single") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["Branches"] = brs |  | ||||||
|  |  | ||||||
| 	repoFile, err := models.GetTargetFile(params["username"], params["reponame"], |  | ||||||
| 		params["branchname"], params["commitid"], treename) |  | ||||||
|  |  | ||||||
| 	if err != nil && err != models.ErrRepoFileNotExist { |  | ||||||
| 		log.Error("repo.Single(GetTargetFile): %v", err) |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	branchLink := "/" + ctx.Repo.Owner.LowerName + "/" + ctx.Repo.Repository.Name + "/src/" + params["branchname"] |  | ||||||
|  |  | ||||||
| 	if len(treename) != 0 && repoFile == nil { |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if repoFile != nil && repoFile.IsFile() { |  | ||||||
| 		if repoFile.Size > 1024*1024 || repoFile.Filemode != git.FileModeBlob { |  | ||||||
| 			ctx.Data["FileIsLarge"] = true |  | ||||||
| 		} else if blob, err := repoFile.LookupBlob(); err != nil { |  | ||||||
| 			log.Error("repo.Single(repoFile.LookupBlob): %v", err) |  | ||||||
| 			ctx.Error(404) |  | ||||||
| 		} else { |  | ||||||
| 			ctx.Data["IsFile"] = true |  | ||||||
| 			ctx.Data["FileName"] = repoFile.Name |  | ||||||
| 			ext := path.Ext(repoFile.Name) |  | ||||||
| 			if len(ext) > 0 { |  | ||||||
| 				ext = ext[1:] |  | ||||||
| 			} |  | ||||||
| 			ctx.Data["FileExt"] = ext |  | ||||||
|  |  | ||||||
| 			readmeExist := base.IsMarkdownFile(repoFile.Name) || base.IsReadmeFile(repoFile.Name) |  | ||||||
| 			ctx.Data["ReadmeExist"] = readmeExist |  | ||||||
| 			if readmeExist { |  | ||||||
| 				ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), "")) |  | ||||||
| 			} else { |  | ||||||
| 				ctx.Data["FileContent"] = string(blob.Contents()) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 	} else { |  | ||||||
| 		// Directory and file list. |  | ||||||
| 		files, err := models.GetReposFiles(params["username"], params["reponame"], |  | ||||||
| 			params["branchname"], params["commitid"], treename) |  | ||||||
| 		if err != nil { |  | ||||||
| 			log.Error("repo.Single(GetReposFiles): %v", err) |  | ||||||
| 			ctx.Error(404) |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ctx.Data["Files"] = files |  | ||||||
|  |  | ||||||
| 		var readmeFile *models.RepoFile |  | ||||||
|  |  | ||||||
| 		for _, f := range files { |  | ||||||
| 			if !f.IsFile() || !base.IsReadmeFile(f.Name) { |  | ||||||
| 				continue |  | ||||||
| 			} else { |  | ||||||
| 				readmeFile = f |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		if readmeFile != nil { |  | ||||||
| 			ctx.Data["ReadmeExist"] = true |  | ||||||
| 			// if file large than 1M not show it |  | ||||||
| 			if readmeFile.Size > 1024*1024 || readmeFile.Filemode != git.FileModeBlob { |  | ||||||
| 				ctx.Data["FileIsLarge"] = true |  | ||||||
| 			} else if blob, err := readmeFile.LookupBlob(); err != nil { |  | ||||||
| 				log.Error("repo.Single(readmeFile.LookupBlob): %v", err) |  | ||||||
| 				ctx.Error(404) |  | ||||||
| 				return |  | ||||||
| 			} else { |  | ||||||
| 				// current repo branch link |  | ||||||
|  |  | ||||||
| 				ctx.Data["FileName"] = readmeFile.Name |  | ||||||
| 				ctx.Data["FileContent"] = string(base.RenderMarkdown(blob.Contents(), branchLink)) |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["Username"] = params["username"] |  | ||||||
| 	ctx.Data["Reponame"] = params["reponame"] |  | ||||||
| 	ctx.Data["Branchname"] = params["branchname"] |  | ||||||
|  |  | ||||||
| 	var treenames []string |  | ||||||
| 	Paths := make([]string, 0) |  | ||||||
|  |  | ||||||
| 	if len(treename) > 0 { |  | ||||||
| 		treenames = strings.Split(treename, "/") |  | ||||||
| 		for i, _ := range treenames { |  | ||||||
| 			Paths = append(Paths, strings.Join(treenames[0:i+1], "/")) |  | ||||||
| 		} |  | ||||||
|  |  | ||||||
| 		ctx.Data["HasParentPath"] = true |  | ||||||
| 		if len(Paths)-2 >= 0 { |  | ||||||
| 			ctx.Data["ParentPath"] = "/" + Paths[len(Paths)-2] |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	// Get latest commit according username and repo name |  | ||||||
| 	commit, err := models.GetCommit(params["username"], params["reponame"], |  | ||||||
| 		params["branchname"], params["commitid"]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("repo.Single(GetCommit): %v", err) |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ctx.Data["LastCommit"] = commit |  | ||||||
|  |  | ||||||
| 	ctx.Data["Paths"] = Paths |  | ||||||
| 	ctx.Data["Treenames"] = treenames |  | ||||||
| 	ctx.Data["BranchLink"] = branchLink |  | ||||||
| 	ctx.HTML(200, "repo/single") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Http(ctx *middleware.Context, params martini.Params) { |  | ||||||
| 	/*if !ctx.Repo.IsValid { |  | ||||||
| 		return |  | ||||||
| 	}*/ |  | ||||||
|  |  | ||||||
| 	// TODO: access check |  | ||||||
|  |  | ||||||
| 	username := params["username"] |  | ||||||
| 	reponame := params["reponame"] |  | ||||||
| 	if strings.HasSuffix(reponame, ".git") { |  | ||||||
| 		reponame = reponame[:len(reponame)-4] |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	prefix := path.Join("/", username, params["reponame"]) |  | ||||||
| 	server := &webdav.Server{ |  | ||||||
| 		Fs:         webdav.Dir(models.RepoPath(username, reponame)), |  | ||||||
| 		TrimPrefix: prefix, |  | ||||||
| 		Listings:   true, |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	server.ServeHTTP(ctx.ResponseWriter, ctx.Req) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Setting(ctx *middleware.Context, params martini.Params) { |  | ||||||
| 	if !ctx.Repo.IsOwner { |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["IsRepoToolbarSetting"] = true |  | ||||||
|  |  | ||||||
| 	// Branches. |  | ||||||
| 	brs, err := models.GetBranches(params["username"], params["reponame"]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("repo.Setting(GetBranches): %v", err) |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} else if len(brs) == 0 { |  | ||||||
| 		ctx.Data["IsBareRepo"] = true |  | ||||||
| 		ctx.HTML(200, "repo/setting") |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var title string |  | ||||||
| 	if t, ok := ctx.Data["Title"].(string); ok { |  | ||||||
| 		title = t |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if len(params["branchname"]) == 0 { |  | ||||||
| 		params["branchname"] = "master" |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["Branchname"] = params["branchname"] |  | ||||||
| 	ctx.Data["Title"] = title + " - settings" |  | ||||||
| 	ctx.HTML(200, "repo/setting") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Commits(ctx *middleware.Context, params martini.Params) { |  | ||||||
| 	brs, err := models.GetBranches(params["username"], params["reponame"]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		ctx.Handle(200, "repo.Commits", err) |  | ||||||
| 		return |  | ||||||
| 	} else if len(brs) == 0 { |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	ctx.Data["IsRepoToolbarCommits"] = true |  | ||||||
| 	commits, err := models.GetCommits(params["username"], |  | ||||||
| 		params["reponame"], params["branchname"]) |  | ||||||
| 	if err != nil { |  | ||||||
| 		ctx.Error(404) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ctx.Data["Username"] = params["username"] |  | ||||||
| 	ctx.Data["Reponame"] = params["reponame"] |  | ||||||
| 	ctx.Data["CommitCount"] = commits.Len() |  | ||||||
| 	ctx.Data["Commits"] = commits |  | ||||||
| 	ctx.HTML(200, "repo/commits") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Issues(ctx *middleware.Context) { |  | ||||||
| 	ctx.Data["IsRepoToolbarIssues"] = true |  | ||||||
| 	ctx.HTML(200, "repo/issues") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Pulls(ctx *middleware.Context) { |  | ||||||
| 	ctx.Data["IsRepoToolbarPulls"] = true |  | ||||||
| 	ctx.HTML(200, "repo/pulls") |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func Action(ctx *middleware.Context, params martini.Params) { |  | ||||||
| 	var err error |  | ||||||
| 	switch params["action"] { |  | ||||||
| 	case "watch": |  | ||||||
| 		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, true) |  | ||||||
| 	case "unwatch": |  | ||||||
| 		err = models.WatchRepo(ctx.User.Id, ctx.Repo.Repository.Id, false) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err != nil { |  | ||||||
| 		log.Error("repo.Action(%s): %v", params["action"], err) |  | ||||||
| 		ctx.JSON(200, map[string]interface{}{ |  | ||||||
| 			"ok":  false, |  | ||||||
| 			"err": err.Error(), |  | ||||||
| 		}) |  | ||||||
| 		return |  | ||||||
| 	} |  | ||||||
| 	ctx.JSON(200, map[string]interface{}{ |  | ||||||
| 		"ok": true, |  | ||||||
| 	}) |  | ||||||
| } |  | ||||||
| @@ -6,6 +6,7 @@ package user | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"net/url" | ||||||
| 	"strings" | 	"strings" | ||||||
|  |  | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
| @@ -77,7 +78,45 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||||||
| 	ctx.Data["Title"] = "Log In" | 	ctx.Data["Title"] = "Log In" | ||||||
|  |  | ||||||
| 	if ctx.Req.Method == "GET" { | 	if ctx.Req.Method == "GET" { | ||||||
| 		ctx.HTML(200, "user/signin") | 		// Check auto-login. | ||||||
|  | 		userName := ctx.GetCookie(base.CookieUserName) | ||||||
|  | 		if len(userName) == 0 { | ||||||
|  | 			ctx.HTML(200, "user/signin") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		isSucceed := false | ||||||
|  | 		defer func() { | ||||||
|  | 			if !isSucceed { | ||||||
|  | 				log.Trace("%s auto-login cookie cleared: %s", ctx.Req.RequestURI, userName) | ||||||
|  | 				ctx.SetCookie(base.CookieUserName, "", -1) | ||||||
|  | 				ctx.SetCookie(base.CookieRememberName, "", -1) | ||||||
|  | 			} | ||||||
|  | 		}() | ||||||
|  |  | ||||||
|  | 		user, err := models.GetUserByName(userName) | ||||||
|  | 		if err != nil { | ||||||
|  | 			ctx.HTML(200, "user/signin") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		secret := base.EncodeMd5(user.Rands + user.Passwd) | ||||||
|  | 		value, _ := ctx.GetSecureCookie(secret, base.CookieRememberName) | ||||||
|  | 		if value != user.Name { | ||||||
|  | 			ctx.HTML(200, "user/signin") | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		isSucceed = true | ||||||
|  | 		ctx.Session.Set("userId", user.Id) | ||||||
|  | 		ctx.Session.Set("userName", user.Name) | ||||||
|  | 		redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")) | ||||||
|  | 		if len(redirectTo) > 0 { | ||||||
|  | 			ctx.SetCookie("redirect_to", "", -1) | ||||||
|  | 			ctx.Redirect(redirectTo) | ||||||
|  | 		} else { | ||||||
|  | 			ctx.Redirect("/") | ||||||
|  | 		} | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -88,7 +127,8 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||||||
|  |  | ||||||
| 	user, err := models.LoginUserPlain(form.UserName, form.Password) | 	user, err := models.LoginUserPlain(form.UserName, form.Password) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if err.Error() == models.ErrUserNotExist.Error() { | 		if err == models.ErrUserNotExist { | ||||||
|  | 			log.Trace("%s Log in failed: %s/%s", ctx.Req.RequestURI, form.UserName, form.Password) | ||||||
| 			ctx.RenderWithErr("Username or password is not correct", "user/signin", &form) | 			ctx.RenderWithErr("Username or password is not correct", "user/signin", &form) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @@ -97,14 +137,29 @@ func SignIn(ctx *middleware.Context, form auth.LogInForm) { | |||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	if form.Remember == "on" { | ||||||
|  | 		secret := base.EncodeMd5(user.Rands + user.Passwd) | ||||||
|  | 		days := 86400 * base.LogInRememberDays | ||||||
|  | 		ctx.SetCookie(base.CookieUserName, user.Name, days) | ||||||
|  | 		ctx.SetSecureCookie(secret, base.CookieRememberName, user.Name, days) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	ctx.Session.Set("userId", user.Id) | 	ctx.Session.Set("userId", user.Id) | ||||||
| 	ctx.Session.Set("userName", user.Name) | 	ctx.Session.Set("userName", user.Name) | ||||||
| 	ctx.Redirect("/") | 	redirectTo, _ := url.QueryUnescape(ctx.GetCookie("redirect_to")) | ||||||
|  | 	if len(redirectTo) > 0 { | ||||||
|  | 		ctx.SetCookie("redirect_to", "", -1) | ||||||
|  | 		ctx.Redirect(redirectTo) | ||||||
|  | 	} else { | ||||||
|  | 		ctx.Redirect("/") | ||||||
|  | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
| func SignOut(ctx *middleware.Context) { | func SignOut(ctx *middleware.Context) { | ||||||
| 	ctx.Session.Delete("userId") | 	ctx.Session.Delete("userId") | ||||||
| 	ctx.Session.Delete("userName") | 	ctx.Session.Delete("userName") | ||||||
|  | 	ctx.SetCookie(base.CookieUserName, "", -1) | ||||||
|  | 	ctx.SetCookie(base.CookieRememberName, "", -1) | ||||||
| 	ctx.Redirect("/") | 	ctx.Redirect("/") | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -246,7 +301,7 @@ func Activate(ctx *middleware.Context) { | |||||||
| 	if len(code) == 0 { | 	if len(code) == 0 { | ||||||
| 		ctx.Data["IsActivatePage"] = true | 		ctx.Data["IsActivatePage"] = true | ||||||
| 		if ctx.User.IsActive { | 		if ctx.User.IsActive { | ||||||
| 			ctx.Error(404) | 			ctx.Handle(404, "user.Activate", nil) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		// Resend confirmation e-mail. | 		// Resend confirmation e-mail. | ||||||
| @@ -274,7 +329,7 @@ func Activate(ctx *middleware.Context) { | |||||||
|  |  | ||||||
| 		ctx.Session.Set("userId", user.Id) | 		ctx.Session.Set("userId", user.Id) | ||||||
| 		ctx.Session.Set("userName", user.Name) | 		ctx.Session.Set("userName", user.Name) | ||||||
| 		ctx.Redirect("/", 302) | 		ctx.Redirect("/") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										2
									
								
								serve.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								serve.go
									
									
									
									
									
								
							| @@ -106,7 +106,7 @@ func runServ(k *cli.Context) { | |||||||
|  |  | ||||||
| 	//os.Setenv("userName", user.Name) | 	//os.Setenv("userName", user.Name) | ||||||
| 	//os.Setenv("userId", strconv.Itoa(int(user.Id))) | 	//os.Setenv("userId", strconv.Itoa(int(user.Id))) | ||||||
| 	repo, err := models.GetRepositoryByName(user, repoName) | 	repo, err := models.GetRepositoryByName(user.Id, repoName) | ||||||
| 	var isExist bool = true | 	var isExist bool = true | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		if err == models.ErrRepoNotExist { | 		if err == models.ErrRepoNotExist { | ||||||
|   | |||||||
| @@ -45,6 +45,7 @@ | |||||||
|                 <div><b>Register Email Confirmation:</b> <i class="fa fa{{if .Service.RegisterEmailConfirm}}-check{{end}}-square-o"></i></div> |                 <div><b>Register Email Confirmation:</b> <i class="fa fa{{if .Service.RegisterEmailConfirm}}-check{{end}}-square-o"></i></div> | ||||||
|                 <div><b>Disenable Registeration:</b> <i class="fa fa{{if .Service.DisenableRegisteration}}-check{{end}}-square-o"></i></div> |                 <div><b>Disenable Registeration:</b> <i class="fa fa{{if .Service.DisenableRegisteration}}-check{{end}}-square-o"></i></div> | ||||||
|                 <div><b>Require Sign In View:</b> <i class="fa fa{{if .Service.RequireSignInView}}-check{{end}}-square-o"></i></div> |                 <div><b>Require Sign In View:</b> <i class="fa fa{{if .Service.RequireSignInView}}-check{{end}}-square-o"></i></div> | ||||||
|  |                 <div><b>Enable Cache Avatar:</b> <i class="fa fa{{if .Service.EnableCacheAvatar}}-check{{end}}-square-o"></i></div> | ||||||
|                 <hr/> |                 <hr/> | ||||||
|                 <div><b>Active Code Lives:</b> {{.Service.ActiveCodeLives}} minutes</div> |                 <div><b>Active Code Lives:</b> {{.Service.ActiveCodeLives}} minutes</div> | ||||||
|                 <div><b>Reset Password Code Lives:</b> {{.Service.ResetPwdCodeLives}} minutes</div> |                 <div><b>Reset Password Code Lives:</b> {{.Service.ResetPwdCodeLives}} minutes</div> | ||||||
| @@ -76,6 +77,36 @@ | |||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  |         <div class="panel panel-default"> | ||||||
|  |             <div class="panel-heading"> | ||||||
|  |                 Session Configuration | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div class="panel-body"> | ||||||
|  |                 <div><b>Session Provider:</b> {{.SessionProvider}}</div> | ||||||
|  |                 <div><b>Cookie Name:</b> {{.SessionConfig.CookieName}}</div> | ||||||
|  |                 <div><b>Enable Set Cookie:</b> <i class="fa fa{{if .SessionConfig.EnableSetCookie}}-check{{end}}-square-o"></i></div> | ||||||
|  |                 <div><b>GC Interval Time:</b> {{.SessionConfig.GcIntervalTime}} seconds</div> | ||||||
|  |                 <div><b>Session Life Time:</b> {{.SessionConfig.SessionLifeTime}} seconds</div> | ||||||
|  |                 <div><b>HTTPS Only:</b> <i class="fa fa{{if .SessionConfig.CookieSecure}}-check{{end}}-square-o"></i></div> | ||||||
|  |                 <div><b>Cookie Life Time:</b> {{.SessionConfig.CookieLifeTime}} seconds</div> | ||||||
|  |                 <div><b>Session ID Hash Function:</b> {{.SessionConfig.SessionIDHashFunc}}</div> | ||||||
|  |                 <div><b>Session ID Hash Key:</b> {{.SessionConfig.SessionIDHashKey}}</div> | ||||||
|  |                 <div><b>Provider Config:</b> {{.SessionConfig.ProviderConfig}}</div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|  |         <div class="panel panel-default"> | ||||||
|  |             <div class="panel-heading"> | ||||||
|  |                 Picture Configuration | ||||||
|  |             </div> | ||||||
|  |  | ||||||
|  |             <div class="panel-body"> | ||||||
|  |                 <div><b>Picture Service:</b> {{.PictureService}}</div> | ||||||
|  |                 <div><b>Picture Root Path:</b> {{.PictureRootPath}}</div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|         <div class="panel panel-default"> |         <div class="panel panel-default"> | ||||||
|             <div class="panel-heading"> |             <div class="panel-heading"> | ||||||
|                 Log Configuration |                 Log Configuration | ||||||
|   | |||||||
| @@ -15,10 +15,42 @@ | |||||||
|  |  | ||||||
|         <div class="panel panel-default"> |         <div class="panel panel-default"> | ||||||
|             <div class="panel-heading"> |             <div class="panel-heading"> | ||||||
|                 System Status |                 System Monitor Status | ||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div class="panel-body"> |             <div class="panel-body"> | ||||||
|  |                 <div>Server Uptime: <b>{{.SysStatus.Uptime}}</b></div> | ||||||
|  |                 <div>Current Goroutines: <b>{{.SysStatus.NumGoroutine}}</b></div> | ||||||
|  |                 <hr/> | ||||||
|  |                 <div>Current Memory Usage: <b>{{.SysStatus.MemAllocated}}</b></div> | ||||||
|  |                 <div>Total Memory Allocated: <b>{{.SysStatus.MemTotal}}</b></div> | ||||||
|  |                 <div>Memory Obtained: <b>{{.SysStatus.MemSys}}</b></div> | ||||||
|  |                 <div>Pointer Lookup Times: <b>{{.SysStatus.Lookups}}</b></div> | ||||||
|  |                 <div>Memory Allocate Times: <b>{{.SysStatus.MemMallocs}}</b></div> | ||||||
|  |                 <div>Memory Free Times: <b>{{.SysStatus.MemFrees}}</b></div> | ||||||
|  |                 <hr/> | ||||||
|  |                 <div>Current Heap Usage: <b>{{.SysStatus.HeapAlloc}}</b></div> | ||||||
|  |                 <div>Heap Memory Obtained: <b>{{.SysStatus.HeapSys}}</b></div> | ||||||
|  |                 <div>Heap Memory Idle: <b>{{.SysStatus.HeapIdle}}</b></div> | ||||||
|  |                 <div>Heap Memory In Use: <b>{{.SysStatus.HeapInuse}}</b></div> | ||||||
|  |                 <div>Heap Memory Released: <b>{{.SysStatus.HeapReleased}}</b></div> | ||||||
|  |                 <div>Heap Objects: <b>{{.SysStatus.HeapObjects}}</b></div> | ||||||
|  |                 <hr/> | ||||||
|  |                 <div>Bootstrap Stack Usage: <b>{{.SysStatus.StackInuse}}</b></div> | ||||||
|  |                 <div>Stack Memory Obtained: <b>{{.SysStatus.StackSys}}</b></div> | ||||||
|  |                 <div>MSpan Structures Usage: <b>{{.SysStatus.MSpanInuse}}</b></div> | ||||||
|  |                 <div>MSpan Structures Obtained: <b>{{.SysStatus.HeapSys}}</b></div> | ||||||
|  |                 <div>MCache Structures Usage: <b>{{.SysStatus.MCacheInuse}}</b></div> | ||||||
|  |                 <div>MCache Structures Obtained: <b>{{.SysStatus.MCacheSys}}</b></div> | ||||||
|  |                 <div>Profiling Bucket Hash Table Obtained: <b>{{.SysStatus.BuckHashSys}}</b></div> | ||||||
|  |                 <div>GC Metadada Obtained: <b>{{.SysStatus.GCSys}}</b></div> | ||||||
|  |                 <div>Other System Allocation Obtained: <b>{{.SysStatus.OtherSys}}</b></div> | ||||||
|  |                 <hr/> | ||||||
|  |                 <div>Next GC Recycle: <b>{{.SysStatus.NextGC}}</b></div> | ||||||
|  |                 <div>Last GC Time: <b>{{.SysStatus.LastGC}} ago</b></div> | ||||||
|  |                 <div>Total GC Pause: <b>{{.SysStatus.PauseTotalNs}}</b></div> | ||||||
|  |                 <div>Last GC Pause: <b>{{.SysStatus.PauseNs}}</b></div> | ||||||
|  |                 <div>GC Times: <b>{{.SysStatus.NumGC}}</b></div> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|     </div> |     </div> | ||||||
|   | |||||||
| @@ -27,7 +27,7 @@ | |||||||
|                             <td>{{.Id}}</td> |                             <td>{{.Id}}</td> | ||||||
|                             <th>{{.UserName}}</th> |                             <th>{{.UserName}}</th> | ||||||
|                             <td><a href="/{{.UserName}}/{{.Name}}">{{.Name}}</a></td> |                             <td><a href="/{{.UserName}}/{{.Name}}">{{.Name}}</a></td> | ||||||
|                             <td><i class="fa fa{{if .Private}}-check{{end}}-square-o"></i></td> |                             <td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td> | ||||||
|                             <td>{{.NumWatches}}</td> |                             <td>{{.NumWatches}}</td> | ||||||
|                             <td>{{.NumForks}}</td> |                             <td>{{.NumForks}}</td> | ||||||
|                             <td>{{DateFormat .Created "M d, Y"}}</td> |                             <td>{{DateFormat .Created "M d, Y"}}</td> | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ | |||||||
|             	<br/> |             	<br/> | ||||||
| 				<form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal"> | 				<form action="/admin/users/{{.User.Id}}" method="post" class="form-horizontal"> | ||||||
| 				    {{if .IsSuccess}}<p class="alert alert-success">Account profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | 				    {{if .IsSuccess}}<p class="alert alert-success">Account profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | ||||||
|  | 				    {{.CsrfTokenHtml}} | ||||||
|                 	<input type="hidden" value="{{.User.Id}}" name="userId"/> |                 	<input type="hidden" value="{{.User.Id}}" name="userId"/> | ||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 						<label class="col-md-3 control-label">Username: </label> | 						<label class="col-md-3 control-label">Username: </label> | ||||||
| @@ -71,7 +72,7 @@ | |||||||
| 					<div class="form-group"> | 					<div class="form-group"> | ||||||
| 					    <div class="col-md-offset-3 col-md-6"> | 					    <div class="col-md-offset-3 col-md-6"> | ||||||
| 					    	<button type="submit" class="btn btn-lg btn-primary btn-block">Update account profile</button> | 					    	<button type="submit" class="btn btn-lg btn-primary btn-block">Update account profile</button> | ||||||
| 					    	<!-- <a type="button" href="/admin/users/{{.User.Id}}/delete" class="btn btn-lg btn-danger btn-block">Delete this account</a> --> | 					    	<a type="button" href="/admin/users/{{.User.Id}}/delete" class="btn btn-lg btn-danger btn-block">Delete this account</a> | ||||||
| 					    </div> | 					    </div> | ||||||
| 					</div> | 					</div> | ||||||
| 				</form> | 				</form> | ||||||
|   | |||||||
| @@ -11,6 +11,7 @@ | |||||||
|             <div class="panel-body"> |             <div class="panel-body"> | ||||||
|             	<br/> |             	<br/> | ||||||
| 				<form action="/admin/users/new" method="post" class="form-horizontal"> | 				<form action="/admin/users/new" method="post" class="form-horizontal"> | ||||||
|  | 					{{.CsrfTokenHtml}} | ||||||
| 				    <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | 				    <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | ||||||
| 					<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | 					<div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | ||||||
| 						<label class="col-md-3 control-label">Username: </label> | 						<label class="col-md-3 control-label">Username: </label> | ||||||
|   | |||||||
| @@ -8,6 +8,7 @@ | |||||||
|         <meta name="author" content="Gogs - Go Git Service" /> |         <meta name="author" content="Gogs - Go Git Service" /> | ||||||
| 		<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}}" /> | ||||||
|  |  | ||||||
| 		 <!-- Stylesheets --> | 		 <!-- Stylesheets --> | ||||||
| 		<link href="/css/bootstrap.min.css" rel="stylesheet" /> | 		<link href="/css/bootstrap.min.css" rel="stylesheet" /> | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||||
| <div class="container" id="gogs-body"> | <div class="container" id="gogs-body"> | ||||||
|     <form action="/repo/create" method="post" class="form-horizontal gogs-card" id="gogs-repo-create"> |     <form action="/repo/create" method="post" class="form-horizontal gogs-card" id="gogs-repo-create"> | ||||||
|  |         {{.CsrfTokenHtml}} | ||||||
|         <h3>Create New Repository</h3> |         <h3>Create New Repository</h3> | ||||||
|         <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> |         <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | ||||||
|         <div class="form-group"> |         <div class="form-group"> | ||||||
|   | |||||||
| @@ -1,11 +1,11 @@ | |||||||
| <div id="gogs-body-nav" class="gogs-repo-nav"> | <div id="gogs-body-nav" class="gogs-repo-nav"> | ||||||
|     <div class="container"> |     <div class="container"> | ||||||
|         <div class="row"> |         <div class="row"> | ||||||
|             <div class="col-md-6"> |             <div class="col-md-7"> | ||||||
|                 <h3 class="name"><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3> |                 <h3 class="name"><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3> | ||||||
|                 <p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> |                 <p class="desc">{{.Repository.Description}}{{if .Repository.Website}}<a href="{{.Repository.Website}}">{{.Repository.Website}}</a>{{end}}</p> | ||||||
|             </div> |             </div> | ||||||
|             <div class="col-md-6 actions text-right clone-group-btn"> |             <div class="col-md-5 actions text-right clone-group-btn"> | ||||||
|                 {{if not .IsBareRepo}} |                 {{if not .IsBareRepo}} | ||||||
|                 <!--<div class="btn-group" id="gogs-repo-clone"> |                 <!--<div class="btn-group" id="gogs-repo-clone"> | ||||||
|                     <button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button> |                     <button type="button" class="btn btn-default"><i class="fa fa-download fa-lg fa-m"></i></button> | ||||||
|   | |||||||
| @@ -19,7 +19,41 @@ | |||||||
|             </div> |             </div> | ||||||
|  |  | ||||||
|             <div class="panel-body"> |             <div class="panel-body"> | ||||||
|                  |                 <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="form-horizontal"> | ||||||
|  |                     {{.CsrfTokenHtml}} | ||||||
|  |                     <input type="hidden" name="action" value="update"> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="col-md-3 text-right">Repository Name <strong class="text-danger">*</strong></label> | ||||||
|  |                         <div class="col-md-9"> | ||||||
|  |                             <input type="text" class="form-control" name="repo-name" required="required" value="{{.Repository.Name}}"/> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="col-md-3 text-right">Description</label> | ||||||
|  |                         <div class="col-md-9"> | ||||||
|  |                             <textarea class="form-control" name="desc" id="repo-desc" rows="6"></textarea> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="col-md-3 text-right">Official Site</label> | ||||||
|  |                         <div class="col-md-9"> | ||||||
|  |                             <input type="url" class="form-control" name="repo-site"/> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <label class="col-md-3 text-right">Default Branch</label> | ||||||
|  |                         <div class="col-md-9"> | ||||||
|  |                             <select name="branch" id="repo-default-branch" class="form-control"> | ||||||
|  |                                 <option value="">Branch</option> | ||||||
|  |                             </select> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                     <div class="form-group"> | ||||||
|  |                         <div class="col-md-9 col-md-offset-3"> | ||||||
|  |                             <button class="btn btn-primary" type="submit">Save Options</button> | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  |                 </form> | ||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
| @@ -40,6 +74,7 @@ | |||||||
|                 <div class="modal fade" id="delete-repository-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> |                 <div class="modal fade" id="delete-repository-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | ||||||
|                     <div class="modal-dialog"> |                     <div class="modal-dialog"> | ||||||
|                         <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="modal-content"> |                         <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="modal-content"> | ||||||
|  |                             {{.CsrfTokenHtml}} | ||||||
|                             <input type="hidden" name="action" value="delete"> |                             <input type="hidden" name="action" value="delete"> | ||||||
|  |  | ||||||
|                             <div class="modal-header"> |                             <div class="modal-header"> | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								templates/status/404.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/status/404.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | {{template "base/head" .}} | ||||||
|  | {{template "base/navbar" .}} | ||||||
|  | <div id="gogs-body" class="container"> | ||||||
|  |     <h4>This page is not found !</h4> | ||||||
|  |     <p>Application Version: {{AppVer}}</p> | ||||||
|  | </div> | ||||||
|  | {{template "base/footer" .}} | ||||||
							
								
								
									
										7
									
								
								templates/status/500.tmpl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								templates/status/500.tmpl
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | |||||||
|  | {{template "base/head" .}} | ||||||
|  | {{template "base/navbar" .}} | ||||||
|  | <div id="gogs-body" class="container"> | ||||||
|  |     <p>An error is occurred : {{.ErrorMsg}}</p> | ||||||
|  |     <p>Application Version: {{AppVer}}</p> | ||||||
|  | </div> | ||||||
|  | {{template "base/footer" .}} | ||||||
| @@ -1,7 +1,8 @@ | |||||||
| {{template "base/head" .}} | {{template "base/head" .}} | ||||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||||
| <div id="gogs-body" class="container"> | <div id="gogs-body" class="container"> | ||||||
|     <form action="/user/activate" method="get" class="form-horizontal gogs-card" id="gogs-login-card"> |     <form action="/user/activate" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | ||||||
|  |         {{.CsrfTokenHtml}} | ||||||
|         <h3>Activate Your Account</h3> |         <h3>Activate Your Account</h3> | ||||||
|         {{if .IsActivatePage}} |         {{if .IsActivatePage}} | ||||||
|             {{if .ServiceNotEnabled}} |             {{if .ServiceNotEnabled}} | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ | |||||||
|     <div class="modal fade" id="delete-account-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> |     <div class="modal fade" id="delete-account-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | ||||||
|         <div class="modal-dialog"> |         <div class="modal-dialog"> | ||||||
|             <form action="/user/delete" method="post" class="modal-content" id="gogs-user-delete"> |             <form action="/user/delete" method="post" class="modal-content" id="gogs-user-delete"> | ||||||
|  |                 {{.CsrfTokenHtml}} | ||||||
|                 <div class="modal-header"> |                 <div class="modal-header"> | ||||||
|                     <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |                     <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | ||||||
|                     <h4 class="modal-title" id="myModalLabel">Delete Account</h4> |                     <h4 class="modal-title" id="myModalLabel">Delete Account</h4> | ||||||
|   | |||||||
| @@ -5,7 +5,9 @@ | |||||||
|     <div id="gogs-user-setting-container" class="col-md-9"> |     <div id="gogs-user-setting-container" class="col-md-9"> | ||||||
|         <div id="gogs-setting-pwd"> |         <div id="gogs-setting-pwd"> | ||||||
|             <h4>Password</h4> |             <h4>Password</h4> | ||||||
|             <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password">{{if .IsSuccess}} |             <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting/password"> | ||||||
|  |             {{.CsrfTokenHtml}} | ||||||
|  |             {{if .IsSuccess}} | ||||||
|                 <p class="alert alert-success">Password is changed successfully. You can now sign in via new password.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} |                 <p class="alert alert-success">Password is changed successfully. You can now sign in via new password.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | ||||||
|                 <div class="form-group"> |                 <div class="form-group"> | ||||||
|                     <label class="col-md-3 control-label">Old Password<strong class="text-danger">*</strong></label> |                     <label class="col-md-3 control-label">Old Password<strong class="text-danger">*</strong></label> | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ | |||||||
|             <div class="modal fade" id="ssh-add-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> |             <div class="modal fade" id="ssh-add-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> | ||||||
|                 <div class="modal-dialog"> |                 <div class="modal-dialog"> | ||||||
|                     <form class="modal-content form-horizontal" id="gogs-ssh-form" method="post" action="/user/setting/ssh/"> |                     <form class="modal-content form-horizontal" id="gogs-ssh-form" method="post" action="/user/setting/ssh/"> | ||||||
|  |                         {{.CsrfTokenHtml}} | ||||||
|                         <div class="modal-header"> |                         <div class="modal-header"> | ||||||
|                             <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> |                             <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | ||||||
|                             <h4 class="modal-title" id="myModalLabel">Add SSH Key</h4> |                             <h4 class="modal-title" id="myModalLabel">Add SSH Key</h4> | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
|         <div id="gogs-setting-pwd"> |         <div id="gogs-setting-pwd"> | ||||||
|             <h4>Account Profile</h4> |             <h4>Account Profile</h4> | ||||||
|             <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting"> |             <form class="form-horizontal" id="gogs-password-form" method="post" action="/user/setting"> | ||||||
|  |                 {{.CsrfTokenHtml}} | ||||||
|                 {{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} |                 {{if .IsSuccess}}<p class="alert alert-success">Your profile has been successfully updated.</p>{{else if .HasError}}<p class="alert alert-danger form-error">{{.ErrorMsg}}</p>{{end}} | ||||||
|                 <p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p> |                 <p>Your Email will be public and used for Account related notifications and any web based operations made via the web.</p> | ||||||
|                 <div class="form-group"> |                 <div class="form-group"> | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||||
| <div class="container" id="gogs-body" data-page="user-signin"> | <div class="container" id="gogs-body" data-page="user-signin"> | ||||||
|     <form action="/user/login" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> |     <form action="/user/login" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | ||||||
|  |         {{.CsrfTokenHtml}} | ||||||
|         <h3>Log in</h3> |         <h3>Log in</h3> | ||||||
|         <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> |         <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div> | ||||||
|         <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> |         <div class="form-group {{if .Err_UserName}}has-error has-feedback{{end}}"> | ||||||
| @@ -18,6 +19,17 @@ | |||||||
|             </div> |             </div> | ||||||
|         </div> |         </div> | ||||||
|  |  | ||||||
|  |         <div class="form-group"> | ||||||
|  |             <div class="col-md-6 col-md-offset-4"> | ||||||
|  |                 <div class="checkbox"> | ||||||
|  |                     <label> | ||||||
|  |                         <input type="checkbox" name="remember" {{if .remember}}checked{{end}}> | ||||||
|  |                         <strong>Remember me</strong> | ||||||
|  |                     </label> | ||||||
|  |                 </div> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |  | ||||||
|         <div class="form-group"> |         <div class="form-group"> | ||||||
|             <div class="col-md-offset-4 col-md-6"> |             <div class="col-md-offset-4 col-md-6"> | ||||||
|                 <button type="submit" class="btn btn-lg btn-primary">Log In</button> |                 <button type="submit" class="btn btn-lg btn-primary">Log In</button> | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ | |||||||
| {{template "base/navbar" .}} | {{template "base/navbar" .}} | ||||||
| <div class="container" id="gogs-body" data-page="user-signup"> | <div class="container" id="gogs-body" data-page="user-signup"> | ||||||
| 	<form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | 	<form action="/user/sign_up" method="post" class="form-horizontal gogs-card" id="gogs-login-card"> | ||||||
|  | 		{{.CsrfTokenHtml}} | ||||||
| 		{{if .DisenableRegisteration}} | 		{{if .DisenableRegisteration}} | ||||||
| 		Sorry, registeration has been disenabled, you can only get account from administrator. | 		Sorry, registeration has been disenabled, you can only get account from administrator. | ||||||
| 		{{else}} | 		{{else}} | ||||||
|   | |||||||
							
								
								
									
										107
									
								
								web.go
									
									
									
									
									
								
							
							
						
						
									
										107
									
								
								web.go
									
									
									
									
									
								
							| @@ -12,7 +12,6 @@ import ( | |||||||
|  |  | ||||||
| 	"github.com/codegangsta/cli" | 	"github.com/codegangsta/cli" | ||||||
| 	"github.com/codegangsta/martini" | 	"github.com/codegangsta/martini" | ||||||
| 	"github.com/martini-contrib/sessions" |  | ||||||
|  |  | ||||||
| 	"github.com/gogits/binding" | 	"github.com/gogits/binding" | ||||||
|  |  | ||||||
| @@ -81,70 +80,92 @@ func runWeb(*cli.Context) { | |||||||
| 	// Middlewares. | 	// Middlewares. | ||||||
| 	m.Use(middleware.Renderer(middleware.RenderOptions{Funcs: []template.FuncMap{base.TemplateFuncs}})) | 	m.Use(middleware.Renderer(middleware.RenderOptions{Funcs: []template.FuncMap{base.TemplateFuncs}})) | ||||||
|  |  | ||||||
| 	// TODO: should use other store because cookie store is not secure. |  | ||||||
| 	store := sessions.NewCookieStore([]byte("secret123")) |  | ||||||
| 	m.Use(sessions.Sessions("my_session", store)) |  | ||||||
|  |  | ||||||
| 	m.Use(middleware.InitContext()) | 	m.Use(middleware.InitContext()) | ||||||
|  |  | ||||||
| 	reqSignIn := middleware.SignInRequire(true) | 	reqSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true}) | ||||||
| 	ignSignIn := middleware.SignInRequire(base.Service.RequireSignInView) | 	ignSignIn := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: base.Service.RequireSignInView}) | ||||||
| 	reqSignOut := middleware.SignOutRequire() | 	reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true}) | ||||||
|  |  | ||||||
| 	// Routers. | 	// Routers. | ||||||
| 	m.Get("/", ignSignIn, routers.Home) | 	m.Get("/", ignSignIn, routers.Home) | ||||||
| 	m.Get("/issues", reqSignIn, user.Issues) | 	m.Get("/issues", reqSignIn, user.Issues) | ||||||
| 	m.Get("/pulls", reqSignIn, user.Pulls) | 	m.Get("/pulls", reqSignIn, user.Pulls) | ||||||
| 	m.Get("/stars", reqSignIn, user.Stars) | 	m.Get("/stars", reqSignIn, user.Stars) | ||||||
| 	m.Any("/user/login", reqSignOut, binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | 	m.Get("/help", routers.Help) | ||||||
| 	m.Any("/user/logout", reqSignIn, user.SignOut) |  | ||||||
| 	m.Any("/user/sign_up", reqSignOut, binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) |  | ||||||
| 	m.Any("/user/delete", reqSignIn, user.Delete) |  | ||||||
| 	m.Get("/user/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) |  | ||||||
| 	m.Get("/user/activate", user.Activate) |  | ||||||
|  |  | ||||||
| 	m.Any("/user/setting", reqSignIn, binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | 	m.Group("/user", func(r martini.Router) { | ||||||
| 	m.Any("/user/setting/password", reqSignIn, binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | 		r.Any("/login", binding.BindIgnErr(auth.LogInForm{}), user.SignIn) | ||||||
| 	m.Any("/user/setting/ssh", reqSignIn, binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | 		r.Any("/sign_up", binding.BindIgnErr(auth.RegisterForm{}), user.SignUp) | ||||||
| 	m.Any("/user/setting/notification", reqSignIn, user.SettingNotification) | 	}, reqSignOut) | ||||||
| 	m.Any("/user/setting/security", reqSignIn, user.SettingSecurity) | 	m.Group("/user", func(r martini.Router) { | ||||||
|  | 		r.Any("/logout", user.SignOut) | ||||||
|  | 		r.Any("/delete", user.Delete) | ||||||
|  | 		r.Any("/setting", binding.BindIgnErr(auth.UpdateProfileForm{}), user.Setting) | ||||||
|  | 	}, reqSignIn) | ||||||
|  | 	m.Group("/user", func(r martini.Router) { | ||||||
|  | 		r.Get("/feeds", binding.Bind(auth.FeedsForm{}), user.Feeds) | ||||||
|  | 		r.Get("/activate", user.Activate) | ||||||
|  | 	}) | ||||||
|  |  | ||||||
|  | 	m.Group("/user/setting", func(r martini.Router) { | ||||||
|  | 		r.Any("/password", binding.BindIgnErr(auth.UpdatePasswdForm{}), user.SettingPassword) | ||||||
|  | 		r.Any("/ssh", binding.BindIgnErr(auth.AddSSHKeyForm{}), user.SettingSSHKeys) | ||||||
|  | 		r.Any("/notification", user.SettingNotification) | ||||||
|  | 		r.Any("/security", user.SettingSecurity) | ||||||
|  | 	}, reqSignIn) | ||||||
|  |  | ||||||
| 	m.Get("/user/:username", ignSignIn, user.Profile) | 	m.Get("/user/:username", ignSignIn, user.Profile) | ||||||
|  |  | ||||||
| 	m.Any("/repo/create", reqSignIn, binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | 	m.Any("/repo/create", reqSignIn, binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create) | ||||||
|  |  | ||||||
| 	m.Get("/help", routers.Help) | 	adminReq := middleware.Toggle(&middleware.ToggleOptions{SignInRequire: true, AdminRequire: true}) | ||||||
|  |  | ||||||
| 	adminReq := middleware.AdminRequire() | 	m.Get("/admin", adminReq, admin.Dashboard) | ||||||
| 	m.Get("/admin", reqSignIn, adminReq, admin.Dashboard) | 	m.Group("/admin", func(r martini.Router) { | ||||||
| 	m.Get("/admin/users", reqSignIn, adminReq, admin.Users) | 		r.Get("/users", admin.Users) | ||||||
| 	m.Any("/admin/users/new", reqSignIn, adminReq, binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) | 		r.Get("/repos", admin.Repositories) | ||||||
| 	m.Any("/admin/users/:userid", reqSignIn, adminReq, binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser) | 		r.Get("/config", admin.Config) | ||||||
| 	m.Get("/admin/repos", reqSignIn, adminReq, admin.Repositories) | 	}, adminReq) | ||||||
| 	m.Get("/admin/config", reqSignIn, adminReq, admin.Config) | 	m.Group("/admin/users", func(r martini.Router) { | ||||||
|  | 		r.Any("/new", binding.BindIgnErr(auth.RegisterForm{}), admin.NewUser) | ||||||
|  | 		r.Any("/:userid", binding.BindIgnErr(auth.AdminEditUserForm{}), admin.EditUser) | ||||||
|  | 		r.Any("/:userid/delete", admin.DeleteUser) | ||||||
|  | 	}, adminReq) | ||||||
|  |  | ||||||
| 	m.Post("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.SettingPost) | 	m.Group("/:username/:reponame", func(r martini.Router) { | ||||||
| 	m.Get("/:username/:reponame/settings", reqSignIn, middleware.RepoAssignment(true), repo.Setting) | 		r.Post("/settings", repo.SettingPost) | ||||||
|  | 		r.Get("/settings", repo.Setting) | ||||||
|  | 		r.Get("/action/:action", repo.Action) | ||||||
|  | 	}, reqSignIn, middleware.RepoAssignment(true)) | ||||||
|  | 	m.Group("/:username/:reponame", func(r martini.Router) { | ||||||
|  | 		r.Get("/commits/:branchname", repo.Commits) | ||||||
|  | 		r.Get("/issues", repo.Issues) | ||||||
|  | 		r.Any("/issues/new", binding.BindIgnErr(auth.CreateIssueForm{}), repo.CreateIssue) | ||||||
|  | 		r.Get("/issues/:issueid", repo.ViewIssue) | ||||||
|  | 		r.Get("/pulls", repo.Pulls) | ||||||
|  | 		r.Get("/branches", repo.Branches) | ||||||
|  | 		r.Get("/src/:branchname", repo.Single) | ||||||
|  | 		r.Get("/src/:branchname/**", repo.Single) | ||||||
|  | 		r.Get("/commits/:branchname", repo.Commits) | ||||||
|  | 		r.Get("/commits/:branchname", repo.Commits) | ||||||
|  | 	}, ignSignIn, middleware.RepoAssignment(true)) | ||||||
|  |  | ||||||
| 	m.Get("/:username/:reponame/commits/:branchname", ignSignIn, middleware.RepoAssignment(true), repo.Commits) | 	// TODO: implement single commit page | ||||||
| 	m.Get("/:username/:reponame/issues", ignSignIn, middleware.RepoAssignment(true), repo.Issues) | 	// m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) | ||||||
| 	m.Get("/:username/:reponame/pulls", ignSignIn, middleware.RepoAssignment(true), repo.Pulls) | 	// m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) | ||||||
| 	m.Get("/:username/:reponame/branches", ignSignIn, middleware.RepoAssignment(true), repo.Branches) |  | ||||||
| 	m.Get("/:username/:reponame/action/:action", reqSignIn, middleware.RepoAssignment(true), repo.Action) |  | ||||||
| 	m.Get("/:username/:reponame/src/:branchname/**", |  | ||||||
| 		ignSignIn, middleware.RepoAssignment(true), repo.Single) |  | ||||||
| 	m.Get("/:username/:reponame/src/:branchname", |  | ||||||
| 		ignSignIn, middleware.RepoAssignment(true), repo.Single) |  | ||||||
| 	m.Get("/:username/:reponame/commit/:commitid/**", ignSignIn, middleware.RepoAssignment(true), repo.Single) |  | ||||||
| 	m.Get("/:username/:reponame/commit/:commitid", ignSignIn, middleware.RepoAssignment(true), repo.Single) |  | ||||||
|  |  | ||||||
| 	m.Get("/:username/:reponame", ignSignIn, middleware.RepoAssignment(true), repo.Single) | 	m.Group("/:username", func(r martini.Router) { | ||||||
|  | 		r.Get("/:reponame", middleware.RepoAssignment(true), repo.Single) | ||||||
| 	m.Any("/:username/:reponame/**", ignSignIn, repo.Http) | 		r.Any("/:reponame/**", repo.Http) | ||||||
|  | 	}, ignSignIn) | ||||||
|  |  | ||||||
| 	if martini.Env == martini.Dev { | 	if martini.Env == martini.Dev { | ||||||
| 		m.Get("/template/**", dev.TemplatePreview) | 		m.Get("/template/**", dev.TemplatePreview) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// not found handler | ||||||
|  | 	m.NotFound(routers.NotFound) | ||||||
|  |  | ||||||
| 	listenAddr := fmt.Sprintf("%s:%s", | 	listenAddr := fmt.Sprintf("%s:%s", | ||||||
| 		base.Cfg.MustValue("server", "HTTP_ADDR"), | 		base.Cfg.MustValue("server", "HTTP_ADDR"), | ||||||
| 		base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | 		base.Cfg.MustValue("server", "HTTP_PORT", "3000")) | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user