瀏覽代碼

Merge branch 'develop' of github.com:gogits/gogs into feature/pull_request2

# Conflicts:
#	modules/bindata/bindata.go
#	public/css/gogs.min.css
#	templates/repo/header.tmpl
Unknwon 9 年之前
父節點
當前提交
a79586cc54

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

@@ -370,6 +370,7 @@ issues.new = New Issue
 issues.create = Create Issue
 issues.new_label = New Label
 issues.new_label_placeholder = Label name...
+issues.create_label = Create Label
 issues.open_tab = %d Open
 issues.close_tab = %d Closed
 issues.filter_label = Label

+ 10 - 10
models/issue.go

@@ -658,6 +658,14 @@ type Milestone struct {
 	ClosedDate      time.Time
 }
 
+func (m *Milestone) BeforeUpdate() {
+	if m.NumIssues > 0 {
+		m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
+	} else {
+		m.Completeness = 0
+	}
+}
+
 func (m *Milestone) AfterSet(colName string, _ xorm.Cell) {
 	if colName == "deadline" {
 		if m.Deadline.Year() == 9999 {
@@ -804,8 +812,6 @@ func ChangeMilestoneIssueStats(issue *Issue) error {
 		m.NumClosedIssues--
 	}
 
-	m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
-
 	return UpdateMilestone(m)
 }
 
@@ -827,13 +833,8 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
 		if issue.IsClosed {
 			m.NumClosedIssues--
 		}
-		if m.NumIssues > 0 {
-			m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
-		} else {
-			m.Completeness = 0
-		}
 
-		if _, err = sess.Id(m.ID).Cols("num_issues,num_completeness,num_closed_issues").Update(m); err != nil {
+		if _, err = sess.Id(m.ID).AllCols().Update(m); err != nil {
 			sess.Rollback()
 			return err
 		}
@@ -860,8 +861,7 @@ func ChangeMilestoneAssign(oldMid, mid int64, issue *Issue) (err error) {
 			return ErrWrongIssueCounter
 		}
 
-		m.Completeness = m.NumClosedIssues * 100 / m.NumIssues
-		if _, err = sess.Id(m.ID).Cols("num_issues,num_completeness,num_closed_issues").Update(m); err != nil {
+		if _, err = sess.Id(m.ID).AllCols().Update(m); err != nil {
 			sess.Rollback()
 			return err
 		}

+ 32 - 1
models/user.go

@@ -13,7 +13,9 @@ import (
 	"fmt"
 	"image"
 	"image/jpeg"
+	_ "image/jpeg"
 	"os"
+	"path"
 	"path/filepath"
 	"strings"
 	"time"
@@ -116,11 +118,40 @@ func (u *User) HomeLink() string {
 
 // AvatarLink returns user gravatar link.
 func (u *User) AvatarLink() string {
+	defaultImgUrl := setting.AppSubUrl + "/img/avatar_default.jpg"
+	imgPath := path.Join(setting.AvatarUploadPath, com.ToStr(u.Id))
 	switch {
 	case u.UseCustomAvatar:
+		if !com.IsExist(imgPath) {
+			return defaultImgUrl
+		}
 		return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id)
 	case setting.DisableGravatar, setting.OfflineMode:
-		return setting.AppSubUrl + "/img/avatar_default.jpg"
+		if !com.IsExist(imgPath) {
+			img, err := avatar.RandomImage([]byte(u.Email))
+			if err != nil {
+				log.Error(3, "RandomImage: %v", err)
+				return defaultImgUrl
+			}
+			if err = os.MkdirAll(path.Dir(imgPath), os.ModePerm); err != nil {
+				log.Error(3, "MkdirAll: %v", err)
+				return defaultImgUrl
+			}
+			fw, err := os.Create(imgPath)
+			if err != nil {
+				log.Error(3, "Create: %v", err)
+				return defaultImgUrl
+			}
+			defer fw.Close()
+
+			if err = jpeg.Encode(fw, img, nil); err != nil {
+				log.Error(3, "Encode: %v", err)
+				return defaultImgUrl
+			}
+			log.Info("New random avatar created: %d", u.Id)
+		}
+
+		return setting.AppSubUrl + "/avatars/" + com.ToStr(u.Id)
 	case setting.Service.EnableCacheAvatar:
 		return setting.AppSubUrl + "/avatar/" + u.Avatar
 	}

+ 24 - 0
modules/avatar/avatar.go

@@ -19,9 +19,11 @@ import (
 	"errors"
 	"fmt"
 	"image"
+	"image/color/palette"
 	"image/jpeg"
 	"image/png"
 	"io"
+	"math/rand"
 	"net/http"
 	"net/url"
 	"os"
@@ -32,6 +34,7 @@ import (
 
 	"github.com/nfnt/resize"
 
+	"github.com/gogits/gogs/modules/identicon"
 	"github.com/gogits/gogs/modules/log"
 	"github.com/gogits/gogs/modules/setting"
 )
@@ -59,6 +62,27 @@ func HashEmail(email string) string {
 	return hex.EncodeToString(h.Sum(nil))
 }
 
+const _RANDOM_AVATAR_SIZE = 200
+
+// RandomImage generates and returns a random avatar image.
+func RandomImage(data []byte) (image.Image, error) {
+	randExtent := len(palette.WebSafe) - 32
+	rand.Seed(time.Now().UnixNano())
+	colorIndex := rand.Intn(randExtent)
+	backColorIndex := colorIndex - 1
+	if backColorIndex < 0 {
+		backColorIndex = randExtent - 1
+	}
+
+	// Size, background, forecolor
+	imgMaker, err := identicon.New(_RANDOM_AVATAR_SIZE,
+		palette.WebSafe[backColorIndex], palette.WebSafe[colorIndex:colorIndex+32]...)
+	if err != nil {
+		return nil, err
+	}
+	return imgMaker.Make(data), nil
+}
+
 // Avatar represents the avatar object.
 type Avatar struct {
 	Hash           string

文件差異過大導致無法顯示
+ 0 - 0
modules/bindata/bindata.go


+ 9 - 9
modules/git/repo_tag.go

@@ -20,12 +20,8 @@ func (repo *Repository) IsTagExist(tagName string) bool {
 	return IsTagExist(repo.Path, tagName)
 }
 
-// GetTags returns all tags of given repository.
-func (repo *Repository) GetTags() ([]string, error) {
-	if gitVer.AtLeast(MustParseVersion("2.0.0")) {
-		return repo.getTagsReversed()
-	}
-	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l")
+func (repo *Repository) getTagsReversed() ([]string, error) {
+	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l", "--sort=-v:refname")
 	if err != nil {
 		return nil, concatenateError(err, stderr)
 	}
@@ -33,10 +29,14 @@ func (repo *Repository) GetTags() ([]string, error) {
 	return tags[:len(tags)-1], nil
 }
 
-func (repo *Repository) getTagsReversed() ([]string, error) {
-	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l", "--sort=-v:refname")
+// GetTags returns all tags of given repository.
+func (repo *Repository) GetTags() ([]string, error) {
+	if gitVer.AtLeast(MustParseVersion("2.0.0")) {
+		return repo.getTagsReversed()
+	}
+	stdout, stderr, err := com.ExecCmdDir(repo.Path, "git", "tag", "-l")
 	if err != nil {
-		return nil, errors.New(stderr)
+		return nil, concatenateError(err, stderr)
 	}
 	tags := strings.Split(stdout, "\n")
 	return tags[:len(tags)-1], nil

+ 405 - 0
modules/identicon/block.go

@@ -0,0 +1,405 @@
+// Copyright 2015 by caixw, All rights reserved
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+package identicon
+
+import (
+	"image"
+)
+
+var (
+	// 可以出现在中间的方块,一般为了美观,都是对称图像。
+	centerBlocks = []blockFunc{b0, b1, b2, b3}
+
+	// 所有方块
+	blocks = []blockFunc{b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, b13, b14, b15, b16}
+)
+
+// 所有block函数的类型
+type blockFunc func(img *image.Paletted, x, y, size float64, angle int)
+
+// 将多边形points旋转angle个角度,然后输出到img上,起点为x,y坐标
+func drawBlock(img *image.Paletted, x, y, size float64, angle int, points []float64) {
+	if angle > 0 { // 0角度不需要转换
+		// 中心坐标与x,y的距离,方便下面指定中心坐标(x+m,y+m),
+		// 0.5的偏移值不能少,否则坐靠右,非正中央
+		m := size/2 - 0.5
+		rotate(points, x+m, y+m, angle)
+	}
+
+	for i := x; i < x+size; i++ {
+		for j := y; j < y+size; j++ {
+			if pointInPolygon(i, j, points) {
+				img.SetColorIndex(int(i), int(j), 1)
+			}
+		}
+	}
+}
+
+// 全空白
+//
+//  --------
+//  |      |
+//  |      |
+//  |      |
+//  --------
+func b0(img *image.Paletted, x, y, size float64, angle int) {
+}
+
+// 全填充正方形
+//
+//  --------
+//  |######|
+//  |######|
+//  |######|
+//  --------
+func b1(img *image.Paletted, x, y, size float64, angle int) {
+	isize := int(size)
+	ix := int(x)
+	iy := int(y)
+	for i := ix + 1; i < ix+isize; i++ {
+		for j := iy + 1; j < iy+isize; j++ {
+			img.SetColorIndex(i, j, 1)
+		}
+	}
+}
+
+// 中间小方块
+//  ----------
+//  |        |
+//  |  ####  |
+//  |  ####  |
+//  |        |
+//  ----------
+func b2(img *image.Paletted, x, y, size float64, angle int) {
+	l := size / 4
+	x = x + l
+	y = y + l
+
+	for i := x; i < x+2*l; i++ {
+		for j := y; j < y+2*l; j++ {
+			img.SetColorIndex(int(i), int(j), 1)
+		}
+	}
+}
+
+// 菱形
+//
+//  ---------
+//  |   #   |
+//  |  ###  |
+//  | ##### |
+//  |#######|
+//  | ##### |
+//  |  ###  |
+//  |   #   |
+//  ---------
+func b3(img *image.Paletted, x, y, size float64, angle int) {
+	m := size / 2
+	points := []float64{}
+
+	drawBlock(img, x, y, size, 0, append(points,
+		x+m, y,
+		x+size, y+m,
+		x+m, y+size,
+		x, y+m,
+		x+m, y,
+	))
+}
+
+// b4
+//
+//  -------
+//  |#####|
+//  |#### |
+//  |###  |
+//  |##   |
+//  |#    |
+//  |------
+func b4(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y,
+		x+size, y,
+		x, y+size,
+		x, y,
+	))
+}
+
+// b5
+//
+//  ---------
+//  |   #   |
+//  |  ###  |
+//  | ##### |
+//  |#######|
+func b5(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x+m, y,
+		x+size,
+		y+size,
+		x, y+size,
+		x+m, y,
+	))
+}
+
+// b6 矩形
+//
+//  --------
+//  |###   |
+//  |###   |
+//  |###   |
+//  --------
+func b6(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y,
+		x+m, y,
+		x+m, y+size,
+		x, y+size,
+		x, y,
+	))
+}
+
+// b7 斜放的锥形
+//
+//  ---------
+//  | #     |
+//  |  ##   |
+//  |  #####|
+//  |   ####|
+//  |--------
+func b7(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y,
+		x+size, y+m,
+		x+size, y+size,
+		x+m, y+size,
+		x, y,
+	))
+}
+
+// b8 三个堆叠的三角形
+//
+//  -----------
+//  |    #    |
+//  |   ###   |
+//  |  #####  |
+//  |  #   #  |
+//  | ### ### |
+//  |#########|
+//  -----------
+func b8(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	mm := m / 2
+
+	// 顶部三角形
+	drawBlock(img, x, y, size, angle, append(points,
+		x+m, y,
+		x+3*mm, y+m,
+		x+mm, y+m,
+		x+m, y,
+	))
+
+	// 底下左边
+	drawBlock(img, x, y, size, angle, append(points[:0],
+		x+mm, y+m,
+		x+m, y+size,
+		x, y+size,
+		x+mm, y+m,
+	))
+
+	// 底下右边
+	drawBlock(img, x, y, size, angle, append(points[:0],
+		x+3*mm, y+m,
+		x+size, y+size,
+		x+m, y+size,
+		x+3*mm, y+m,
+	))
+}
+
+// b9 斜靠的三角形
+//
+//  ---------
+//  |#      |
+//  | ####  |
+//  |  #####|
+//  |  #### |
+//  |   #   |
+//  ---------
+func b9(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y,
+		x+size, y+m,
+		x+m, y+size,
+		x, y,
+	))
+}
+
+// b10
+//
+//  ----------
+//  |    ####|
+//  |    ### |
+//  |    ##  |
+//  |    #   |
+//  |####    |
+//  |###     |
+//  |##      |
+//  |#       |
+//  ----------
+func b10(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x+m, y,
+		x+size, y,
+		x+m, y+m,
+		x+m, y,
+	))
+
+	drawBlock(img, x, y, size, angle, append(points[:0],
+		x, y+m,
+		x+m, y+m,
+		x, y+size,
+		x, y+m,
+	))
+}
+
+// b11 左上角1/4大小的方块
+//
+//  ----------
+//  |####    |
+//  |####    |
+//  |####    |
+//  |        |
+//  |        |
+//  ----------
+func b11(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y,
+		x+m, y,
+		x+m, y+m,
+		x, y+m,
+		x, y,
+	))
+}
+
+// b12
+//
+//  -----------
+//  |         |
+//  |         |
+//  |#########|
+//  |  #####  |
+//  |    #    |
+//  -----------
+func b12(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y+m,
+		x+size, y+m,
+		x+m, y+size,
+		x, y+m,
+	))
+}
+
+// b13
+//
+//  -----------
+//  |         |
+//  |         |
+//  |    #    |
+//  |  #####  |
+//  |#########|
+//  -----------
+func b13(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x+m, y+m,
+		x+size, y+size,
+		x, y+size,
+		x+m, y+m,
+	))
+}
+
+// b14
+//
+//  ---------
+//  |   #   |
+//  | ###   |
+//  |####   |
+//  |       |
+//  |       |
+//  ---------
+func b14(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x+m, y,
+		x+m, y+m,
+		x, y+m,
+		x+m, y,
+	))
+}
+
+// b15
+//
+//  ----------
+//  |#####   |
+//  |###     |
+//  |#       |
+//  |        |
+//  |        |
+//  ----------
+func b15(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x, y,
+		x+m, y,
+		x, y+m,
+		x, y,
+	))
+}
+
+// b16
+//
+//  ---------
+//  |   #   |
+//  | ##### |
+//  |#######|
+//  |   #   |
+//  | ##### |
+//  |#######|
+//  ---------
+func b16(img *image.Paletted, x, y, size float64, angle int) {
+	points := []float64{}
+	m := size / 2
+	drawBlock(img, x, y, size, angle, append(points,
+		x+m, y,
+		x+size, y+m,
+		x, y+m,
+		x+m, y,
+	))
+
+	drawBlock(img, x, y, size, angle, append(points[:0],
+		x+m, y+m,
+		x+size, y+size,
+		x, y+size,
+		x+m, y+m,
+	))
+}

