Lunny Xiao 11 years ago
parent
commit
a30e72323d

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ Gogs(Go Git Service) is a GitHub-like clone in the Go Programming Language.
 
 Since we choose to use pure Go implmentation of Git manipulation, Gogs certainly supports **ALL platforms**  that Go supports, including Linux, Max OS X, and Windows with **ZERO** dependency.
 
-##### Current version: 0.0.8 Alpha
+##### Current version: 0.0.9 Alpha
 
 ## Purpose
 

+ 2 - 1
bee.json

@@ -12,7 +12,8 @@
 		"models": "",
 		"others": [
 			"modules",
-			"$GOPATH/src/github.com/gogits/binding"
+			"$GOPATH/src/github.com/gogits/binding",
+			"$GOPATH/src/github.com/gogits/git"
 		]
 	},
 	"cmd_args": [

+ 1 - 0
conf/app.ini

@@ -7,6 +7,7 @@ LANG_IGNS=Google Go|C|Python|Ruby
 LICENSES=Apache v2 License|GPL v2|MIT License|BSD (3-Clause) License
 
 [server]
+DOMAIN = gogits.org
 HTTP_ADDR = 
 HTTP_PORT = 3000
 

+ 1 - 1
gogs.go

@@ -20,7 +20,7 @@ import (
 // Test that go1.1 tag above is included in builds. main.go refers to this definition.
 const go11tag = true
 
-const APP_VER = "0.0.8.0316.1"
+const APP_VER = "0.0.9.0317.1"
 
 func init() {
 	base.AppVer = APP_VER

+ 4 - 0
models/action.go

@@ -44,6 +44,10 @@ func (a Action) GetRepoName() string {
 	return a.RepoName
 }
 
+func (a Action) GetContent() string {
+	return a.Content
+}
+
 // CommitRepoAction records action for commit repository.
 func CommitRepoAction(userId int64, userName string,
 	repoId int64, repoName string, commits [][]string) error {

+ 3 - 0
models/repo.go

@@ -307,6 +307,9 @@ func DeleteRepository(userId, repoId int64, userName string) (err error) {
 	}
 
 	session := orm.NewSession()
+	if err = session.Begin(); err != nil {
+		return err
+	}
 	if _, err = session.Delete(&Repository{Id: repoId}); err != nil {
 		session.Rollback()
 		return err

+ 75 - 4
models/repo2.go

@@ -5,11 +5,26 @@
 package models
 
 import (
+	"fmt"
 	"path"
 	"strings"
 	"time"
 
-	git "github.com/gogits/git"
+	"github.com/Unknwon/com"
+
+	"github.com/gogits/git"
+)
+
+type Commit struct {
+	Author  string
+	Email   string
+	Date    time.Time
+	SHA     string
+	Message string
+}
+
+var (
+	ErrRepoFileNotLoaded = fmt.Errorf("repo file not loaded")
 )
 
 type RepoFile struct {
@@ -18,6 +33,7 @@ type RepoFile struct {
 	Message    string
 	Created    time.Time
 	Size       int64
+	Repo       *git.Repository
 	LastCommit string
 }
 
@@ -43,10 +59,34 @@ func findTree(repo *git.Repository, tree *git.Tree, rpath string) *git.Tree {
 	return g
 }
 
-func GetReposFiles(userName, reposName, branchName, rpath string) ([]*RepoFile, error) {
-	f := RepoPath(userName, reposName)
+func (file *RepoFile) LookupBlob() (*git.Blob, error) {
+	if file.Repo == nil {
+		return nil, ErrRepoFileNotLoaded
+	}
+
+	return file.Repo.LookupBlob(file.Id)
+}
+
+func GetBranches(userName, reposName string) ([]string, error) {
+	repo, err := git.OpenRepository(RepoPath(userName, reposName))
+	if err != nil {
+		return nil, err
+	}
+
+	refs, err := repo.AllReferences()
+	if err != nil {
+		return nil, err
+	}
+
+	brs := make([]string, len(refs))
+	for i, ref := range refs {
+		brs[i] = ref.Name
+	}
+	return brs, nil
+}
 
-	repo, err := git.OpenRepository(f)
+func GetReposFiles(userName, reposName, branchName, rpath string) ([]*RepoFile, error) {
+	repo, err := git.OpenRepository(RepoPath(userName, reposName))
 	if err != nil {
 		return nil, err
 	}
@@ -128,6 +168,7 @@ func GetReposFiles(userName, reposName, branchName, rpath string) ([]*RepoFile,
 				cm.Message(),
 				cm.Committer.When,
 				size,
+				repo,
 				cm.Id().String(),
 			}
 
@@ -142,3 +183,33 @@ func GetReposFiles(userName, reposName, branchName, rpath string) ([]*RepoFile,
 
 	return append(repodirs, repofiles...), nil
 }
+
+func GetLastestCommit(userName, repoName string) (*Commit, error) {
+	stdout, _, err := com.ExecCmd("git", "--git-dir="+RepoPath(userName, repoName), "log", "-1")
+	if err != nil {
+		return nil, err
+	}
+
+	commit := new(Commit)
+	for _, line := range strings.Split(stdout, "\n") {
+		if len(line) == 0 {
+			continue
+		}
+		switch {
+		case line[0] == 'c':
+			commit.SHA = line[7:]
+		case line[0] == 'A':
+			infos := strings.SplitN(line, " ", 3)
+			commit.Author = infos[1]
+			commit.Email = infos[2][1 : len(infos[2])-1]
+		case line[0] == 'D':
+			commit.Date, err = time.Parse("Mon Jan 02 15:04:05 2006 -0700", line[8:])
+			if err != nil {
+				return nil, err
+			}
+		case line[:4] == "    ":
+			commit.Message = line[4:]
+		}
+	}
+	return commit, nil
+}

+ 0 - 7
modules/auth/repo.go

@@ -17,7 +17,6 @@ import (
 )
 
 type CreateRepoForm struct {
-	UserId      int64  `form:"userId"`
 	RepoName    string `form:"repo" binding:"Required;AlphaDash"`
 	Visibility  string `form:"visibility"`
 	Description string `form:"desc" binding:"MaxSize(100)"`
@@ -52,9 +51,3 @@ func (f *CreateRepoForm) Validate(errors *binding.Errors, req *http.Request, con
 
 	validate(errors, data, f)
 }
-
-type DeleteRepoForm struct {
-	UserId   int64  `form:"userId" binding:"Required"`
-	UserName string `form:"userName" binding:"Required"`
-	RepoId   int64  `form:"repoId" binding:"Required"`
-}

+ 2 - 0
modules/base/conf.go

@@ -18,6 +18,7 @@ import (
 var (
 	AppVer  string
 	AppName string
+	Domain  string
 	Cfg     *goconfig.ConfigFile
 )
 
@@ -58,4 +59,5 @@ func init() {
 	Cfg.BlockMode = false
 
 	AppName = Cfg.MustValue("", "APP_NAME")
+	Domain = Cfg.MustValue("server", "DOMAIN")
 }

+ 39 - 0
modules/base/markdown.go

@@ -0,0 +1,39 @@
+// 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 base
+
+import (
+	"github.com/slene/blackfriday"
+)
+
+func RenderMarkdown(rawBytes []byte) []byte {
+	htmlFlags := 0
+	htmlFlags |= blackfriday.HTML_USE_XHTML
+	// htmlFlags |= blackfriday.HTML_USE_SMARTYPANTS
+	// htmlFlags |= blackfriday.HTML_SMARTYPANTS_FRACTIONS
+	// htmlFlags |= blackfriday.HTML_SMARTYPANTS_LATEX_DASHES
+	htmlFlags |= blackfriday.HTML_SKIP_HTML
+	htmlFlags |= blackfriday.HTML_SKIP_STYLE
+	htmlFlags |= blackfriday.HTML_SKIP_SCRIPT
+	htmlFlags |= blackfriday.HTML_GITHUB_BLOCKCODE
+	htmlFlags |= blackfriday.HTML_OMIT_CONTENTS
+	htmlFlags |= blackfriday.HTML_COMPLETE_PAGE
+	renderer := blackfriday.HtmlRenderer(htmlFlags, "", "")
+
+	// set up the parser
+	extensions := 0
+	extensions |= blackfriday.EXTENSION_NO_INTRA_EMPHASIS
+	extensions |= blackfriday.EXTENSION_TABLES
+	extensions |= blackfriday.EXTENSION_FENCED_CODE
+	extensions |= blackfriday.EXTENSION_AUTOLINK
+	extensions |= blackfriday.EXTENSION_STRIKETHROUGH
+	extensions |= blackfriday.EXTENSION_HARD_LINE_BREAK
+	extensions |= blackfriday.EXTENSION_SPACE_HEADERS
+	extensions |= blackfriday.EXTENSION_NO_EMPTY_LINE_BEFORE_BLOCK
+
+	body := blackfriday.Markdown(rawBytes, renderer, extensions)
+
+	return body
+}

+ 4 - 0
modules/base/template.go

@@ -19,6 +19,10 @@ var TemplateFuncs template.FuncMap = map[string]interface{}{
 	"AppVer": func() string {
 		return AppVer
 	},
+	"AppDomain": func() string {
+		return Domain
+	},
+	"AvatarLink": AvatarLink,
 	"str2html":   Str2html,
 	"TimeSince":  TimeSince,
 	"FileSize":   FileSize,

+ 27 - 3
modules/base/tool.go

@@ -5,8 +5,10 @@
 package base
 
 import (
+	"bytes"
 	"crypto/md5"
 	"encoding/hex"
+	"encoding/json"
 	"fmt"
 	"math"
 	"strings"
@@ -20,6 +22,11 @@ func EncodeMd5(str string) string {
 	return hex.EncodeToString(m.Sum(nil))
 }
 
+// AvatarLink returns avatar link by given e-mail.
+func AvatarLink(email string) string {
+	return "http://1.gravatar.com/avatar/" + EncodeMd5(email)
+}
+
 // Seconds-based time units
 const (
 	Minute = 60
@@ -235,6 +242,7 @@ type Actioner interface {
 	GetOpType() int
 	GetActUserName() string
 	GetRepoName() string
+	GetContent() string
 }
 
 // ActionIcon accepts a int that represents action operation type
@@ -243,23 +251,39 @@ func ActionIcon(opType int) string {
 	switch opType {
 	case 1: // Create repository.
 		return "plus-circle"
+	case 5: // Commit repository.
+		return "arrow-circle-o-right"
 	default:
 		return "invalid type"
 	}
 }
 
 const (
-	CreateRepoTpl = `<a href="/user/%s">%s</a> created repository <a href="/%s/%s">%s</a>`
+	TPL_CREATE_REPO    = `<a href="/user/%s">%s</a> created repository <a href="/%s/%s">%s</a>`
+	TPL_COMMIT_REPO    = `<a href="/user/%s">%s</a> pushed to <a href="/%s/%s/tree/%s">%s</a> at <a href="/%s/%s">%s/%s</a>%s`
+	TPL_COMMIT_REPO_LI = `<div><img id="gogs-user-avatar-commit" src="%s?s=16" alt="user-avatar" title="username"/> <a href="/%s/%s/commit/%s">%s</a> %s</div>`
 )
 
 // ActionDesc accepts int that represents action operation type
 // and returns the description.
-func ActionDesc(act Actioner) string {
+func ActionDesc(act Actioner, avatarLink string) string {
 	actUserName := act.GetActUserName()
 	repoName := act.GetRepoName()
+	content := act.GetContent()
 	switch act.GetOpType() {
 	case 1: // Create repository.
-		return fmt.Sprintf(CreateRepoTpl, actUserName, actUserName, actUserName, repoName, repoName)
+		return fmt.Sprintf(TPL_CREATE_REPO, actUserName, actUserName, actUserName, repoName, repoName)
+	case 5: // Commit repository.
+		var commits [][]string
+		if err := json.Unmarshal([]byte(content), &commits); err != nil {
+			return err.Error()
+		}
+		buf := bytes.NewBuffer([]byte("\n"))
+		for _, commit := range commits {
+			buf.WriteString(fmt.Sprintf(TPL_COMMIT_REPO_LI, avatarLink, actUserName, repoName, commit[0], commit[0][:7], commit[1]) + "\n")
+		}
+		return fmt.Sprintf(TPL_COMMIT_REPO, actUserName, actUserName, actUserName, repoName, "master", "master", actUserName, repoName, actUserName, repoName,
+			buf.String())
 	default:
 		return "invalid type"
 	}

+ 3 - 2
modules/middleware/repo.go

@@ -6,6 +6,7 @@ package middleware
 
 import (
 	"errors"
+	"strings"
 
 	"github.com/codegangsta/martini"
 
@@ -23,8 +24,7 @@ func RepoAssignment(redirect bool) martini.Handler {
 		)
 
 		// get repository owner
-		ctx.Repo.IsOwner = ctx.IsSigned && ctx.User.LowerName == params["username"]
-		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
+		ctx.Repo.IsOwner = ctx.IsSigned && ctx.User.LowerName == strings.ToLower(params["username"])
 
 		if !ctx.Repo.IsOwner {
 			user, err = models.GetUserByName(params["username"])
@@ -70,5 +70,6 @@ func RepoAssignment(redirect bool) martini.Handler {
 		ctx.Data["Owner"] = user
 		ctx.Data["Title"] = user.Name + "/" + repo.Name
 		ctx.Data["RepositoryLink"] = ctx.Data["Title"]
+		ctx.Data["IsRepositoryOwner"] = ctx.Repo.IsOwner
 	}
 }

+ 105 - 19
public/css/gogs.css

@@ -10,6 +10,7 @@ body {
 
 html, body {
     height: 100%;
+    font-family: Helvetica, Arial, sans-serif;
 }
 
 /* override bs3 */
@@ -50,7 +51,6 @@ html, body {
 .gogs-masthead {
     background-color: #428bca;
     box-shadow: inset 0 -2px 5px rgba(0, 0, 0, .1);
-    padding: 0 16px;
     margin: 0;
 }
 
@@ -65,6 +65,12 @@ html, body {
     height: 46px;
 }
 
+#gogs-nav-logo{
+    padding-left: 0;
+    padding-right: 0;
+    margin-right: 10px;
+}
+
 .gogs-nav-item:hover,
 .gogs-nav-item:focus {
     color: #fff;
@@ -128,6 +134,11 @@ html, body {
     padding: 5px 0;
     margin-left: 10px;
     height: 28px;
+    float: right;
+}
+
+#gogs-nav-signin{
+    float: right;
 }
 
 #gogs-nav-out .fa {
@@ -228,6 +239,12 @@ html, body {
     border-radius: 6px;
 }
 
+#gogs-user-avatar-commit {
+    width: 16px;
+    height: 16px;
+    border-radius: 2px;
+}
+
 #gogs-user-name {
     margin-top: 20px;
     font-size: 1.6em;
@@ -338,10 +355,6 @@ html, body {
 
 /* #gogs-feed */
 
-#gogs-feed-left {
-    padding-left: 0;
-}
-
 #gogs-feed-right .repo-panel .panel-heading .btn {
     margin-top: -4px;
 }
@@ -399,18 +412,11 @@ html, body {
 
 .gogs-repo-nav h3 .fa {
     color: #BBB;
+    margin-left: 0;
 }
 
-.gogs-repo-btns {
-    margin-top: 18px;
-}
-
-.gogs-repo-btns .btn-group {
-    margin-left: 1em;
-}
-
-.gogs-repo-btns .btn-group .btn {
-    padding-left: 6px;
+.gogs-repo-nav .actions {
+    padding-top: 20px;
 }
 
 #gogs-repo-watching .dropdown-menu {
@@ -475,7 +481,7 @@ html, body {
 
 .activity-list .info {
     float: left;
-    padding:0 0 0 10px;
+    padding: 0 0 0 10px;
     line-height: 1.7em;
 }
 
@@ -525,6 +531,10 @@ html, body {
 }
 
 /* #gogs-source */
+#gogs-source {
+    margin-top: -20px;
+}
+
 #gogs-source .source-toolbar:after {
     clear: both;
 }
@@ -560,7 +570,9 @@ html, body {
 .file-list .icon {
     font-size: 17px;
     padding: 5px 0 4px 10px;
-    width: 40px;
+    width: 50px;
+    color: #999;
+    text-align: right;
 }
 
 .file-list .wrap {
@@ -581,13 +593,87 @@ html, body {
 
 .file-list .date .wrap {
     max-width: 120px;
-    padding: 0 20px 0 0; 
+    padding: 0 20px 0 0;
 }
 
 .file-list .date {
     text-align: right;
 }
 
+.file-content .file-head {
+    font-size: 18px;
+}
+
+.file-content .file-head .icon {
+    color: #666;
+    margin: 0 .5em 0 0;
+}
+
+.file-content .file-body {
+    padding: 30px 30px 50px;
+}
+
+.branch-list th{
+    background-color: #FFF;
+    line-height: 28px !important;
+}
+
+.branch-list td{
+    line-height: 36px !important;
+}
+
+.branch-box tr:hover td{
+    background-color: rgba(19, 95, 215, 0.06) !important;
+}
+
+.branch-box .name{
+    padding-left: 20px;
+    font-size: 15px;
+}
+
+.branch-box .action{
+    width: 150px;
+}
+
+.branch-box td.date,.branch-box td.behind,.branch-box td.ahead{
+    width: 120px;
+    font-family: Verdana, Arial, sans-serif;
+}
+
+.branch-box .graph{
+    display: block;
+    height: 3px;
+}
+
+.branch-box .behind{
+    text-align: right;
+    direction: rtl;
+}
+
+.branch-box .behind .graph{
+    background-color: #888;
+}
+
+.branch-box .ahead .graph{
+    background-color: #0093c4;
+}
+
+.branch-box .branch-main{
+    background-color: #444;
+    color: #FFF;
+    border-color: #444;
+}
+
+.branch-box .branch-main a{
+    color: #FFF;
+}
+
+.branch-box .branch-main .name .btn{
+    margin-left: .5em;
+}
+
+/* wrapper and footer */
+
 #wrapper {
     min-height: 100%;
     height: auto !important;
@@ -604,7 +690,7 @@ html, body {
 }
 
 #footer .footer-wrap {
-    padding: 20px 0;
+    padding: 20px 15px;
 }
 
 #footer a {

+ 317 - 0
public/css/markdown.css

@@ -0,0 +1,317 @@
+.markdown {
+  font-size: 14px;
+}
+
+.markdown a {
+  color: #4183C4;
+}
+
+.markdown h1,
+.markdown h2,
+.markdown h3,
+.markdown h4,
+.markdown h5,
+.markdown h6 {
+  line-height: 1.7;
+  padding: 15px 0 0;
+  margin: 0 0 15px;
+  color: #666;
+}
+
+.markdown h1,
+.markdown h2 {
+  border-bottom: 1px solid #EEE;
+}
+
+.markdown h2 {
+  border-bottom: 1px solid #EEE;
+}
+
+.markdown h1 {
+  color: #000;
+  font-size: 33px
+}
+
+.markdown h2 {
+  color: #333;
+  font-size: 28px
+}
+
+.markdown h3 {
+  font-size: 22px
+}
+
+.markdown h4 {
+  font-size: 18px
+}
+
+.markdown h5 {
+  font-size: 14px
+}
+
+.markdown h6 {
+  font-size: 14px
+}
+
+.markdown table {
+  border-collapse: collapse;
+  border-spacing: 0;
+  display: block;
+  overflow: auto;
+  width: 100%;
+  margin: 0 0 9px;
+}
+
+.markdown table th {
+  font-weight: 700
+}
+
+.markdown table th,
+.markdown table td {
+  border: 1px solid #DDD;
+  padding: 6px 13px;
+}
+
+.markdown table tr {
+  background-color: #FFF;
+  border-top: 1px solid #CCC;
+}
+
+.markdown table tr:nth-child(2n) {
+  background-color: #F8F8F8
+}
+
+.markdown li {
+  line-height: 1.6;
+  margin-top: 6px;
+}
+
+.markdown dl dt {
+  font-style: italic;
+  margin-top: 9px;
+}
+
+.markdown dl dd {
+  margin: 0 0 9px;
+  padding: 0 9px;
+}
+
+.markdown blockquote,
+.markdown blockquote p {
+  font-size: 14px;
+  background-color: #f5f5f5;
+}
+
+.markdown > pre {
+  line-height: 1.6;
+  overflow: auto;
+  background: #fff;
+  padding: 6px 10px;
+  border: 1px solid #ddd;
+}
+
+.markdown > pre.linenums {
+  padding: 0;
+}
+
+.markdown > pre > ol.linenums {
+  -webkit-box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc;
+  box-shadow: inset 40px 0 0 #f5f5f5, inset 41px 0 0 #ccc;
+}
+
+.markdown > pre > code,
+.markdown > pre > ol.linenums > li > code {
+  white-space: pre;
+  word-wrap: normal;
+}
+
+.markdown > pre > ol.linenums > li > code {
+  padding: 0 10px;
+}
+
+.markdown > pre > ol.linenums > li:first-child {
+  padding-top: 6px;
+}
+
+.markdown > pre > ol.linenums > li:last-child {
+  padding-bottom: 6px;
+}
+
+.markdown > pre > ol.linenums > li {
+  border-left: 1px solid #ddd;
+}
+
+.markdown hr {
+  border: none;
+  color: #ccc;
+  height: 4px;
+  padding: 0;
+  margin: 15px 0;
+  border-bottom: 2px solid #EEE;
+}
+
+.markdown blockquote:last-child,
+.markdown ul:last-child,
+.markdown ol:last-child,
+.markdown > pre:last-child,
+.markdown > pre:last-child,
+.markdown p:last-child {
+  margin-bottom: 0;
+}
+
+.markdown .btn {
+  color: #fff;
+}
+
+/* Author: jmblog */
+/* Project: https://github.com/jmblog/color-themes-for-google-code-prettify */
+/* GitHub Theme */
+/* Pretty printing styles. Used with prettify.js. */
+/* SPAN elements with the classes below are added by prettyprint. */
+/* plain text */
+.pln {
+  color: #333333;
+}
+
+@media screen {
+  /* string content */
+  .str {
+    color: #dd1144;
+  }
+
+  /* a keyword */
+  .kwd {
+    color: #333333;
+  }
+
+  /* a comment */
+  .com {
+    color: #999988;
+  }
+
+  /* a type name */
+  .typ {
+    color: #445588;
+  }
+
+  /* a literal value */
+  .lit {
+    color: #445588;
+  }
+
+  /* punctuation */
+  .pun {
+    color: #333333;
+  }
+
+  /* lisp open bracket */
+  .opn {
+    color: #333333;
+  }
+
+  /* lisp close bracket */
+  .clo {
+    color: #333333;
+  }
+
+  /* a markup tag name */
+  .tag {
+    color: navy;
+  }
+
+  /* a markup attribute name */
+  .atn {
+    color: teal;
+  }
+
+  /* a markup attribute value */
+  .atv {
+    color: #dd1144;
+  }
+
+  /* a declaration */
+  .dec {
+    color: #333333;
+  }
+
+  /* a variable name */
+  .var {
+    color: teal;
+  }
+
+  /* a function name */
+  .fun {
+    color: #990000;
+  }
+}
+/* Use higher contrast and text-weight for printable form. */
+@media print, projection {
+  .str {
+    color: #006600;
+  }
+
+  .kwd {
+    color: #006;
+    font-weight: bold;
+  }
+
+  .com {
+    color: #600;
+    font-style: italic;
+  }
+
+  .typ {
+    color: #404;
+    font-weight: bold;
+  }
+
+  .lit {
+    color: #004444;
+  }
+
+  .pun, .opn, .clo {
+    color: #444400;
+  }
+
+  .tag {
+    color: #006;
+    font-weight: bold;
+  }
+
+  .atn {
+    color: #440044;
+  }
+
+  .atv {
+    color: #006600;
+  }
+}
+
+/* Specify class=linenums on a pre to get line numbering */
+ol.linenums {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+/* IE indents via margin-left */
+li.L0,
+li.L1,
+li.L2,
+li.L3,
+li.L4,
+li.L5,
+li.L6,
+li.L7,
+li.L8,
+li.L9 {
+  /* */
+}
+
+/* Alternate shading for lines */
+li.L1,
+li.L3,
+li.L5,
+li.L7,
+li.L9 {
+  /* */
+}

+ 48 - 6
public/js/app.js

@@ -40,10 +40,37 @@ var Gogits = {
             //container: "body"
         });
     };
+    Gogits.initPopovers = function () {
+        var hideAllPopovers = function() {
+           $('[data-toggle=popover]').each(function() {
+                $(this).popover('hide');
+            });  
+        };
+
+        $(document).on('click', function(e) {
+            var $e = $(e.target);
+            if($e.data('toggle') == 'popover'||$e.parents("[data-toggle=popover], .popover").length > 0){
+                return;
+            }
+            hideAllPopovers();
+        });
+
+        $("body").popover({
+            selector: "[data-toggle=popover]"
+        });
+    };
     Gogits.initTabs = function () {
         var $tabs = $('[data-init=tabs]');
         $tabs.find("li:eq(0) a").tab("show");
+    };
+
+    // render markdown
+    Gogits.renderMarkdown = function () {
+        var $pre = $('.markdown').find('pre > code').parent();
+        $pre.addClass("prettyprint");
+        prettyPrint();
     }
+
 })(jQuery);
 
 // ajax utils
@@ -68,8 +95,10 @@ var Gogits = {
 
 function initCore() {
     Gogits.initTooltips();
+    Gogits.initPopovers();
     Gogits.initTabs();
     Gogits.initModals();
+    Gogits.renderMarkdown();
 }
 
 function initRegister() {
@@ -98,17 +127,30 @@ function initRegister() {
     });
 }
 
-function initUserSetting(){
+function initUserSetting() {
     $('#gogs-ssh-keys .delete').confirmation({
         singleton: true,
-        onConfirm: function(e, $this){
-            Gogits.ajaxDelete("",{"id":$this.data("del")},function(json){
-                if(json.ok){
+        onConfirm: function (e, $this) {
+            Gogits.ajaxDelete("", {"id": $this.data("del")}, function (json) {
+                if (json.ok) {
                     window.location.reload();
-                }else{
+                } else {
                     alert(json.err);
                 }
             });
         }
     });
-}
+}
+
+(function ($) {
+    $(function () {
+        initCore();
+        var body = $("#gogs-body");
+        if (body.data("page") == "user-signup") {
+            initRegister();
+        }
+        if (body.data("page") == "user") {
+            initUserSetting();
+        }
+    });
+})(jQuery);

File diff suppressed because it is too large
+ 0 - 0
public/js/bootstrap.min.js


File diff suppressed because it is too large
+ 291 - 0
public/js/lib.js


+ 20 - 34
routers/repo/repo.go

@@ -20,49 +20,35 @@ func Create(ctx *middleware.Context, form auth.CreateRepoForm) {
 		return
 	}
 
-	if ctx.HasError() {
-		ctx.Render.HTML(200, "repo/create", ctx.Data)
+	if _, err := models.CreateRepository(ctx.User,
+		form.RepoName, form.Description, form.Language, form.License,
+		form.Visibility == "private", form.InitReadme == "on"); err == nil {
+		ctx.Render.Redirect("/"+ctx.User.Name+"/"+form.RepoName, 302)
 		return
-	}
-
-	// TODO: access check
-
-	user, err := models.GetUserById(form.UserId)
-	if err != nil {
-		if err.Error() == models.ErrUserNotExist.Error() {
-			ctx.RenderWithErr("User does not exist", "repo/create", &form)
-			return
-		}
-	}
-
-	if err == nil {
-		if _, err = models.CreateRepository(user,
-			form.RepoName, form.Description, form.Language, form.License,
-			form.Visibility == "private", form.InitReadme == "on"); err == nil {
-			ctx.Render.Redirect("/"+user.Name+"/"+form.RepoName, 302)
-			return
-		}
-	}
-
-	if err.Error() == models.ErrRepoAlreadyExist.Error() {
+	} else if err == models.ErrRepoAlreadyExist {
 		ctx.RenderWithErr("Repository name has already been used", "repo/create", &form)
 		return
 	}
-
-	ctx.Handle(200, "repo.Create", err)
 }
 
-func Delete(ctx *middleware.Context, form auth.DeleteRepoForm) {
-	ctx.Data["Title"] = "Delete repository"
-
-	if ctx.Req.Method == "GET" {
-		ctx.Render.HTML(200, "repo/delete", ctx.Data)
+func SettingPost(ctx *middleware.Context) {
+	if !ctx.Repo.IsOwner {
+		ctx.Render.Error(404)
 		return
 	}
 
-	if err := models.DeleteRepository(form.UserId, form.RepoId, form.UserName); err != nil {
-		ctx.Handle(200, "repo.Delete", err)
-		return
+	switch ctx.Query("action") {
+	case "delete":
+		if len(ctx.Repo.Repository.Name) == 0 || ctx.Repo.Repository.Name != ctx.Query("repository") {
+			ctx.Data["ErrorMsg"] = "Please make sure you entered repository name is correct."
+			ctx.Render.HTML(200, "repo/setting", ctx.Data)
+			return
+		}
+
+		if err := models.DeleteRepository(ctx.User.Id, ctx.Repo.Repository.Id, ctx.User.LowerName); err != nil {
+			ctx.Handle(200, "repo.Delete", err)
+			return
+		}
 	}
 
 	ctx.Render.Redirect("/", 302)

+ 73 - 4
routers/repo/single.go

@@ -9,10 +9,33 @@ import (
 
 	"github.com/codegangsta/martini"
 
+	"github.com/gogits/git"
+
 	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/middleware"
 )
 
+func Branches(ctx *middleware.Context, params martini.Params) {
+	if !ctx.Repo.IsValid {
+		return
+	}
+
+	ctx.Data["Username"] = params["username"]
+	ctx.Data["Reponame"] = params["reponame"]
+
+	brs, err := models.GetBranches(params["username"], params["reponame"])
+	if err != nil {
+		ctx.Handle(200, "repo.Branches", err)
+		return
+	}
+
+	ctx.Data["Branches"] = brs
+	ctx.Data["IsRepoToolbarBranches"] = true
+
+	ctx.Render.HTML(200, "repo/branches", ctx.Data)
+}
+
 func Single(ctx *middleware.Context, params martini.Params) {
 	if !ctx.Repo.IsValid {
 		return
@@ -22,17 +45,28 @@ func Single(ctx *middleware.Context, params martini.Params) {
 		params["branchname"] = "master"
 	}
 
+	// Get tree path
 	treename := params["_1"]
+
+	// Directory and file list.
 	files, err := models.GetReposFiles(params["username"], params["reponame"],
 		params["branchname"], treename)
 	if err != nil {
-		ctx.Handle(200, "repo.Single", err)
+		ctx.Render.Error(404)
 		return
 	}
 	ctx.Data["Username"] = params["username"]
 	ctx.Data["Reponame"] = params["reponame"]
 	ctx.Data["Branchname"] = params["branchname"]
 
+	// Branches.
+	brs, err := models.GetBranches(params["username"], params["reponame"])
+	if err != nil {
+		ctx.Render.Error(404)
+		return
+	}
+	ctx.Data["Branches"] = brs
+
 	var treenames []string
 	Paths := make([]string, 0)
 
@@ -43,16 +77,52 @@ func Single(ctx *middleware.Context, params martini.Params) {
 		}
 	}
 
+	// Get latest commit according username and repo name
+	commit, err := models.GetLastestCommit(params["username"], params["reponame"])
+	if err != nil {
+		ctx.Render.Error(404)
+		return
+	}
+	ctx.Data["LatestCommit"] = commit
+
+	var readmeFile *models.RepoFile
+
+	for _, f := range files {
+		if !f.IsFile() {
+			continue
+		}
+
+		if len(f.Name) < 6 {
+			continue
+		}
+
+		if strings.ToLower(f.Name[:6]) == "readme" {
+			readmeFile = f
+			break
+		}
+	}
+
+	if readmeFile != nil {
+		// 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 {
+			ctx.Data["FileIsLarge"] = true
+		} else {
+			ctx.Data["ReadmeContent"] = string(base.RenderMarkdown(blob.Contents()))
+		}
+	}
+
 	ctx.Data["Paths"] = Paths
 	ctx.Data["Treenames"] = treenames
 	ctx.Data["IsRepoToolbarSource"] = true
-	ctx.Data["IsRepositoryOwner"] = strings.ToLower(params["username"]) == ctx.User.LowerName
 	ctx.Data["Files"] = files
 	ctx.Render.HTML(200, "repo/single", ctx.Data)
 }
 
 func Setting(ctx *middleware.Context, params martini.Params) {
-	if !ctx.Repo.IsValid {
+	if !ctx.Repo.IsOwner {
+		ctx.Render.Error(404)
 		return
 	}
 
@@ -63,7 +133,6 @@ func Setting(ctx *middleware.Context, params martini.Params) {
 
 	ctx.Data["Title"] = title + " - settings"
 	ctx.Data["IsRepoToolbarSetting"] = true
-	ctx.Data["IsRepositoryOwner"] = strings.ToLower(params["username"]) == ctx.User.LowerName
 	ctx.Render.HTML(200, "repo/setting", ctx.Data)
 }
 

+ 1 - 1
routers/user/user.go

@@ -195,7 +195,7 @@ func Feeds(ctx *middleware.Context, form auth.FeedsForm) {
 	feeds := make([]string, len(actions))
 	for i := range actions {
 		feeds[i] = fmt.Sprintf(feedTpl, base.ActionIcon(actions[i].OpType),
-			base.TimeSince(actions[i].Created), base.ActionDesc(actions[i]))
+			base.TimeSince(actions[i].Created), base.ActionDesc(actions[i], ctx.User.AvatarLink()))
 	}
 	ctx.Render.JSON(200, &feeds)
 }

+ 7 - 14
templates/base/footer.tmpl

@@ -1,19 +1,12 @@
-<script>
-    $(function(){
-        initCore();{{if .PageIsSignUp}}
-        initRegister();{{end}}{{if .PageIsUserSetting}}
-        initUserSetting();{{end}}
-    });
-</script>
-	<div class="wrapper-push"></div>
+<div class="wrapper-push"></div>
 </div>
 <footer id="footer">
-	<div class="container footer-wrap">
-		<p>
-			© 2014 Gogs · ver {{AppVer}} · <i class="fa fa-github"></i><a target="_blank" href="https://github.com/gogits/gogs">GitHub</a>
-		</p>
-		<p class="desc"></p>
-	</div>
+    <div class="container footer-wrap">
+        <p>© 2014 Gogs · ver {{AppVer}} ·
+            <i class="fa fa-github"></i><a target="_blank" href="https://github.com/gogits/gogs">GitHub</a>
+        </p>
+        <p class="desc"></p>
+    </div>
 </footer>
 </body>
 </html>

+ 5 - 1
templates/base/head.tmpl

@@ -3,7 +3,9 @@
 	<head>
 		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 		<link rel="shortcut icon" href="/img/favicon.png" />
-		<meta name="author" content="Gogs - Go Git Service" />
+        <meta http-equiv="X-UA-Compatible" content="IE=edge"/>
+        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
+        <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="keywords" content="go, git">
 
@@ -11,10 +13,12 @@
 		<link href="/css/bootstrap.min.css" rel="stylesheet" />
 		<link href="/css/todc-bootstrap.min.css" rel="stylesheet" />
 		<link href="/css/font-awesome.min.css" rel="stylesheet" />
+		<link href="/css/markdown.css" rel="stylesheet" />
 		<link href="/css/gogs.css" rel="stylesheet" />
 
 		<script src="/js/jquery-1.10.1.min.js"></script>
 		<script src="/js/bootstrap.min.js"></script>
+        <script src="/js/lib.js"></script>
         <script src="/js/app.js"></script>
 		<title>{{if .Title}}{{.Title}} - {{end}}{{AppName}}</title>
 	</head>

+ 42 - 0
templates/repo/branches.tmpl

@@ -0,0 +1,42 @@
+{{template "base/head" .}}
+{{template "base/navbar" .}}
+{{template "repo/nav" .}}
+{{template "repo/toolbar" .}}
+<div id="gogs-body" class="container">
+    <div id="gogs-source">
+        <div class="panel panel-default branch-box info-box">
+            <div class="panel-heading info-head">
+                <h4>Branches</h4>
+            </div>
+            <table class="panel-footer table branch-list table table-hover">
+                <thead>
+                <tr>
+                    <th class="name"></th>
+                    <th class="behind">Behind</th>
+                    <th class="ahead">Ahead</th>
+                    <th class="date">Last Commit</th>
+                    <th class="action"></th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr class="branch-main">
+                    <td class="name" colspan="3">
+                        <a href="#"><strong>BranchName</strong></a>
+                        <button class="btn btn-primary btn-sm">base branch</button>
+                    </td>
+                    <td class="date">3 years ago</td>
+                    <td class="action"></td>
+                </tr>
+                <tr>
+                    <td class="name"><a href="#"><strong>BranchName</strong></a></td>
+                    <td class="behind">102 <span class="graph" style="width: 100%"></span></td>
+                    <td class="ahead"><span class="graph" style="width: 4%"></span>4</td>
+                    <td class="date">3 years ago</td>
+                    <td class="action"><a class="btn btn-info btn-sm" href="#">compare</a></td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+</div>
+{{template "base/footer" .}}

+ 0 - 12
templates/repo/delete.tmpl

@@ -1,12 +0,0 @@
-{{template "base/head" .}}
-{{template "base/navbar" .}}
-<div class="container">
-	<form action="/repo/delete" method="post" class="form-horizontal">
-		<div class="form-group">
-		    <div class="col-md-offset-4 col-md-3">
-		    	<button type="submit" class="btn btn-danger">Delete repository</button>
-		    </div>
-		</div>
-	</form>
-</div>
-{{template "base/footer" .}}

+ 32 - 33
templates/repo/nav.tmpl

@@ -1,42 +1,41 @@
 <div id="gogs-body-nav" class="gogs-repo-nav">
     <div class="container">
-        <div class="gogs-repo-btns pull-right">
-            <div class="btn-group" id="gogs-repo-clone">
-                <button type="button" class="btn btn-default"><i class="fa fa-download"></i>Clone</button>
-                <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
-                    <span class="caret"></span>
-                    <span class="sr-only">Toggle Dropdown</span>
-                </button>
-                <div class="dropdown-menu" role="menu">
-                    <div data-val="down-http">http link</div>
-                    <div data-val="down-git">git link</div>
-                </div>
+        <div class="row">
+            <div class="col-md-6">
+                <h3><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3>
             </div>
-            <div class="btn-group" id="gogs-repo-watching">
-                <button type="button" class="btn btn-default"><i class="fa fa-eye"></i>Watch {x}</button>
-                <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
-                    <span class="caret"></span>
-                    <span class="sr-only">Toggle Dropdown</span>
-                </button>
-                <div class="dropdown-menu" role="menu">
-                    <div class="dropdown-item" data-val="not-watching">
-                        <h4 role="presentation" class="dropdown-header">Not Watching</h4>
-                        <p class="description">You only receive notifications for conversations in which you participate or are @mentioned.</p>
-                        <p class="divider"></p>
-                    </div>
-                    <div class="dropdown-item" data-val="watching">
-                        <h4 role="presentation" class="dropdown-header">Watching</h4>
-                        <p class="description">You receive notifications for all conversations in this repository.</p>
+            <div class="col-md-6 actions text-right">
+                <div class="btn-group" id="gogs-repo-clone">
+                    <button type="button" class="btn btn-default"><i class="fa fa-download"></i>Clone</button>
+                    <button type="button" class="btn btn-default dropdown-toggle" data-container="body" data-toggle="popover" data-placement="bottom" data-content="<label>SSH:</label><div class='input-group'><input type='text' class='form-control' value='git@{{AppDomain}}:{{.Owner.Name}}/{{.Repository.Name}}.git'></div>" data-html="1">
+                        <span class="caret"></span>
+                    </button>
+                </div>
+                <div class="btn-group" id="gogs-repo-watching">
+                    <button type="button" class="btn btn-default"><i class="fa fa-eye"></i>Watch {x}</button>
+                    <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown">
+                        <span class="caret"></span>
+                        <span class="sr-only">Toggle Dropdown</span>
+                    </button>
+                    <div class="dropdown-menu" role="menu">
+                        <div class="dropdown-item" data-val="not-watching">
+                            <h4 role="presentation" class="dropdown-header">Not Watching</h4>
+                            <p class="description">You only receive notifications for conversations in which you participate or are @mentioned.</p>
+                            <p class="divider"></p>
+                        </div>
+                        <div class="dropdown-item" data-val="watching">
+                            <h4 role="presentation" class="dropdown-header">Watching</h4>
+                            <p class="description">You receive notifications for all conversations in this repository.</p>
+                        </div>
                     </div>
                 </div>
-            </div>
-            <div class="btn-group">
-                <button type="button" class="btn btn-default"><i class="fa fa-star"></i>Star&nbsp;&nbsp;{{.Repository.NumStars}}</button>
-            </div>
-            <div class="btn-group">
-                <button type="button" class="btn btn-default"><i class="fa fa-code-fork"></i>Fork&nbsp;&nbsp;{{.Repository.NumForks}}</button>
+                <div class="btn-group">
+                    <button type="button" class="btn btn-default"><i class="fa fa-star"></i>Star&nbsp;&nbsp;{{.Repository.NumStars}}</button>
+                </div>
+                <div class="btn-group">
+                    <button type="button" class="btn btn-default"><i class="fa fa-code-fork"></i>Fork&nbsp;&nbsp;{{.Repository.NumForks}}</button>
+                </div>
             </div>
         </div>
-        <h3><i class="fa fa-book fa-lg"></i><a href="{{.Owner.HomeLink}}">{{.Owner.Name}}</a> / {{.Repository.Name}}</h3>
     </div>
 </div>

+ 49 - 19
templates/repo/setting.tmpl

@@ -4,30 +4,60 @@
 {{template "repo/toolbar" .}}
 <div id="gogs-body" class="container">
     <div id="gogs-user-setting-nav" class="col-md-3">
-        <h4>Repository Settings</h4>
         <ul class="list-group" data-init="tabs">
-            <li class="list-group-item"><a href="#options" data-toggle="tab">Options</a></li>
-            <!--<li class="list-group-item" data-toggle="tab"><a href="#">Collaborators</a></li>
-            <li class="list-group-item" data-toggle="tab"><a href="#">Notifications</a></li>-->
-            <li class="list-group-item"><a href="#delete" data-toggle="tab">Delete</a></li>
+            <li class="list-group-item active"><a href="/{{.Owner.Name}}/{{.Repository.Name}}/settings">Options</a></li>
+            <!--<li class="list-group-item"><a href="#">Collaborators</a></li>
+            <li class="list-group-item"><a href="#">Notifications</a></li>-->
         </ul>
     </div>
-    <div id="gogs-repo-setting-container" class="col-md-9 tab-content">
-        <div id="options" class="tab-pane">
-            <h4>Repository Options</h4>
+    <div id="gogs-repo-setting-container" class="col-md-9">
+        {{if .ErrorMsg}}<p class="alert alert-danger">{{.ErrorMsg}}</p>{{end}}
+        <div class="panel panel-default">
+            <div class="panel-heading">
+                Repository Options
+            </div>
+            <div class="panel-body">
+                
+            </div>
         </div>
-        <div id="delete" class="tab-pane">
-            <h4>Delete Repository</h4>
-            <p class="alert alert-warning">Unexpected bad things will happen if you don't read this!</p>
-            <p>This action <strong>CANNOT</strong> be undone. This will delete the repository, wiki, issues, and comments permanently. </p>
+        <div class="panel panel-warning">
+            <div class="panel-heading">
+                Danger Zone
+            </div>
+            <div class="panel-body">
+                <button type="button" class="btn btn-default pull-right" href="#delete-repository-modal" data-toggle="modal">
+                    Delete this repository
+                </button>
+                <dd>
+                    <dt>Delete this repository.</dt>
+                    <dl>Once you delete a repository, there is no going back. Please be certain.</dl>
+                </dd>
 
-            <form action="/repo/delete" method="post">
-                <input type="hidden" name="userId" value="{{.Owner.Id}}"/>
-                <input type="hidden" name="userName" value="{{.Owner.Name}}"/>
-                <input type="hidden" name="repoId" value="{{.Repository.Id}}"/>
-                <hr/>
-                <button class="btn btn-danger btn-lg">I understand the consequences, delete this repository</button>
-            </form>
+                <div class="modal fade" id="delete-repository-modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
+                    <div class="modal-dialog">
+                        <form action="/{{.Owner.Name}}/{{.Repository.Name}}/settings" method="post" class="modal-content">
+                            <input type="hidden" name="action" value="delete">
+
+                            <div class="modal-header">
+                                <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
+                                <h4 class="modal-title" id="myModalLabel">Delete repository</h4>
+                            </div>
+
+                            <div class="modal-body">
+                                <div class="form-group">
+                                    <label>Please enter your repository name "<strong class="text-danger">{{.Repository.Name}}</strong>"</label>
+                                    <input name="repository" class="form-control" type="text" placeholder="Type your repository name" required="required">
+                                </div>
+                            </div>
+
+                            <div class="modal-footer">
+                                <button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>
+                                <button class="btn btn-danger btn-lg">I understand the consequences, delete this repository</button>
+                            </div>
+                        </form>
+                    </div>
+                </div>
+            </div>
         </div>
     </div>
 </div>

+ 50 - 39
templates/repo/single.tmpl

@@ -5,56 +5,61 @@
 <div id="gogs-body" class="container">
     <div id="gogs-source">
         <div class="source-toolbar">
+            {{ $username := .Username}}
+            {{ $reponame := .Reponame}}
+            {{ $branchname := .Branchname}}
+            {{ $treenames := .Treenames}}
+            {{ $repoLink := .RepositoryLink}}
+            {{ $n := len $treenames}}
             <button class="btn btn-default pull-right"><i class="fa fa-plus-square"></i>Add File</button>
             <div class="dropdown branch-switch">
-                <a href="#" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><i class="fa fa-chain"></i>master&nbsp;&nbsp;
+                <a href="#" class="btn btn-success dropdown-toggle" data-toggle="dropdown"><i class="fa fa-chain"></i>{{$branchname}}&nbsp;&nbsp;
                     <b class="caret"></b></a>
                 <ul class="dropdown-menu">
-                    <li><a class="current" href="/{{.RepositoryLink}}/tree/master">master</a></li>
-                    <li><a href="/{{.RepositoryLink}}/tree/develop">develop</a></li>
+                    {{range .Branches}}
+                    <li><a {{if eq . $branchname}}class="current" {{end}}href="/{{$repoLink}}/tree/{{.}}">{{.}}</a></li>
+                    {{end}}
                 </ul>
             </div>
             {{$paths := .Paths}}
-                {{ $username := .Username}}
-            {{ $reponame := .Reponame}}
-            {{ $branchname := .Branchname}}
-            {{ $treenames := .Treenames}}
-            {{ $n := len $treenames}}
             {{ $l := Subtract $n 1}}
             <ol class="breadcrumb">
-                <li class="root dir"><a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}">{{.Repository.Name}}</a></li>
+                <li class="root dir">
+                    <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}">{{.Repository.Name}}</a></li>
                 {{range $i, $v := $treenames}}
                 <li class="dir">
-                {{if eq $i $l}}{{$v}}
-                {{else}}
-                <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{index $paths $i}}">{{$v}}</a>&nbsp;
-                {{end}}</li>
+                    {{if eq $i $l}}{{$v}}
+                    {{else}}
+                    <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{index $paths $i}}">{{$v}}</a>&nbsp;
+                    {{end}}
+                </li>
                 {{end}}
             </ol>
         </div>
         <div class="panel panel-default info-box">
             <div class="panel-heading info-head">
-                Merge branch 'release/1.1.1'
+                <a href="/{{$username}}/{{$reponame}}/commit/{{.LatestCommit.SHA}}">{{.LatestCommit.Message}}</a>
             </div>
             <div class="panel-body info-content">
-                slene authored 4 days ago
+                <a href="/user/{{.LatestCommit.Author}}">{{.LatestCommit.Author}}</a> <span class="text-muted">{{TimeSince .LatestCommit.Date}}</span>
             </div>
             <table class="panel-footer table file-list">
                 <thead class="hidden">
-    	            <tr>
-                        <th class="icon"></th>
-    	                <th class="name">Filename</th>
-                        <th class="text">Message</th>
-    	                <th class="date">Date modified</th>
-    	            </tr>
+                <tr>
+                    <th class="icon"></th>
+                    <th class="name">Filename</th>
+                    <th class="text">Message</th>
+                    <th class="date">Date modified</th>
+                </tr>
                 </thead>
                 <tbody>
-        			{{range .Files}}
-    				<tr {{if .IsDir}}class="is-dir"{{end}}>
-                        <td class="icon">
-                            <i class="fa {{if .IsDir}}fa-folder{{else}}fa-file-text-o{{end}}"></i>
-                        </td>
-    	                <td class="name">
+                {{range .Files}}
+                <tr
+                {{if .IsDir}}class="is-dir"{{end}}>
+                <td class="icon">
+                    <i class="fa {{if .IsDir}}fa-folder{{else}}fa-file-text-o{{end}}"></i>
+                </td>
+                <td class="name">
                             <span class="wrap">
                                 {{if .IsDir}}
                                 <a href="/{{$username}}/{{$reponame}}/tree/{{$branchname}}/{{.Path}}">{{.Name}}</a>
@@ -62,29 +67,35 @@
                                 <a href="/{{$username}}/{{$reponame}}/blob/{{$branchname}}/{{.Name}}">{{.Name}}</a>
                                 {{end}}
                             </span>
-                        </td>
-    	                <td class="text">
+                </td>
+                <td class="text">
                             <span class="wrap">
                                 {{.Message}}
                             </span>
-                        </td>
-                        <td class="date">
+                </td>
+                <td class="date">
                             <span class="wrap">
                                 {{TimeSince .Created}}
                             </span>
-                        </td>
-    				</tr>
-        			{{end}}
+                </td>
+                </tr>
+                {{end}}
                 </tbody>
             </table>
         </div>
         <div class="panel panel-default file-content">
-            <div class="panel-heading">
-                README.md
-            </div>
-            <div class="panel-body markdown">
-                 httplib
+            <div class="panel-heading file-head">
+                <i class="icon fa fa-book"></i> README.md
             </div>
+            {{if .FileIsLarge}}
+                <div class="panel-footer">
+                    Large file size 1000kb
+                </div>
+            {{else}}
+                <div class="panel-body file-body markdown">
+                    {{.ReadmeContent|str2html}}
+                </div>
+            {{end}}
         </div>
     </div>
 </div>

+ 4 - 3
templates/repo/toolbar.tmpl

@@ -4,9 +4,10 @@
             <div class="collapse navbar-collapse">
                 <ul class="nav navbar-nav">
                     <li class="{{if .IsRepoToolbarSource}}active{{end}}"><a href="/{{.RepositoryLink}}">Source</a></li>
-                    <li><a href="/{{.RepositoryLink}}/commits">Commits</a></li>
-                    <li><a href="/{{.RepositoryLink}}/issues">Issues <!--<span class="badge">42</span>--></a></li>
-                    <li><a href="/{{.RepositoryLink}}/pulls">Pull Requests</a></li>
+                    <li class="{{if .IsRepoToolbarCommits}}active{{end}}"><a href="/{{.RepositoryLink}}/commits">Commits</a></li>
+                    <li class="{{if .IsRepoToolbarBranches}}active{{end}}"><a href="/{{.RepositoryLink}}/branches">Branches</a></li>
+                    <li class="{{if .IsRepoToolbarPulls}}active{{end}}"><a href="/{{.RepositoryLink}}/pulls">Pull Requests</a></li>
+                    <li class="{{if .IsRepoToolbarIssues}}active{{end}}"><a href="/{{.RepositoryLink}}/issues">Issues <!--<span class="badge">42</span>--></a></li>
                     <li class="dropdown">
                         <a href="#" class="dropdown-toggle" data-toggle="dropdown">More <b class="caret"></b></a>
                         <ul class="dropdown-menu">

+ 1 - 1
templates/user/dashboard.tmpl

@@ -11,7 +11,7 @@
         <h3>News Feed</h3>
     </div>
 </div>
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     {{if .HasInfo}}<div class="alert alert-info">{{.InfoMsg}}</div>{{end}}
     <div id="gogs-feed-left" class="col-md-8">
         <ul class="list-unstyled activity-list">

+ 1 - 1
templates/user/delete.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-setting-nav" class="col-md-3">
         <h4>Account Setting</h4>
         <ul class="list-group">

+ 1 - 1
templates/user/notification.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-setting-nav" class="col-md-3">
         <h4>Account Setting</h4>
         <ul class="list-group">

+ 1 - 1
templates/user/password.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-setting-nav" class="col-md-3">
         <h4>Account Setting</h4>
         <ul class="list-group">

+ 3 - 2
templates/user/profile.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-profile" class="col-md-3">
         <div class="profile-avatar text-center">
             <a href="{{.Owner.HomeLink}}" class="center-block" data-toggle="tooltip" data-placement="bottom" title="Change Avatar">
@@ -32,10 +32,11 @@
             {{if eq .TabName "activity"}}
             <div class="tab-pane active">
                 <ul class="list-unstyled activity-list">
+                {{$avatarLink := .Owner.AvatarLink}}
                 {{range .Feeds}}
                     <li>
                         <i class="icon fa fa-{{ActionIcon .OpType}}"></i>
-                        <div class="info"><span class="meta">{{TimeSince .Created}}</span><br>{{ActionDesc . | str2html}}</div>
+                        <div class="info"><span class="meta">{{TimeSince .Created}}</span><br>{{ActionDesc . $avatarLink | str2html}}</div>
                         <span class="clearfix"></span>
                     </li>
                 {{else}}

+ 1 - 1
templates/user/publickey.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-setting-nav" class="col-md-3">
         <h4>Account Setting</h4>
         <ul class="list-group">

+ 1 - 1
templates/user/security.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-setting-nav" class="col-md-3">
         <h4>Account Setting</h4>
         <ul class="list-group">

+ 1 - 1
templates/user/setting.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div id="gogs-body" class="container">
+<div id="gogs-body" class="container" data-page="user">
     <div id="gogs-user-setting-nav" class="col-md-3">
         <h4>Account Setting</h4>
         <ul class="list-group">

+ 1 - 1
templates/user/signin.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div class="container" id="gogs-body">
+<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">
         <h3>Log in</h3>
         <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>

+ 1 - 1
templates/user/signup.tmpl

@@ -1,6 +1,6 @@
 {{template "base/head" .}}
 {{template "base/navbar" .}}
-<div class="container" id="gogs-body">
+<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">
         <h3>Sign Up</h3>
 	    <div class="alert alert-danger form-error{{if .HasError}}{{else}} hidden{{end}}">{{.ErrorMsg}}</div>

+ 4 - 2
web.go

@@ -68,14 +68,16 @@ func runWeb(*cli.Context) {
 	m.Get("/user/:username", middleware.SignInRequire(false), user.Profile)
 
 	m.Any("/repo/create", middleware.SignInRequire(true), binding.BindIgnErr(auth.CreateRepoForm{}), repo.Create)
-	m.Any("/repo/delete", middleware.SignInRequire(true), binding.Bind(auth.DeleteRepoForm{}), repo.Delete)
 
 	m.Get("/help", routers.Help)
 
-	m.Get("/:username/:reponame/settings", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Setting)
+	m.Post("/:username/:reponame/settings", middleware.SignInRequire(true), middleware.RepoAssignment(true), repo.SettingPost)
+	m.Get("/:username/:reponame/settings", middleware.SignInRequire(true), middleware.RepoAssignment(true), repo.Setting)
+
 	m.Get("/:username/:reponame/commits", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Commits)
 	m.Get("/:username/:reponame/issues", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Issues)
 	m.Get("/:username/:reponame/pulls", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Pulls)
+	m.Get("/:username/:reponame/branches", middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Branches)
 	m.Get("/:username/:reponame/tree/:branchname/**",
 		middleware.SignInRequire(false), middleware.RepoAssignment(true), repo.Single)
 	m.Get("/:username/:reponame/tree/:branchname",

Some files were not shown because too many files changed in this diff