# Conflicts: # modules/bindata/bindata.go # public/css/gogs.min.css # templates/repo/header.tmpltags/v1.2.0-rc1
@@ -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 | |||
@@ -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 | |||
} | |||
@@ -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 | |||
} | |||
@@ -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 | |||
@@ -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 | |||
@@ -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, | |||
)) | |||
} |
@@ -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" |
@@ -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) | |||
} |
@@ -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) | |||
} | |||
} |
@@ -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 | |||
} |
@@ -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 () { | |||
@@ -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)); | |||
} | |||
@@ -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; | |||
} | |||
} | |||
} | |||
} |
@@ -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> | |||
@@ -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> | |||
@@ -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> | |||
@@ -1,5 +0,0 @@ | |||
{{if .Flash}} | |||
<div class="sixteen wide center aligned centered column"> | |||
{{template "base/alert" .}} | |||
</div> | |||
{{end}} |
@@ -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> | |||
@@ -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> | |||
@@ -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" .}} |
@@ -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> | |||
@@ -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> |
@@ -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}}%"> </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}} | |||
@@ -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}} | |||
@@ -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,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> | |||
@@ -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}} | |||