+ 39 - 0
modules/identicon/doc.go

@@ -0,0 +1,39 @@
+// Copyright 2015 by caixw, All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+// 一个基于hash值生成随机图像的包。
+//
+// 关于identicon并没有统一的标准,一般用于在用户注册时,
+// 取用户的邮箱或是访问IP等数据(也可以是其它任何数据),
+// 进行hash运算,之后根据hash数据,产生一张图像,
+// 这样即可以为用户产生一张独特的头像,又不会泄漏用户的隐藏。
+//
+// 在identicon中,把图像分成以下九个部分:
+//  -------------
+//  | 1 | 2 | 3 |
+//  -------------
+//  | 4 | 5 | 6 |
+//  -------------
+//  | 7 | 8 | 9 |
+//  -------------
+// 其中1、3、9、7为不同角度(依次增加90度)的同一张图片,
+// 2、6、8、4也是如此,这样可以保持图像是对称的,比较美观。
+// 5则单独使用一张图片。
+//
+//  // 根据用户访问的IP,为其生成一张头像
+//  img, _ := identicon.Make(128, color.NRGBA{},color.NRGBA{}, []byte("192.168.1.1"))
+//  fi, _ := os.Create("/tmp/u1.png")
+//  png.Encode(fi, img)
+//  fi.Close()
+//
+//  // 或者
+//  ii, _ := identicon.New(128, color.NRGBA{}, color.NRGBA{}, color.NRGBA{})
+//  img := ii.Make([]byte("192.168.1.1"))
+//  img = ii.Make([]byte("192.168.1.2"))
+//
+// NOTE: go test 会在当前目录的testdata文件夹下产生大量的随机图片。
+// 要运行测试,必须保证该文件夹是存在的,且有相应的写入权限。
+package identicon
+
+const Version = "0.2.6.150603"

+ 147 - 0
modules/identicon/identicon.go

@@ -0,0 +1,147 @@
+// Copyright 2015 by caixw, All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+package identicon
+
+import (
+	"crypto/md5"
+	"fmt"
+	"image"
+	"image/color"
+)
+
+const (
+	minSize       = 16 // 图片的最小尺寸
+	maxForeColors = 32 // 在New()函数中可以指定的最大颜色数量
+)
+
+// Identicon 用于产生统一尺寸的头像。
+// 可以根据用户提供的数据,经过一定的算法,自动产生相应的图案和颜色。
+type Identicon struct {
+	foreColors []color.Color
+	backColor  color.Color
+	size       int
+	rect       image.Rectangle
+}
+
+// 声明一个Identicon实例。
+// size表示整个头像的大小。
+// back表示前景色。
+// fore表示所有可能的前景色,会为每个图像随机挑选一个作为其前景色。
+// NOTE:前景色不要与背景色太相近。
+func New(size int, back color.Color, fore ...color.Color) (*Identicon, error) {
+	if len(fore) == 0 || len(fore) > maxForeColors {
+		return nil, fmt.Errorf("前景色数量必须介于[1]~[%v]之间,当前为[%v]", maxForeColors, len(fore))
+	}
+
+	if size < minSize {
+		return nil, fmt.Errorf("参数size的值(%v)不能小于%v", size, minSize)
+	}
+
+	return &Identicon{
+		foreColors: fore,
+		backColor:  back,
+		size:       size,
+
+		// 画布坐标从0开始,其长度应该是size-1
+		rect: image.Rect(0, 0, size, size),
+	}, nil
+}
+
+// 根据data数据产生一张唯一性的头像图片。
+func (i *Identicon) Make(data []byte) image.Image {
+	h := md5.New()
+	h.Write(data)
+	sum := h.Sum(nil)
+
+	// 第一个方块
+	index := int(sum[0]+sum[1]+sum[2]+sum[3]) % len(blocks)
+	b1 := blocks[index]
+
+	// 第二个方块
+	index = int(sum[4]+sum[5]+sum[6]+sum[7]) % len(blocks)
+	b2 := blocks[index]
+
+	// 中间方块
+	index = int(sum[8]+sum[9]+sum[10]+sum[11]) % len(centerBlocks)
+	c := centerBlocks[index]
+
+	// 旋转角度
+	angle := int(sum[12]+sum[13]+sum[14]) % 4
+
+	// 根据最后一个字段,获取前景颜色
+	index = int(sum[15]) % len(i.foreColors)
+
+	p := image.NewPaletted(i.rect, []color.Color{i.backColor, i.foreColors[index]})
+	drawBlocks(p, i.size, c, b1, b2, angle)
+	return p
+}
+
+// 根据data数据产生一张唯一性的头像图片。
+// size 头像的大小。
+// back, fore头像的背景和前景色。
+func Make(size int, back, fore color.Color, data []byte) (image.Image, error) {
+	if size < minSize {
+		return nil, fmt.Errorf("参数size的值(%v)不能小于%v", size, minSize)
+	}
+
+	h := md5.New()
+	h.Write(data)
+	sum := h.Sum(nil)
+
+	// 第一个方块
+	index := int(sum[0]+sum[1]+sum[2]+sum[3]) % len(blocks)
+	b1 := blocks[index]
+
+	// 第二个方块
+	index = int(sum[4]+sum[5]+sum[6]+sum[7]) % len(blocks)
+	b2 := blocks[index]
+
+	// 中间方块
+	index = int(sum[8]+sum[9]+sum[10]+sum[11]) % len(centerBlocks)
+	c := centerBlocks[index]
+
+	// 旋转角度
+	angle := int(sum[12]+sum[13]+sum[14]+sum[15]) % 4
+
+	// 画布坐标从0开始,其长度应该是size-1
+	p := image.NewPaletted(image.Rect(0, 0, size, size), []color.Color{back, fore})
+	drawBlocks(p, size, c, b1, b2, angle)
+	return p, nil
+}
+
+// 将九个方格都填上内容。
+// p为画板。
+// c为中间方格的填充函数。
+// b1,b2为边上8格的填充函数。
+// angle为b1,b2的起始旋转角度。
+func drawBlocks(p *image.Paletted, size int, c, b1, b2 blockFunc, angle int) {
+	// 每个格子的长宽。先转换成float,再计算!
+	blockSize := float64(size) / 3
+	twoBlockSize := 2 * blockSize
+
+	incr := func() { // 增加angle的值,但不会大于3
+		angle++
+		if angle > 3 {
+			angle = 0
+		}
+	}
+
+	c(p, blockSize, blockSize, blockSize, 0)
+
+	b1(p, 0, 0, blockSize, angle)
+	b2(p, blockSize, 0, blockSize, angle)
+
+	incr()
+	b1(p, twoBlockSize, 0, blockSize, angle)
+	b2(p, twoBlockSize, blockSize, blockSize, angle)
+
+	incr()
+	b1(p, twoBlockSize, twoBlockSize, blockSize, angle)
+	b2(p, blockSize, twoBlockSize, blockSize, angle)
+
+	incr()
+	b1(p, 0, twoBlockSize, blockSize, angle)
+	b2(p, 0, blockSize, blockSize, angle)
+}

+ 114 - 0
modules/identicon/identicon_test.go

@@ -0,0 +1,114 @@
+// Copyright 2015 by caixw, All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+package identicon
+
+import (
+	"image"
+	"image/color"
+	"image/png"
+	"os"
+	"strconv"
+	"testing"
+
+	"github.com/issue9/assert"
+)
+
+var (
+	back  = color.RGBA{255, 0, 0, 100}
+	fore  = color.RGBA{0, 255, 255, 100}
+	fores = []color.Color{color.Black, color.RGBA{200, 2, 5, 100}, color.RGBA{2, 200, 5, 100}}
+	size  = 128
+)
+
+// 依次画出各个网络的图像。
+func TestBlocks(t *testing.T) {
+	p := []color.Color{back, fore}
+
+	a := assert.New(t)
+
+	for k, v := range blocks {
+		img := image.NewPaletted(image.Rect(0, 0, size*4, size), p) // 横向4张图片大小
+
+		for i := 0; i < 4; i++ {
+			v(img, float64(i*size), 0, float64(size), i)
+		}
+
+		fi, err := os.Create("./testdata/block-" + strconv.Itoa(k) + ".png")
+		a.NotError(err).NotNil(fi)
+		a.NotError(png.Encode(fi, img))
+		a.NotError(fi.Close()) // 关闭文件
+	}
+}
+
+// 产生一组测试图片
+func TestDrawBlocks(t *testing.T) {
+	a := assert.New(t)
+
+	for i := 0; i < 20; i++ {
+		p := image.NewPaletted(image.Rect(0, 0, size, size), []color.Color{back, fore})
+		c := (i + 1) % len(centerBlocks)
+		b1 := (i + 2) % len(blocks)
+		b2 := (i + 3) % len(blocks)
+		drawBlocks(p, size, centerBlocks[c], blocks[b1], blocks[b2], 0)
+
+		fi, err := os.Create("./testdata/draw-" + strconv.Itoa(i) + ".png")
+		a.NotError(err).NotNil(fi)
+		a.NotError(png.Encode(fi, p))
+		a.NotError(fi.Close()) // 关闭文件
+	}
+}
+
+func TestMake(t *testing.T) {
+	a := assert.New(t)
+
+	for i := 0; i < 20; i++ {
+		img, err := Make(size, back, fore, []byte("make-"+strconv.Itoa(i)))
+		a.NotError(err).NotNil(img)
+
+		fi, err := os.Create("./testdata/make-" + strconv.Itoa(i) + ".png")
+		a.NotError(err).NotNil(fi)
+		a.NotError(png.Encode(fi, img))
+		a.NotError(fi.Close()) // 关闭文件
+	}
+}
+
+func TestIdenticon(t *testing.T) {
+	a := assert.New(t)
+
+	ii, err := New(size, back, fores...)
+	a.NotError(err).NotNil(ii)
+
+	for i := 0; i < 20; i++ {
+		img := ii.Make([]byte("identicon-" + strconv.Itoa(i)))
+		a.NotNil(img)
+
+		fi, err := os.Create("./testdata/identicon-" + strconv.Itoa(i) + ".png")
+		a.NotError(err).NotNil(fi)
+		a.NotError(png.Encode(fi, img))
+		a.NotError(fi.Close()) // 关闭文件
+	}
+}
+
+// BenchmarkMake	    5000	    229378 ns/op
+func BenchmarkMake(b *testing.B) {
+	a := assert.New(b)
+	for i := 0; i < b.N; i++ {
+		img, err := Make(size, back, fore, []byte("Make"))
+		a.NotError(err).NotNil(img)
+	}
+}
+
+// BenchmarkIdenticon_Make	   10000	    222127 ns/op
+func BenchmarkIdenticon_Make(b *testing.B) {
+	a := assert.New(b)
+
+	ii, err := New(size, back, fores...)
+	a.NotError(err).NotNil(ii)
+
+	for i := 0; i < b.N; i++ {
+		img := ii.Make([]byte("Make"))
+		a.NotNil(img)
+	}
+}

+ 69 - 0
modules/identicon/polygon.go

@@ -0,0 +1,69 @@
+// Copyright 2015 by caixw, All rights reserved.
+// Use of this source code is governed by a MIT
+// license that can be found in the LICENSE file.
+
+package identicon
+
+var (
+	// 4个元素分别表示cos(0),cos(90),cos(180),cos(270)
+	cos = []float64{1, 0, -1, 0}
+
+	// 4个元素分别表示sin(0),sin(90),sin(180),sin(270)
+	sin = []float64{0, 1, 0, -1}
+)
+
+// 将points中的所有点,以x,y为原点旋转angle个角度。
+// angle取值只能是[0,1,2,3],分别表示[0,90,180,270]
+func rotate(points []float64, x, y float64, angle int) {
+	if angle > 3 {
+		panic("rotate:参数angle必须0,1,2,3三值之一")
+	}
+
+	for i := 0; i < len(points); i += 2 {
+		px := points[i] - x
+		py := points[i+1] - y
+		points[i] = px*cos[angle] - py*sin[angle] + x
+		points[i+1] = px*sin[angle] + py*cos[angle] + y
+	}
+}
+
+// 判断某个点是否在多边形之内,不包含构成多边形的线和点
+// x,y 需要判断的点坐标
+// points 组成多边形的所顶点,每两个元素表示一点顶点,其中最后一个顶点必须与第一个顶点相同。
+func pointInPolygon(x float64, y float64, points []float64) bool {
+	if len(points) < 8 { // 只有2个以上的点,才能组成闭合多边形
+		return false
+	}
+
+	// 大致算法如下:
+	// 把整个平面以给定的测试点为原点分两部分:
+	// - y>0,包含(x>0 && y==0)
+	// - y<0,包含(x<0 && y==0)
+	// 依次扫描每一个点,当该点与前一个点处于不同部分时(即一个在y>0区,一个在y<0区),
+	// 则判断从前一点到当前点是顺时针还是逆时针(以给定的测试点为原点),如果是顺时针r++,否则r--。
+	// 结果为:2==abs(r)。
+
+	r := 0
+	x1, y1 := points[0], points[1]
+	prev := (y1 > y) || ((x1 > x) && (y1 == y))
+	for i := 2; i < len(points); i += 2 {
+		x2, y2 := points[i], points[i+1]
+		curr := (y2 > y) || ((x2 > x) && (y2 == y))
+
+		if curr == prev {
+			x1, y1 = x2, y2
+			continue
+		}
+
+		mul := (x1-x)*(y2-y) - (x2-x)*(y1-y)
+		if mul > 0 {
+			r++
+		} else if mul < 0 {
+			r--
+		}
+		x1, y1 = x2, y2
+		prev = curr
+	}
+
+	return r == 2 || r == -2
+}

文件差異過大導致無法顯示
+ 0 - 0
public/css/gogs.min.css


文件差異過大導致無法顯示
+ 1 - 1
public/css/semantic.min.css


+ 10 - 2
public/js/gogs.js

@@ -43,6 +43,15 @@ function initRepository() {
 
     // Labels
     if ($('.repository.labels').length > 0) {
+        // Create label
+        var $new_label_panel = $('.new-label.segment');
+        $('.new-label.button').click(function () {
+            $new_label_panel.show();
+        });
+        $('.new-label.segment .cancel').click(function () {
+            $new_label_panel.hide();
+        });
+
         $('.color-picker').each(function () {
             $(this).minicolors();
         });
@@ -53,8 +62,7 @@ function initRepository() {
         });
         $('.edit-label-button').click(function () {
             $('#label-modal-id').val($(this).data('id'));
-            $('#label-modal-title').val($(this).data('title'));
-            $('#label-modal-color').val($(this).data('color'))
+            $('.edit-label .new-label-input').val($(this).data('title'));
             $('.minicolors-swatch-color').css("background-color", $(this).data('color'));
             $('.edit-label.modal').modal({
                 onApprove: function () {

文件差異過大導致無法顯示
+ 1 - 1
public/js/semantic.min.js


+ 27 - 27
public/less/_base.less

@@ -9,27 +9,25 @@ img {
 }
 .full.height {
 	padding: 0;
-  margin: 0 0 -87px 0;
+  margin: 0 0 -@footer-margin*2 0;
   min-height: 100%;
 }
 .following.bar {
 	z-index: 900;
 	left: 0;
 	width: 100%;
-	padding: 0.7em 0;
+	padding: 5px 0;
 	&.light {
 		background-color: white;
 		border-bottom: 1px solid #DDDDDD;
 		box-shadow: 0 2px 3px rgba(0, 0, 0, 0.04);
 	}
-	.ui.secondary.menu {
-		height: 30px;
-	}
 	.column .menu {
 		margin-top: 0;
 	}
 	.brand {
 		float: left;
+		margin-top: 5px;
 		margin-right: 5px;
 	}
 	.head.link.item {
@@ -41,7 +39,6 @@ img {
 	}
 	.user.avatar {
 		padding: 0;
-		margin-top: 1px;
 	}
 	.searchbox {
   	background-color: rgb(244, 244, 244)!important;
@@ -51,7 +48,6 @@ img {
 	}
 	.octicon {
     width: 16px;
-    opacity: 1!important;
     text-align: center;
   }
 }
@@ -68,28 +64,36 @@ img {
 			color: #d95c5c!important;
 		}
 	}
+
+	.message {
+		text-align: center;
+	}
 }
 footer {
 	margin-top: @footer-margin!important;
+	height: @footer-margin;
 	background-color: white;
 	border-top: 1px solid #d6d6d6;
 	clear: both;
 	width: 100%;
 	color: #888888;
-	.fa {
-		width: 16px;
-  	text-align: center;
-  	color: #428bca;
-	}
-	.ui.language.dropdown {
-		z-index: 10000;
-	}
-	.links >* {
-		border-left: 1px solid #d6d6d6;
-		padding-left: 8px;
-		margin-left: 5px;
-		&:first-child {
-			border-left: none;
+	.container {
+		padding-top: 10px;
+		.fa {
+			width: 16px;
+	  	text-align: center;
+	  	color: #428bca;
+		}
+		.ui.language.dropdown {
+			z-index: 10000;
+		}
+		.links >* {
+			border-left: 1px solid #d6d6d6;
+			padding-left: 8px;
+			margin-left: 5px;
+			&:first-child {
+				border-left: none;
+			}
 		}
 	}
 }
@@ -101,15 +105,11 @@ footer {
 	text-align: center;
 }
 
-.text-error {
-	color: #d95c5c !important;
-}
-
 .generate-img(16);
 .generate-img(@n, @i: 1) when (@i =< @n) {
   .img-@{i} {
-    width: (2px * @i);
-    height: (2px * @i);
+    width: (2px * @i)!important;
+    height: (2px * @i)!important;
   }
   .generate-img(@n, (@i + 1));
 }

+ 47 - 85
public/less/_repository.less

@@ -4,11 +4,11 @@
 	padding-top: 15px;
 	padding-bottom: @footer-margin * 3;
 	.head {
-		height: 75px;
-		padding-top: 20px;
+		height: 40px;
 		background-color: #FCFCFC;
 		.mega-octicon {
 			width: @mega-octicon-width;
+			font-size: 30px;
 		}
 		a,
 		.fork-flag {
@@ -25,65 +25,13 @@
 			line-height: 10px;
 			white-space: nowrap;
 		}
-		.button {
-			margin-left: 10px;
-			i {
-				margin-right: 5px;
-			}
-		}
 		.num {
 			font-weight: bold;
 		}
-		.octicon {
-			height: 5px;
-		}
-	}
-	.navbar {
-		height: 60px;
-		padding-top: 20px;
-		.ui.secondary.menu .item {
-			margin-left: -10px;
-			margin-top: -7px;
-			&>.input {
-				.new-label-input,
-				.color-picker {
-					background-color: white;
-					border: 1px solid rgba(0,0,0,.15);
-				}
-			}
-			&.input {
-				margin-right: -7px;
-			}
-			.new-label-input {
-				width: 150px;
-			}
-			.color-picker {
-				height: 35px;
-				width: auto;
-				padding-left: 30px;
-			}
-			.minicolors-swatch.minicolors-sprite {
-				top: 10px;
-				left: 10px;
-				width: 15px;
-				height: 15px;
-			}
-			&.precolors {
-				padding-left: 0;
-				padding-right: 0;
-				margin-right: 10px;
-				width: 120px;
-				.color {
-					float: left;
-					width: 15px;
-					height: 15px;
-				}
-			}
-		}
 	}
 	.filter.menu {
 		.label.color {
-			margin-left: 17px;
+			margin-left: 15px;
 			padding: 0 8px;
 		}
 		.octicon {
@@ -99,7 +47,7 @@
   		.clickable .name {
   			padding-left: 15px!important;
   		}
-		}
+	 	}
 	}
 
 	.page.buttons {
@@ -107,8 +55,8 @@
 	}
 
 	.issue.list {
-		clear: both;
 		list-style: none;
+		padding-top: 15px;
 		>.item {
 			padding-top: 15px;
 			padding-bottom: 10px;
@@ -167,8 +115,8 @@
 	}
 
 	.label.list {
-		clear: both;
 		list-style: none;
+		padding-top: 15px;
 		.item {
 			padding-top: 10px;
 			padding-bottom: 10px;
@@ -189,8 +137,8 @@
 	}
 
 	.milestone.list {
-		clear: both;
 		list-style: none;
+		padding-top: 15px;
 		> .item {
 			padding-top: 10px;
 			padding-bottom: 10px;
@@ -243,11 +191,8 @@
 		textarea {
 			height: 200px;
 		}
-	}
-
-	&.settings {
-		.content {
-			padding-left: 20px!important;
+		#deadline {
+			width: 150px;
 		}
 	}
 
@@ -316,30 +261,47 @@
 	}
 }
 
-.edit-label.modal {
-	.color-picker {
-		margin-top: -8px!important;
-		height: 35px;
-		width: auto!important;
-		padding-left: 30px!important;
-	}
-	.minicolors-swatch.minicolors-sprite {
-		top: 1px;
-		left: 10px;
-		width: 15px;
-		height: 15px;
+.ui.vertical.menu {
+	.header.item {
+		font-size: 1.1em;
+		background: #f0f0f0;
 	}
-	.precolors {
-		margin-bottom: -11px!important;
-		padding-left: 0!important;
-		padding-right: 0!important;
-		margin-right: 10px!important;
-		width: 120px!important;
-		.color {
-			float: left;
-			margin: 0!important;
+}
+
+.edit-label.modal,
+.new-label.segment {
+	.form {
+		.column {
+			padding-right: 0;
+		}
+		.buttons {
+			margin-left: auto;
+			padding-top: 15px;
+		}
+		.color.picker.column {
+			width: auto;
+			.color-picker {
+				height: 35px;
+				width: auto;
+				padding-left: 30px;
+			}
+		}
+		.minicolors-swatch.minicolors-sprite {
+			top: 10px;
+			left: 10px;
 			width: 15px;
 			height: 15px;
 		}
+		.precolors {
+			padding-left: 0;
+			padding-right: 0;
+			margin: 3px 10px auto 10px;
+			width: 120px;
+			.color {
+				float: left;
+				width: 15px;
+				height: 15px;
+			}
+		}
 	}
 }

+ 2 - 2
templates/base/footer.tmpl

@@ -1,6 +1,6 @@
 	</div>
-	<footer class="ui page grid">
-		<div class="sixteen wide column">
+	<footer>
+		<div class="ui container">
 			<div class="ui left">
 				© 2015 Gogs · {{.i18n.Tr "version"}}: {{AppVer}} · {{.i18n.Tr "page"}}: <strong>{{LoadTimes .PageStartTime}}</strong> · {{.i18n.Tr "template"}}: <strong>{{call .TmplLoadTimes}}</strong>
 			</div>

+ 33 - 32
templates/base/head.tmpl

@@ -46,39 +46,8 @@
 		<noscript>Please enable JavaScript in your browser!</noscript>
 		{{if not .PageIsInstall}}
 		<div class="following bar light">
-		  <div class="ui page grid">
+		  <div class="ui container">
 		    <div class="column">
-	      	{{if .IsSigned}}
-		      <div class="ui right floated secondary menu">
-		      	<a class="view-ui item user avatar poping up" href="{{AppSubUrl}}/{{.SignedUser.Name}}" data-content="{{.SignedUser.Name}}" data-variation="inverted">
-		      		<img class="img-15" src="{{.SignedUser.AvatarLink}}"/>
-		      		<span class="sr-only">{{.SignedUser.Name}}</span>
-		      	</a>
-						<div class="ui pointing dropdown head link jump item">
-							<span class="text">
-								<i class="octicon octicon-plus"></i>
-								<i class="dropdown icon"></i>
-							</span>
-			        <div class="menu">
-			          <a class="item" href="{{AppSubUrl}}/repo/create"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "new_repo"}}</a>
-			          <a class="item" href="{{AppSubUrl}}/repo/migrate"><i class="octicon octicon-repo-clone"></i> {{.i18n.Tr "new_migrate"}}</a>
-			          <a class="item" href="{{AppSubUrl}}/org/create"><i class="octicon octicon-organization"></i> {{.i18n.Tr "new_org"}}</a>
-							</div>
-						</div>
-						{{if .IsAdmin}}
-						<a class="view-ui item poping up {{if .PageIsAdmin}}active{{end}}" href="{{AppSubUrl}}/admin" data-content="{{.i18n.Tr "admin_panel"}}" data-variation="inverted"><i class="octicon icon settings"></i><span class="sr-only">{{.i18n.Tr "admin_panel"}}</span></a>
-						{{end}}
-						<a class="view-ui item poping up {{if .PageIsSettings}}active{{end}}" href="{{AppSubUrl}}/user/settings" data-content="{{.i18n.Tr "account_settings"}}" data-variation="inverted"><i class="octicon octicon-settings"></i><span class="sr-only">{{.i18n.Tr "account_settings"}}</span></a>
-						<a class="view-ui item poping up" href="{{AppSubUrl}}/user/logout" data-content="{{.i18n.Tr "sign_out"}}" data-variation="inverted"><i class="octicon octicon-sign-out"></i><span class="sr-only">{{.i18n.Tr "sign_out"}}</span></a>
-		      </div>
-	      	{{else}}
-		      <div class="ui right floated secondary menu">
-		      	{{if .ShowRegistrationButton}}
-		      	<a class="view-ui item {{if .PageIsSignUp}}active{{end}}" href="{{AppSubUrl}}/user/sign_up"><i class="octicon octicon-person-add"></i> {{.i18n.Tr "register"}}</a>
-		      	{{end}}
-		      	<a class="view-ui item {{if .PageIsSignIn}}active{{end}}" href="{{AppSubUrl}}/user/login"><i class="octicon octicon-sign-in"></i> {{.i18n.Tr "sign_in"}}</a>
-		      </div>
-		      {{end}}
 		      <div class="ui secondary menu">
 		      	<img class="img-15 ui image brand" src="{{AppSubUrl}}/img/favicon.png">
 		      	<a class="view-ui item {{if .PageIsHome}}active{{end}}" href="{{AppSubUrl}}/">{{if .IsSigned}}{{.i18n.Tr "dashboard"}}{{else}}{{.i18n.Tr "home"}}{{end}}</a>
@@ -90,6 +59,38 @@
 		      	    <i class="search icon"></i>
 		      	  </div>
 		      	</div> -->
+
+  	      	{{if .IsSigned}}
+  		      <div class="right menu">
+  		      	<a class="view-ui item user avatar poping up" href="{{AppSubUrl}}/{{.SignedUser.Name}}" data-content="{{.SignedUser.Name}}" data-variation="inverted">
+  		      		<img class="img-15" src="{{.SignedUser.AvatarLink}}"/>
+  		      		<span class="sr-only">{{.SignedUser.Name}}</span>
+  		      	</a>
+  						<div class="ui dropdown head link jump item">
+  							<span class="text">
+  								<i class="octicon octicon-plus"></i>
+  								<i class="dropdown icon"></i>
+  							</span>
+  			        <div class="menu">
+  			          <a class="item" href="{{AppSubUrl}}/repo/create"><i class="octicon octicon-repo-create"></i> {{.i18n.Tr "new_repo"}}</a>
+  			          <a class="item" href="{{AppSubUrl}}/repo/migrate"><i class="octicon octicon-repo-clone"></i> {{.i18n.Tr "new_migrate"}}</a>
+  			          <a class="item" href="{{AppSubUrl}}/org/create"><i class="octicon octicon-organization"></i> {{.i18n.Tr "new_org"}}</a>
+  							</div>
+  						</div>
+  						{{if .IsAdmin}}
+  						<a class="view-ui item poping up {{if .PageIsAdmin}}active{{end}}" href="{{AppSubUrl}}/admin" data-content="{{.i18n.Tr "admin_panel"}}" data-variation="inverted"><i class="octicon icon settings"></i><span class="sr-only">{{.i18n.Tr "admin_panel"}}</span></a>
+  						{{end}}
+  						<a class="view-ui item poping up {{if .PageIsSettings}}active{{end}}" href="{{AppSubUrl}}/user/settings" data-content="{{.i18n.Tr "account_settings"}}" data-variation="inverted"><i class="octicon octicon-settings"></i><span class="sr-only">{{.i18n.Tr "account_settings"}}</span></a>
+  						<a class="view-ui item poping up" href="{{AppSubUrl}}/user/logout" data-content="{{.i18n.Tr "sign_out"}}" data-variation="inverted"><i class="octicon octicon-sign-out"></i><span class="sr-only">{{.i18n.Tr "sign_out"}}</span></a>
+  		      </div>
+  	      	{{else}}
+  		      <div class="ui right floated secondary menu">
+  		      	{{if .ShowRegistrationButton}}
+  		      	<a class="view-ui item {{if .PageIsSignUp}}active{{end}}" href="{{AppSubUrl}}/user/sign_up"><i class="octicon octicon-person-add"></i> {{.i18n.Tr "register"}}</a>
+  		      	{{end}}
+  		      	<a class="view-ui item {{if .PageIsSignIn}}active{{end}}" href="{{AppSubUrl}}/user/login"><i class="octicon octicon-sign-in"></i> {{.i18n.Tr "sign_in"}}</a>
+  		      </div>
+  		      {{end}}
 		      </div>
 		    </div>
 		  </div>

+ 11 - 13
templates/repo/header.tmpl

@@ -1,16 +1,14 @@
 {{with .Repository}}
-<div class="ui middle page head grid">
-	<h2 class="ui left">
-		<div class="ui breadcrumb">
-		<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}repo{{end}}"></i>
-	  <a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
-	  <div class="divider"> / </div>
-	  <a href="{{$.RepoLink}}">{{.Name}}</a>
-	  {{if .IsMirror}}<div class="ui label">{{$.i18n.Tr "mirror"}}</div>{{end}}
-	  {{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.RepoLink}}">{{SubStr .BaseRepo.RepoLink 1 -1}}</a></div>{{end}}
-		</div>
-	</h2>
-	<div class="ui right floated secondary menu">
+<div class="ui head container">
+	<div class="ui huge breadcrumb">
+	<i class="mega-octicon octicon-{{if .IsPrivate}}lock{{else if .IsMirror}}repo-clone{{else if .IsFork}}repo-forked{{else}}repo{{end}}"></i>
+  <a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a>
+  <div class="divider"> / </div>
+  <a href="{{$.RepoLink}}">{{.Name}}</a>
+  {{if .IsMirror}}<div class="ui label">{{$.i18n.Tr "mirror"}}</div>{{end}}
+  {{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.RepoLink}}">{{SubStr .BaseRepo.RepoLink 1 -1}}</a></div>{{end}}
+	</div>
+	<div class="ui right">
 		<a class="ui black basic button" href="{{$.RepoLink}}/action/{{if $.IsWatchingRepo}}un{{end}}watch?redirect_to={{$.Link}}">
 		  <i class="fa fa-eye{{if not $.IsWatchingRepo}}-slash{{end}}"></i>
 		  {{if $.IsWatchingRepo}}{{$.i18n.Tr "repo.unwatch"}}{{else}}{{$.i18n.Tr "repo.watch"}}{{end}} <span class="num">{{.NumWatches}}</span>
@@ -19,7 +17,7 @@
 		  <i class="fa fa-star{{if not $.IsStaringRepo}}-o{{end}}"></i>
 		  {{if $.IsStaringRepo}}{{$.i18n.Tr "repo.unstar"}}{{else}}{{$.i18n.Tr "repo.star"}}{{end}} <span class="num">{{.NumStars}}</span>
 		</a>
-		<a class="ui black basic button {{if $.IsRepositoryOwner}}poping up{{end}}" {{if not $.IsRepositoryOwner}}href="{{AppSubUrl}}/repo/fork/{{.Id}}"{{end}} {{if $.IsRepositoryOwner}}data-content="{{$.i18n.Tr "repo.fork_from_self"}}"{{end}}>
+		<a class="ui black basic button {{if $.IsRepositoryOwner}}poping up{{end}}" {{if not $.IsRepositoryOwner}}href="{{AppSubUrl}}/repo/fork/{{.Id}}"{{end}} {{if $.IsRepositoryOwner}}data-content="{{$.i18n.Tr "repo.fork_from_self"}}" data-position="top right"{{end}}>
 		  <i class="octicon octicon-repo-forked"></i>
 		  {{$.i18n.Tr "repo.fork"}} <span class="num">{{.NumForks}}</span>
 		</a>

+ 0 - 5
templates/repo/issue/alert.tmpl

@@ -1,5 +0,0 @@
-{{if .Flash}}
-<div class="sixteen wide center aligned centered column">
-	{{template "base/alert" .}}
-</div>
-{{end}}

+ 50 - 52
templates/repo/issue/labels.tmpl

@@ -1,77 +1,73 @@
 {{template "base/head" .}}
 <div class="repository labels">
 	{{template "repo/header" .}}
-	<div class="ui middle page grid body">
+	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
 			{{if .IsRepositoryAdmin}}
-			<form class="ui right form" action="{{$.RepoLink}}/labels/new" method="post">
+			<div class="ui right">
+				<div class="ui green new-label button">{{.i18n.Tr "repo.issues.new_label"}}</div>
+			</div>
+			{{end}}
+		</div>
+		<div class="ui new-label segment hide">
+			<form class="ui form" action="{{$.RepoLink}}/labels/new" method="post">
 				{{.CsrfTokenHtml}}
-				<div class="ui right floated secondary menu">
-					<div class="input item">
-						<div class="ui large input">
-						  <input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" required>
+				<div class="ui grid">
+					<div class="five wide column">
+						<div class="ui small input">
+				  		<input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required>
 						</div>
 					</div>
-					<div class="item">
-						<div class="ui large input">
-			        <input class="color-picker" name="color" value="#70c24a" required>
-			    	</div>
+					<div class="color picker column">
+		      	<input class="color-picker" name="color" value="#70c24a" required>
 					</div>
-					<div class="item precolors">
+					<div class="column precolors">
 						{{template "repo/issue/label_precolors"}}
 					</div>
-					<button class="ui green button">{{.i18n.Tr "repo.issues.new_label"}}</button>
+					<div class="buttons">
+						<div class="ui blue small basic cancel button">{{.i18n.Tr "repo.milestones.cancel"}}</div>
+						<button class="ui green small button">{{.i18n.Tr "repo.issues.create_label"}}</button>
+					</div>
 				</div>
 			</form>
-			{{end}}
 		</div>
 		<div class="ui divider"></div>
-		{{template "repo/issue/alert" .}}
-		<div class="ui left">
-			<div class="ui black label">{{.i18n.Tr "repo.issues.label_count" .NumLabels}}</div>
-		</div>
+		{{template "base/alert" .}}
+		<div class="ui black label">{{.i18n.Tr "repo.issues.label_count" .NumLabels}}</div>
 
-		<div class="sixteen wide column">
-			<div class="label list">
-				{{range .Labels}}
-				<li class="item">
-					<div class="ui label" style="background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div>
-					{{if $.IsRepositoryAdmin}}
-					<a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a>
-					<a class="ui right edit-label-button" href="#" data-id={{.ID}} data-title={{.Name}} data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
-					{{end}}
-					<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
-				</li>
+		<div class="label list">
+			{{range .Labels}}
+			<li class="item">
+				<div class="ui label" style="background-color: {{.Color}}"><i class="octicon octicon-tag"></i> {{.Name}}</div>
+				{{if $.IsRepositoryAdmin}}
+				<a class="ui right delete-button" href="#" data-url="{{$.RepoLink}}/labels/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a>
+				<a class="ui right edit-label-button" href="#" data-id={{.ID}} data-title={{.Name}} data-color={{.Color}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
 				{{end}}
-			</div>
+				<a class="ui right open-issues" href="{{$.RepoLink}}/issues?labels={{.ID}}"><i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.label_open_issues" .NumOpenIssues}}</a>
+			</li>
+			{{end}}
 		</div>
 	</div>
 </div>
 
 {{if .IsRepositoryAdmin}}
-<div class="ui basic delete modal">
-  <div class="header">
+<div class="ui small basic delete modal">
+  <div class="ui icon header">
+    <i class="trash icon"></i>
     {{.i18n.Tr "repo.issues.label_deletion"}}
   </div>
   <div class="content">
-    <div class="image">
-      <i class="trash icon"></i>
-    </div>
-    <div class="description">
-      <p>{{.i18n.Tr "repo.issues.label_deletion_desc"}}</p>
-    </div>
+    <p>{{.i18n.Tr "repo.issues.label_deletion_desc"}}</p>
   </div>
   <div class="actions">
-    <div class="two fluid ui inverted buttons">
-      <div class="ui red basic inverted button">
-        <i class="remove icon"></i>
-        {{.i18n.Tr "modal.no"}}
-      </div>
-      <div class="ui green basic inverted positive button">
-        <i class="checkmark icon"></i>
-        {{.i18n.Tr "modal.yes"}}
-      </div>
+    <div class="ui red basic inverted cancel button">
+      <i class="remove icon"></i>
+      {{.i18n.Tr "modal.no"}}
+    </div>
+    <div class="ui green basic inverted ok button">
+      <i class="checkmark icon"></i>
+      {{.i18n.Tr "modal.yes"}}
     </div>
   </div>
 </div>
@@ -84,14 +80,16 @@
 		<form class="ui edit-label form" action="{{$.RepoLink}}/labels/edit" method="post">
 			{{.CsrfTokenHtml}}
 			<input id="label-modal-id" name="id" type="hidden">
-			<div class="inline fields">
-				<div class="field">
-					<input id="label-modal-title" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" required>
+			<div class="ui grid">
+				<div class="five wide column">
+					<div class="ui small input">
+			  		<input class="new-label-input" name="title" placeholder="{{.i18n.Tr "repo.issues.new_label_placeholder"}}" autofocus required>
+					</div>
 				</div>
-				<div class="field">
-		      <input id="label-modal-color" class="color-picker" name="color" value="#70c24a" required>
+				<div class="color picker column">
+	      	<input class="color-picker" name="color" value="#70c24a" required>
 				</div>
-				<div class="field precolors">
+				<div class="column precolors">
 					{{template "repo/issue/label_precolors"}}
 				</div>
 			</div>

+ 59 - 63
templates/repo/issue/list.tmpl

@@ -1,28 +1,26 @@
 {{template "base/head" .}}
 <div class="repository">
 	{{template "repo/header" .}}
-	<div class="ui middle page grid body">
+	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
-			<div class="ui right floated secondary menu">
+			<div class="ui right">
 				<a class="ui green button" href="{{$.RepoLink}}/issues/new">{{.i18n.Tr "repo.issues.new"}}</a>
 			</div>
 		</div>
 		<div class="ui divider"></div>
-		<div class="ui left">
-			<div class="ui tiny buttons">
-			  <a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{$.ViewType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}">
-			  	<i class="octicon octicon-issue-opened"></i>
-			  	{{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}}
-			  </a>
-			  <a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}&state=closed&labels={{.SelectLabels}}&milestone={{.MilestoneID}}">
-			  	<i class="octicon octicon-issue-closed"></i>
-			  	{{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}}
-			  </a>
-			</div>
+		<div class="ui tiny buttons">
+		  <a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{$.ViewType}}&state=open&labels={{.SelectLabels}}&milestone={{.MilestoneID}}">
+		  	<i class="octicon octicon-issue-opened"></i>
+		  	{{.i18n.Tr "repo.issues.open_tab" .IssueStats.OpenCount}}
+		  </a>
+		  <a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/issues?type={{.ViewType}}&state=closed&labels={{.SelectLabels}}&milestone={{.MilestoneID}}">
+		  	<i class="octicon octicon-issue-closed"></i>
+		  	{{.i18n.Tr "repo.issues.close_tab" .IssueStats.ClosedCount}}
+		  </a>
 		</div>
 		<div class="ui right floated secondary filter menu">
-			<div class="ui {{if not .Labels}}disabled{{end}} pointing dropdown jump item">
+			<div class="ui {{if not .Labels}}disabled{{end}} dropdown jump item">
 				<span class="text">
 					{{.i18n.Tr "repo.issues.filter_label"}}
 					<i class="dropdown icon"></i>
@@ -34,7 +32,7 @@
           {{end}}
 				</div>
 			</div>
-			<div class="ui {{if not .Milestones}}disabled{{end}} pointing dropdown jump item">
+			<div class="ui {{if not .Milestones}}disabled{{end}} dropdown jump item">
 				<span class="text">
 					{{.i18n.Tr "repo.issues.filter_milestone"}}
 					<i class="dropdown icon"></i>
@@ -46,7 +44,7 @@
           {{end}}
 				</div>
 			</div>
-			<!-- <div class="ui {{if not .Assignees}}disabled{{end}} pointing dropdown jump item">
+			<!-- <div class="ui {{if not .Assignees}}disabled{{end}} dropdown jump item">
 				<span class="text">
 					{{.i18n.Tr "repo.issues.filter_assignee"}}
 					<i class="dropdown icon"></i>
@@ -57,7 +55,7 @@
           {{end}}
 				</div>
 			</div> -->
-			<div class="ui pointing dropdown type jump item">
+			<div class="ui dropdown type jump item">
 				<span class="text">
 					{{.i18n.Tr "repo.issues.filter_type"}}
 					<i class="dropdown icon"></i>
@@ -71,57 +69,55 @@
 			</div>
 		</div>
 
-		<div class="sixteen wide column">
-			<div class="issue list">
-				{{range .Issues}}
-				{{ $timeStr:= TimeSince .Created $.Lang }}
-	      <li class="item">
-	      	<div class="ui {{if .IsRead}}black{{else}}green{{end}} label">#{{.Index}}</div>
-	      	<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Name}}</a>
+		<div class="issue list">
+			{{range .Issues}}
+			{{ $timeStr:= TimeSince .Created $.Lang }}
+      <li class="item">
+      	<div class="ui {{if .IsRead}}black{{else}}green{{end}} label">#{{.Index}}</div>
+      	<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Name}}</a>
 
-	      	{{range .Labels}}
-					<a class="ui label" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}" style="background-color: {{.Color}}">{{.Name}}</a>
-	      	{{end}}
+      	{{range .Labels}}
+				<a class="ui label" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{.ID}}&milestone={{$.MilestoneID}}" style="background-color: {{.Color}}">{{.Name}}</a>
+      	{{end}}
 
-	      	{{if .NumComments}}
-	      	<span class="comment ui right"><i class="octicon octicon-comment"></i> {{.NumComments}}</span>
-	      	{{end}}
+      	{{if .NumComments}}
+      	<span class="comment ui right"><i class="octicon octicon-comment"></i> {{.NumComments}}</span>
+      	{{end}}
 
-	        <p class="desc">
-	        	{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.Name|Str2html}}
-		        {{if .Milestone}}
-						{{with .Milestone}}
-						<a class="milestone" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}">
-							<span class="octicon octicon-milestone"></span> {{.Name}}
-						</a>
-						{{end}}
-		        {{end}}
-	        </p>
-	      </li>
-	      {{end}}
-				
-				{{with .Page}}
-				{{if gt .TotalPages 1}}
-				<div class="center page buttons">
-					<div class="ui borderless pagination menu">
-					  <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Previous}}"{{end}}>
-					    <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
-					  </a>
-						{{range .Pages}}
-						{{if eq .Num -1}}
-						<a class="disabled item">...</a>
-						{{else}}
-						<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Num}}"{{end}}>{{.Num}}</a>
-						{{end}}
-						{{end}}
-					  <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Next}}"{{end}}>
-					    {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
-					  </a>
-					</div>
+        <p class="desc">
+        	{{$.i18n.Tr "repo.issues.opened_by" $timeStr .Poster.Name|Str2html}}
+	        {{if .Milestone}}
+					{{with .Milestone}}
+					<a class="milestone" href="{{$.RepoLink}}/issues?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{.ID}}">
+						<span class="octicon octicon-milestone"></span> {{.Name}}
+					</a>
+					{{end}}
+	        {{end}}
+        </p>
+      </li>
+      {{end}}
+			
+			{{with .Page}}
+			{{if gt .TotalPages 1}}
+			<div class="center page buttons">
+				<div class="ui borderless pagination menu">
+				  <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Previous}}"{{end}}>
+				    <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
+				  </a>
+					{{range .Pages}}
+					{{if eq .Num -1}}
+					<a class="disabled item">...</a>
+					{{else}}
+					<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Num}}"{{end}}>{{.Num}}</a>
+					{{end}}
+					{{end}}
+				  <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?type={{$.ViewType}}&state={{$.State}}&labels={{$.SelectLabels}}&milestone={{$.MilestoneID}}&page={{.Next}}"{{end}}>
+				    {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
+				  </a>
 				</div>
-				{{end}}
-				{{end}}
 			</div>
+			{{end}}
+			{{end}}
 		</div>
 	</div>
 </div>

+ 20 - 22
templates/repo/issue/milestone_new.tmpl

@@ -1,7 +1,7 @@
 {{template "base/head" .}}
 <div class="repository new milestone">
 	{{template "repo/header" .}}
-	<div class="ui middle page grid body">
+	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
 			{{if and .IsRepositoryAdmin .PageIsEditMilestone}}
@@ -11,7 +11,6 @@
 			{{end}}
 		</div>
 		<div class="ui divider"></div>
-		<div class="sixteen wide column page grid">
 			<h2 class="ui dividing header">
 				{{if .PageIsEditMilestone}}
 			  {{.i18n.Tr "repo.milestones.edit"}}
@@ -21,13 +20,9 @@
 			  <div class="sub header">{{.i18n.Tr "repo.milestones.new_subheader"}}</div>
 			  {{end}}
 			</h2>
+      {{template "base/alert" .}}
 			<form class="ui form grid" action="{{.Link}}" method="post">
         {{.CsrfTokenHtml}}
-        {{if .Flash}}
-				<div class="sixteen wide column">
-        	{{template "base/alert" .}}
-        </div>
-        {{end}}
 				<div class="eleven wide column">
 					<div class="field {{if .Err_Title}}error{{end}}">
 						<label>{{.i18n.Tr "repo.milestones.title"}}</label>
@@ -38,7 +33,7 @@
 						<textarea name="content">{{.content}}</textarea>
 					</div>
 				</div>
-				<div class="three wide column">
+				<div class="four wide column">
 					<div class="field {{if .Err_Deadline}}error{{end}}">
 						<label>
 							{{.i18n.Tr "repo.milestones.due_date"}}
@@ -50,21 +45,24 @@
 						<input class="milestone datepicker" data-lang="{{.DateLang}}" data-start-date="{{.deadline}}">
 					</div>
 				</div>
-				<div class="ui divider"></div>
-				{{if .PageIsEditMilestone}}
-				<button class="ui right green button">
-					{{.i18n.Tr "repo.milestones.modify"}}
-				</button>
-				<a class="ui right blue basic button" href="{{.RepoLink}}/milestones">
-					{{.i18n.Tr "repo.milestones.cancel"}}
-				</a>
-				{{else}}
-				<button class="ui right green button">
-					{{.i18n.Tr "repo.milestones.create"}}
-				</button>
-				{{end}}
+				<div class="ui container">
+					<div class="ui divider"></div>
+					<div class="ui right">
+						{{if .PageIsEditMilestone}}
+						<a class="ui blue basic button" href="{{.RepoLink}}/milestones">
+							{{.i18n.Tr "repo.milestones.cancel"}}
+						</a>
+						<button class="ui green button">
+							{{.i18n.Tr "repo.milestones.modify"}}
+						</button>
+						{{else}}
+						<button class="ui green button">
+							{{.i18n.Tr "repo.milestones.create"}}
+						</button>
+						{{end}}
+					</div>
+				</div>
 			</form>
-		</div>
 	</div>
 </div>
 {{template "base/footer" .}}

+ 81 - 91
templates/repo/issue/milestones.tmpl

@@ -1,125 +1,115 @@
 {{template "base/head" .}}
 <div class="repository milestones">
 	{{template "repo/header" .}}
-	<div class="ui middle page grid body">
+	<div class="ui container">
 		<div class="navbar">
 			{{template "repo/issue/navbar" .}}
 			{{if .IsRepositoryAdmin}}
-			<div class="ui right floated secondary menu">
+			<div class="ui right">
 				<a class="ui green button" href="{{$.Link}}/new">{{.i18n.Tr "repo.milestones.new"}}</a>
 			</div>
 			{{end}}
 		</div>
 		<div class="ui divider"></div>
-		{{template "repo/issue/alert" .}}
-		<div class="ui left">
-			<div class="ui tiny buttons">
-			  <a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/milestones?state=open">
-			  	<i class="octicon octicon-milestone"></i>
-			  	{{.i18n.Tr "repo.milestones.open_tab" .OpenCount}}
-			  </a>
-			  <a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/milestones?state=closed">
-			  	<i class="octicon octicon-milestone"></i>
-			  	{{.i18n.Tr "repo.milestones.close_tab" .ClosedCount}}
-			  </a>
-			</div>
+		{{template "base/alert" .}}
+		<div class="ui tiny buttons">
+		  <a class="ui green basic button {{if not .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/milestones?state=open">
+		  	<i class="octicon octicon-milestone"></i>
+		  	{{.i18n.Tr "repo.milestones.open_tab" .OpenCount}}
+		  </a>
+		  <a class="ui red basic button {{if .IsShowClosed}}active{{end}}" href="{{.RepoLink}}/milestones?state=closed">
+		  	<i class="octicon octicon-milestone"></i>
+		  	{{.i18n.Tr "repo.milestones.close_tab" .ClosedCount}}
+		  </a>
 		</div>
 		
-		<div class="sixteen wide column">
-			<div class="milestone list">
-				{{range .Milestones}}
-				<li class="item">
-					<i class="octicon octicon-milestone"></i> <a href="{{$.RepoLink}}/issues?state={{$.State}}&milestone={{.ID}}">{{.Name}}</a>
-					<div class="ui right blue progress" data-percent="{{.Completeness}}">
-				    <div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}>
-				      <div class="progress"></div>
-				    </div>
-					</div>
-					<div class="meta">
-						{{ $closedDate:= TimeSince .ClosedDate $.Lang }}
-						{{if .IsClosed}}
-							<span class="octicon octicon-clock"></span> {{$.i18n.Tr "repo.milestones.closed" $closedDate|Str2html}}
-						{{else}}
-							<span class="octicon octicon-calendar"></span> 
-							{{if .DeadlineString}}
-							<span {{if .IsOverDue}}class="overdue"{{end}}>{{.DeadlineString}}</span>
-							{{else}}
-							{{$.i18n.Tr "repo.milestones.no_due_date"}}
-							{{end}}
-						{{end}}
-						<span class="issue-stats">
-							<i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_tab" .NumOpenIssues}}
-							<i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}}
-						</span>
-					</div>
-					{{if $.IsRepositoryAdmin}}
-					<div class="ui right operate">
-						<a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
-						{{if .IsClosed}}
-						<a href="{{$.Link}}/{{.ID}}/open" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-check"></i> {{$.i18n.Tr "repo.milestones.open"}}</a>
+		<div class="milestone list">
+			{{range .Milestones}}
+			<li class="item">
+				<i class="octicon octicon-milestone"></i> <a href="{{$.RepoLink}}/issues?state={{$.State}}&milestone={{.ID}}">{{.Name}}</a>
+				<div class="ui right green progress" data-percent="{{.Completeness}}">
+			    <div class="bar" {{if not .Completeness}}style="background-color: transparent"{{end}}>
+			      <div class="progress"></div>
+			    </div>
+				</div>
+				<div class="meta">
+					{{ $closedDate:= TimeSince .ClosedDate $.Lang }}
+					{{if .IsClosed}}
+						<span class="octicon octicon-clock"></span> {{$.i18n.Tr "repo.milestones.closed" $closedDate|Str2html}}
+					{{else}}
+						<span class="octicon octicon-calendar"></span> 
+						{{if .DeadlineString}}
+						<span {{if .IsOverDue}}class="overdue"{{end}}>{{.DeadlineString}}</span>
 						{{else}}
-						<a href="{{$.Link}}/{{.ID}}/close" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-x"></i> {{$.i18n.Tr "repo.milestones.close"}}</a>
+						{{$.i18n.Tr "repo.milestones.no_due_date"}}
 						{{end}}
-						<a class="delete-button" href="#" data-url="{{$.RepoLink}}/milestones/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a>
-					</div>
-					{{if .Content}}
-					<div class="content">
-						{{.RenderedContent|Str2html}}
-					</div>
 					{{end}}
+					<span class="issue-stats">
+						<i class="octicon octicon-issue-opened"></i> {{$.i18n.Tr "repo.issues.open_tab" .NumOpenIssues}}
+						<i class="octicon octicon-issue-closed"></i> {{$.i18n.Tr "repo.issues.close_tab" .NumClosedIssues}}
+					</span>
+				</div>
+				{{if $.IsRepositoryAdmin}}
+				<div class="ui right operate">
+					<a href="{{$.Link}}/{{.ID}}/edit" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-pencil"></i> {{$.i18n.Tr "repo.issues.label_edit"}}</a>
+					{{if .IsClosed}}
+					<a href="{{$.Link}}/{{.ID}}/open" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-check"></i> {{$.i18n.Tr "repo.milestones.open"}}</a>
+					{{else}}
+					<a href="{{$.Link}}/{{.ID}}/close" data-id={{.ID}} data-title={{.Name}}><i class="octicon octicon-x"></i> {{$.i18n.Tr "repo.milestones.close"}}</a>
 					{{end}}
-				</li>
-				{{end}}
-				
-				{{with .Page}}
-				{{if gt .TotalPages 1}}
-				<div class="center page buttons">
-					<div class="ui borderless pagination menu">
-					  <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?state={{$.State}}&page={{.Previous}}"{{end}}>
-					    <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
-					  </a>
-						{{range .Pages}}
-						{{if eq .Num -1}}
-						<a class="disabled item">...</a>
-						{{else}}
-						<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?state={{$.State}}&page={{.Num}}"{{end}}>{{.Num}}</a>
-						{{end}}
-						{{end}}
-					  <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?state={{$.State}}&page={{.Next}}"{{end}}>
-					    {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
-					  </a>
-					</div>
+					<a class="delete-button" href="#" data-url="{{$.RepoLink}}/milestones/delete" data-id="{{.ID}}"><i class="octicon octicon-trashcan"></i> {{$.i18n.Tr "repo.issues.label_delete"}}</a>
+				</div>
+				{{if .Content}}
+				<div class="content">
+					{{.RenderedContent|Str2html}}
 				</div>
 				{{end}}
 				{{end}}
+			</li>
+			{{end}}
+			
+			{{with .Page}}
+			{{if gt .TotalPages 1}}
+			<div class="center page buttons">
+				<div class="ui borderless pagination menu">
+				  <a class="{{if not .HasPrevious}}disabled{{end}} item" {{if .HasPrevious}}href="{{$.Link}}?state={{$.State}}&page={{.Previous}}"{{end}}>
+				    <i class="left arrow icon"></i> {{$.i18n.Tr "repo.issues.previous"}}
+				  </a>
+					{{range .Pages}}
+					{{if eq .Num -1}}
+					<a class="disabled item">...</a>
+					{{else}}
+					<a class="{{if .IsCurrent}}active{{end}} item" {{if not .IsCurrent}}href="{{$.Link}}?state={{$.State}}&page={{.Num}}"{{end}}>{{.Num}}</a>
+					{{end}}
+					{{end}}
+				  <a class="{{if not .HasNext}}disabled{{end}} item" {{if .HasNext}}href="{{$.Link}}?state={{$.State}}&page={{.Next}}"{{end}}>
+				    {{$.i18n.Tr "repo.issues.next"}} <i class="icon right arrow"></i>
+				  </a>
+				</div>
 			</div>
+			{{end}}
+			{{end}}
 		</div>
 	</div>
 </div>
 
 {{if .IsRepositoryAdmin}}
-<div class="ui basic delete modal">
-  <div class="header">
+<div class="ui small basic delete modal">
+  <div class="ui icon header">
+    <i class="trash icon"></i>
     {{.i18n.Tr "repo.milestones.deletion"}}
   </div>
   <div class="content">
-    <div class="image">
-      <i class="trash icon"></i>
-    </div>
-    <div class="description">
-      <p>{{.i18n.Tr "repo.milestones.deletion_desc"}}</p>
-    </div>
+    <p>{{.i18n.Tr "repo.milestones.deletion_desc"}}</p>
   </div>
   <div class="actions">
-    <div class="two fluid ui inverted buttons">
-      <div class="ui red basic inverted button">
-        <i class="remove icon"></i>
-        {{.i18n.Tr "modal.no"}}
-      </div>
-      <div class="ui green basic inverted positive button">
-        <i class="checkmark icon"></i>
-        {{.i18n.Tr "modal.yes"}}
-      </div>
+    <div class="ui red basic inverted cancel button">
+      <i class="remove icon"></i>
+      {{.i18n.Tr "modal.no"}}
+    </div>
+    <div class="ui green basic inverted ok button">
+      <i class="checkmark icon"></i>
+      {{.i18n.Tr "modal.yes"}}
     </div>
   </div>
 </div>

+ 4 - 6
templates/repo/issue/navbar.tmpl

@@ -1,7 +1,5 @@
-<div class="ui left">
-	<div class="ui compact menu">
-	  <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
-	  <a class="{{if .PageIsLabels}}active{{end}} item" href="{{.RepoLink}}/labels">{{.i18n.Tr "repo.labels"}}</a>
-	  <a class="{{if .PageIsMilestones}}active{{end}} item" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a>
-	</div>
+<div class="ui compact small menu">
+  <a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">{{.i18n.Tr "repo.issues"}}</a>
+  <a class="{{if .PageIsLabels}}active{{end}} item" href="{{.RepoLink}}/labels">{{.i18n.Tr "repo.labels"}}</a>
+  <a class="{{if .PageIsMilestones}}active{{end}} item" href="{{.RepoLink}}/milestones">{{.i18n.Tr "repo.milestones"}}</a>
 </div>

+ 3 - 1
templates/repo/issue/view.tmpl

@@ -69,7 +69,9 @@
                                 <a href="{{AppSubUrl}}/{{.Poster.Name}}" class="user">{{.Poster.Name}}</a> commented <span class="time">{{TimeSince .Created $.Lang}}</span>
                                 <!-- <a class="issue-comment-del pull-right issue-action" href="#" title="Edit Comment"><i class="fa fa-times-circle"></i></a>
                                 <a class="issue-comment-edit pull-right issue-action" href="#" title="Remove Comment" data-url="{remove-link}"><i class="fa fa-edit"></i></a> -->
+                                {{if eq .Poster.Id $.Owner.Id}}
                                 <span class="role label label-default pull-right">Owner</span>
+                                {{end}}
                             </div>
                             <div class="panel-body markdown">
                                 {{if len .Content}}
@@ -251,7 +253,7 @@
                     <h4>Milestone</h4>
                     {{if .Milestone}}
                     <p class="completion{{if eq .Milestone.Completeness 0}} hidden{{end}}"><span style="width:{{.Milestone.Completeness}}%">&nbsp;</span></p>
-                    <p class="name"><strong><a href="{{$.RepoLink}}/issues?milestone={{.Milestone.Index}}{{if $.Issue.IsClosed}}&state=closed{{end}}">{{.Milestone.Name}}</a></strong></p>
+                    <p class="name"><strong><a href="{{$.RepoLink}}/issues?milestone={{.Milestone.ID}}{{if $.Issue.IsClosed}}&state=closed{{end}}">{{.Milestone.Name}}</a></strong></p>
                     {{else}}
                     <p class="name">No milestone</p>
                     {{end}}

+ 3 - 3
templates/repo/pulls/fork.tmpl

@@ -14,19 +14,19 @@
 			      <div class="ui selection dropdown">
 			        <input type="hidden" id="uid" name="uid" value="{{.ContextUser.Id}}" required>
 			        <span class="text">
-			        	<img class="ui mini avatar image" src="{{.ContextUser.AvatarLink}}">
+			        	<img class="ui mini image" src="{{.ContextUser.AvatarLink}}">
             		{{.ContextUser.Name}}
             	</span>
 			        <i class="dropdown icon"></i>
 			        <div class="menu">
 			        	<div class="item" data-value="{{.SignedUser.Id}}">
-				        	<img class="ui mini avatar image" src="{{.SignedUser.AvatarLink}}">
+				        	<img class="ui mini image" src="{{.SignedUser.AvatarLink}}">
 	            		{{.SignedUser.Name}}
 			        	</div>
 			        	{{range .Orgs}}
 			        	{{if .IsOwnedBy $.SignedUser.Id}}
 			        	<div class="item" data-value="{{.Id}}">
-				        	<img class="ui mini avatar image" src="{{.AvatarLink}}">
+				        	<img class="ui mini image" src="{{.AvatarLink}}">
 	            		{{.Name}}
 			        	</div>
 			        	{{end}}

+ 17 - 21
templates/repo/settings/deploy_keys.tmpl

@@ -1,16 +1,17 @@
 {{template "base/head" .}}
 <div class="repository settings">
 	{{template "repo/header" .}}
-	<div class="ui page grid">
+	<div class="ui container">
+	<div class="ui grid">
 		{{template "repo/settings/navbar" .}}
 		<div class="twelve wide column content">
 			{{template "base/alert" .}}
-			<h4 class="ui top attached header">
+			<h3 class="ui top attached header">
 			  {{.i18n.Tr "repo.settings.deploy_keys"}}
 			  <div class="ui right">
 			  	<div id="add-deploy-key" class="ui blue tiny button">{{.i18n.Tr "repo.settings.add_deploy_key"}}</div>
 			  </div>
-			</h4>
+			</h3>
 			<div class="ui attached segment">
 				{{if .Deploykeys}}
 				<div class="ui key list">
@@ -31,7 +32,7 @@
 								<i>{{$.i18n.Tr "settings.add_on"}} <span>{{DateFmtShort .Created}}</span> —  <i class="octicon octicon-info"></i> {{if .HasUsed}}{{$.i18n.Tr "settings.last_used"}} <span>{{DateFmtShort .Updated}}</span>{{else}}{{$.i18n.Tr "settings.no_activity"}}{{end}}</i>
 							</div>
 						</div>
-						<div class="three wide column">
+						<div class="two wide column">
 							<button class="ui red tiny button delete-button" data-url="{{$.Link}}/delete" data-id="{{.ID}}">
 								{{$.i18n.Tr "settings.delete_key"}}
 							</button>
@@ -67,30 +68,25 @@
 			</div>
 		</div>
 	</div>
+	</div>
 </div>
 
-<div class="ui basic delete modal">
-  <div class="header">
+<div class="ui small basic delete modal">
+  <div class="ui icon header">
+    <i class="trash icon"></i>
     {{.i18n.Tr "repo.settings.deploy_key_deletion"}}
   </div>
   <div class="content">
-    <div class="image">
-      <i class="trash icon"></i>
-    </div>
-    <div class="description">
-      <p>{{.i18n.Tr "repo.settings.deploy_key_deletion_desc"}}</p>
-    </div>
+    <p>{{.i18n.Tr "repo.settings.deploy_key_deletion_desc"}}</p>
   </div>
   <div class="actions">
-    <div class="two fluid ui inverted buttons">
-      <div class="ui red basic inverted button">
-        <i class="remove icon"></i>
-        {{.i18n.Tr "modal.no"}}
-      </div>
-      <div class="ui green basic inverted positive button">
-        <i class="checkmark icon"></i>
-        {{.i18n.Tr "modal.yes"}}
-      </div>
+    <div class="ui red basic inverted cancel button">
+      <i class="remove icon"></i>
+      {{.i18n.Tr "modal.no"}}
+    </div>
+    <div class="ui green basic inverted ok button">
+      <i class="checkmark icon"></i>
+      {{.i18n.Tr "modal.yes"}}
     </div>
   </div>
 </div>

+ 1 - 0
templates/repo/settings/navbar.tmpl

@@ -1,5 +1,6 @@
 <div class="four wide column">
 	<div class="ui vertical menu">
+		<div class="header item">{{.i18n.Tr "repo.settings"}}</div>
 	  <a class="{{if .PageIsSettingsOptions}}active{{end}} item" href="{{.RepoLink}}/settings">
 	    {{.i18n.Tr "repo.settings.options"}}
 	  </a>

+ 1 - 1
templates/user/dashboard/feeds.tmpl

@@ -30,7 +30,7 @@
                 {{range $push.Commits}}
                 <li><img class="avatar-16" src="{{AvatarLink .AuthorEmail}}?s=16"> <a href="{{$repoLink}}/commit/{{.Sha1}}">{{ShortSha .Sha1}}</a> <span class="text-truncate grid-4-5">{{.Message}}</span></li>
                 {{end}}
-                {{if $push.CompareUrl}}<li><a href="{{$.AppSubUrl}}/{{$push.CompareUrl}}">{{$.i18n.Tr "action.compare_2_commits"}} »</a></li>{{end}}
+                {{if $push.CompareUrl}}<li><a href="{{AppSubUrl}}/{{$push.CompareUrl}}">{{$.i18n.Tr "action.compare_2_commits"}} »</a></li>{{end}}
             </ul>
         </div>
         {{else if eq .GetOpType 6}}

部分文件因文件數量過多而無法顯示