Browse Source

Merge branch 'V20211115' into fix-674

tags/v1.21.12.1
zouap 3 years ago
parent
commit
2420328814
32 changed files with 690 additions and 147 deletions
  1. +1
    -0
      models/org.go
  2. +22
    -1
      models/repo.go
  3. +15
    -13
      modules/setting/setting.go
  4. +24
    -9
      modules/storage/obs.go
  5. +2
    -0
      options/locale/locale_en-US.ini
  6. +3
    -0
      options/locale/locale_zh-CN.ini
  7. +41
    -0
      public/img/git-logo.svg
  8. +0
    -0
      routers/org/members.go
  9. +6
    -0
      routers/repo/http.go
  10. +14
    -0
      routers/repo/user_data_analysis.go
  11. +120
    -36
      routers/repo/view.go
  12. +10
    -3
      routers/routes/routes.go
  13. +4
    -4
      routers/user/auth.go
  14. +2
    -2
      templates/base/head.tmpl
  15. +2
    -2
      templates/base/head_home.tmpl
  16. +8
    -0
      templates/base/head_navbar.tmpl
  17. +6
    -5
      templates/org/home.tmpl
  18. +6
    -6
      templates/org/navber.tmpl
  19. +13
    -3
      templates/repo/activity.tmpl
  20. +9
    -0
      templates/repo/contributors.tmpl
  21. +31
    -28
      templates/repo/header.tmpl
  22. +13
    -7
      templates/repo/home.tmpl
  23. +5
    -1
      templates/repo/release/list.tmpl
  24. +15
    -0
      templates/repo/view_file.tmpl
  25. +7
    -0
      templates/repo/wiki/start.tmpl
  26. +9
    -1
      templates/repo/wiki/view.tmpl
  27. +109
    -0
      web_src/js/components/Contributors.vue
  28. +13
    -15
      web_src/js/components/EditAboutInfo.vue
  29. +15
    -10
      web_src/js/components/basic/editDialog.vue
  30. +74
    -0
      web_src/js/features/letteravatar.js
  31. +18
    -1
      web_src/js/index.js
  32. +73
    -0
      web_src/less/openi.less

+ 1
- 0
models/org.go View File

@@ -182,6 +182,7 @@ func CreateOrganization(org, owner *User) (err error) {
if _, err = sess.Insert(&OrgUser{
UID: owner.ID,
OrgID: org.ID,
IsPublic: setting.Service.DefaultOrgMemberVisible,
}); err != nil {
return fmt.Errorf("insert org-user relation: %v", err)
}


+ 22
- 1
models/repo.go View File

@@ -210,9 +210,12 @@ type Repository struct {
Balance string `xorm:"NOT NULL DEFAULT '0'"`
BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"`

// git clone total count
// git clone and git pull total count
CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"`

// only git clone total count
GitCloneCnt int64 `xorm:"NOT NULL DEFAULT 0"`

CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`

@@ -2473,6 +2476,24 @@ func (repo *Repository) IncreaseCloneCnt() {
return
}

func (repo *Repository) IncreaseGitCloneCnt() {
sess := x.NewSession()
defer sess.Close()

if err := sess.Begin(); err != nil {
return
}
if _, err := sess.Exec("UPDATE `repository` SET git_clone_cnt = git_clone_cnt + 1 WHERE id = ?", repo.ID); err != nil {
return
}

if err := sess.Commit(); err != nil {
return
}

return
}

func UpdateRepositoryCommitNum(repo *Repository) error {
if _, err := x.Exec("UPDATE `repository` SET num_commit = ? where id = ?", repo.NumCommit, repo.ID); err != nil {
return err


+ 15
- 13
modules/setting/setting.go View File

@@ -163,6 +163,7 @@ var (
// UI settings
UI = struct {
ExplorePagingNum int
ContributorPagingNum int
IssuePagingNum int
RepoSearchPagingNum int
MembersPagingNum int
@@ -203,19 +204,20 @@ var (
Keywords string
} `ini:"ui.meta"`
}{
ExplorePagingNum: 20,
IssuePagingNum: 10,
RepoSearchPagingNum: 10,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
GraphMaxCommitNum: 100,
CodeCommentLines: 4,
ReactionMaxUserNum: 10,
ThemeColorMetaTag: `#6cc644`,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea`,
Themes: []string{`gitea`, `arc-green`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
ExplorePagingNum: 20,
ContributorPagingNum: 50,
IssuePagingNum: 10,
RepoSearchPagingNum: 10,
MembersPagingNum: 20,
FeedMaxCommitNum: 5,
GraphMaxCommitNum: 100,
CodeCommentLines: 4,
ReactionMaxUserNum: 10,
ThemeColorMetaTag: `#6cc644`,
MaxDisplayFileSize: 8388608,
DefaultTheme: `gitea`,
Themes: []string{`gitea`, `arc-green`},
Reactions: []string{`+1`, `-1`, `laugh`, `hooray`, `confused`, `heart`, `rocket`, `eyes`},
Notification: struct {
MinTimeout time.Duration
TimeoutStep time.Duration


+ 24
- 9
modules/storage/obs.go View File

@@ -10,11 +10,11 @@ import (
"strconv"
"strings"

"github.com/unknwon/com"

"code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/obs"
"code.gitea.io/gitea/modules/setting"

"github.com/unknwon/com"
)

type FileInfo struct {
@@ -178,30 +178,45 @@ func GetObsListObject(jobName, parentDir string) ([]FileInfo, error) {
input := &obs.ListObjectsInput{}
input.Bucket = setting.Bucket
input.Prefix = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir), "/")
strPrefix := strings.Split(input.Prefix, "/")
output, err := ObsCli.ListObjects(input)
fileInfos := make([]FileInfo, 0)
if err == nil {
for _, val := range output.Contents {
str1 := strings.Split(val.Key, "/")
var isDir bool
var fileName,nextParentDir string
var fileName, nextParentDir string
if strings.HasSuffix(val.Key, "/") {
//dirs in next level dir
if len(str1)-len(strPrefix) > 2 {
continue
}
fileName = str1[len(str1)-2]
isDir = true
nextParentDir = fileName
if fileName == parentDir || (fileName + "/") == setting.OutPutPath {
if parentDir == "" {
nextParentDir = fileName
} else {
nextParentDir = parentDir + "/" + fileName
}

if fileName == strPrefix[len(strPrefix)-1] || (fileName+"/") == setting.OutPutPath {
continue
}
} else {
//files in next level dir
if len(str1)-len(strPrefix) > 1 {
continue
}
fileName = str1[len(str1)-1]
isDir = false
nextParentDir = parentDir
}

fileInfo := FileInfo{
ModTime: val.LastModified.Format("2006-01-02 15:04:05"),
ModTime: val.LastModified.Local().Format("2006-01-02 15:04:05"),
FileName: fileName,
Size: val.Size,
IsDir:isDir,
Size: val.Size,
IsDir: isDir,
ParenDir: nextParentDir,
}
fileInfos = append(fileInfos, fileInfo)
@@ -242,7 +257,7 @@ func GetObsCreateSignedUrl(jobName, parentDir, fileName string) (string, error)
input := &obs.CreateSignedUrlInput{}
input.Bucket = setting.Bucket
input.Key = strings.TrimPrefix(path.Join(setting.TrainJobModelPath, jobName, setting.OutPutPath, parentDir, fileName), "/")
input.Expires = 60 * 60
input.Method = obs.HttpMethodGet



+ 2
- 0
options/locale/locale_en-US.ini View File

@@ -218,6 +218,7 @@ show_only_private = Showing only private
show_only_public = Showing only public

issues.in_your_repos = In your repositories
contributors = Contributors

[explore]
repos = Repositories
@@ -755,6 +756,7 @@ unit_disabled = The site administrator has disabled this repository section.
language_other = Other
datasets = Datasets
datasets.desc = Enable Dataset
cloudbrain_helper=Use GPU/NPU resources to open notebooks, model training tasks, etc.

debug=Debug
stop=Stop


+ 3
- 0
options/locale/locale_zh-CN.ini View File

@@ -220,6 +220,8 @@ show_only_public=只显示公开的

issues.in_your_repos=属于该用户项目的

contributors=贡献者

[explore]
repos=项目
users=用户
@@ -757,6 +759,7 @@ unit_disabled=站点管理员已禁用此项目单元。
language_other=其它
datasets=数据集
datasets.desc=数据集功能
cloudbrain_helper=使用GPU/NPU资源,开启Notebook、模型训练任务等

debug=调试
stop=停止


+ 41
- 0
public/img/git-logo.svg View File

@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.4.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 96 17.3" style="enable-background:new 0 0 96 17.3;" xml:space="preserve">
<style type="text/css">
.st0{fill:#5BB973;}
</style>
<g>
<path class="st0" d="M5.9,12.5l-1,3.8h-3L6.6,1.5H10l4.7,14.8h-3.1l-1-3.8H5.9z M6.5,10.2H10L9.6,8.5C9.4,7.8,9.1,7,8.9,6.1
C8.7,5.3,8.5,4.5,8.3,3.7H8.2C8,4.5,7.8,5.3,7.6,6.2S7.2,7.8,6.9,8.5L6.5,10.2z"/>
<path class="st0" d="M19.5,1.5v14.8h-3V1.5H19.5z"/>
<path class="st0" d="M27.7,6.6v10.6h-2.1V6.6h-1.8v-2h1.8V0.3h2.1v4.3h1.7v2H27.7z M31.6,7.4c-0.2,0.9-0.5,1.8-0.8,2.7
c-0.3,0.9-0.7,1.6-1.1,2.2c-0.1-0.1-0.2-0.2-0.4-0.3s-0.3-0.2-0.4-0.3c-0.2-0.1-0.3-0.2-0.5-0.3c-0.2-0.1-0.3-0.2-0.4-0.2
c0.4-0.5,0.7-1.1,1-1.9s0.5-1.5,0.6-2.3L31.6,7.4z M34.6,5.8c0,0.9-0.1,1.9-0.3,2.9c-0.1,1-0.4,2-0.7,3s-0.8,2-1.4,2.9
c-0.6,0.9-1.4,1.8-2.4,2.6c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.3-0.3-0.4-0.4c-0.1-0.1-0.3-0.3-0.4-0.4s-0.3-0.2-0.4-0.3
c0.9-0.7,1.6-1.5,2.1-2.3c0.6-0.8,1-1.7,1.3-2.5c0.3-0.9,0.5-1.7,0.7-2.6s0.2-1.7,0.3-2.5h-2.7V3.7h2.7V0.5h2v3.2h3.7
c0,0.1,0,0.3,0,0.4c0,0.1,0,0.2,0,0.3s0,0.2,0,0.3l-0.1,2.7l1.3-0.3c0.1,0.4,0.3,0.9,0.4,1.3c0.1,0.5,0.3,0.9,0.4,1.4
c0.1,0.5,0.2,0.9,0.3,1.3c0.1,0.4,0.2,0.8,0.2,1.1L39,12.8c-0.1-0.5-0.2-1.1-0.3-1.8s-0.3-1.4-0.5-2.1c0,1.2-0.1,2.1-0.1,3
c0,0.8-0.1,1.5-0.2,2.1c-0.1,0.6-0.1,1-0.2,1.4c-0.1,0.3-0.2,0.6-0.3,0.8c-0.2,0.3-0.4,0.5-0.7,0.7c-0.2,0.1-0.5,0.2-0.8,0.3
C35.6,17,35.3,17,34.8,17s-0.9,0-1.3,0c0-0.3-0.1-0.6-0.2-1c-0.1-0.4-0.3-0.7-0.4-1c0.4,0,0.9,0.1,1.2,0.1c0.4,0,0.7,0,0.8,0
c0.2,0,0.3,0,0.4-0.1c0.1,0,0.2-0.1,0.3-0.2c0.1-0.1,0.2-0.4,0.3-0.8c0.1-0.4,0.1-0.9,0.2-1.6s0.1-1.6,0.1-2.7
c0-1.1,0.1-2.4,0.1-3.9H34.6z"/>
<path class="st0" d="M47.8,1.1C47.6,1.7,47.3,2.4,47,3c-0.3,0.6-0.6,1.2-0.9,1.9v12.4h-2.2v-9c-0.2,0.2-0.4,0.5-0.6,0.7
S43,9.3,42.8,9.5c0-0.1-0.1-0.3-0.2-0.5c-0.1-0.2-0.2-0.4-0.3-0.6c-0.1-0.2-0.2-0.4-0.3-0.6s-0.2-0.4-0.3-0.5
c0.4-0.4,0.8-0.9,1.2-1.4c0.4-0.5,0.8-1.1,1.1-1.6c0.4-0.6,0.7-1.2,1-1.8c0.3-0.6,0.6-1.3,0.8-1.9L47.8,1.1z M53.5,13.2v4h-2.2V5.3
h-0.7c-0.4,0.7-0.7,1.3-1.1,1.9s-0.8,1.1-1.2,1.6c-0.1-0.1-0.2-0.2-0.3-0.4c-0.1-0.1-0.3-0.3-0.4-0.4S47.2,7.8,47,7.7
c-0.2-0.1-0.3-0.2-0.4-0.3C47,6.9,47.4,6.5,47.8,6c0.4-0.5,0.7-1.1,1-1.7s0.6-1.2,0.9-1.8c0.3-0.6,0.5-1.3,0.7-1.9l2.1,0.5
c-0.1,0.4-0.3,0.8-0.4,1.1s-0.3,0.7-0.5,1.1h7v2h-5.1v1.9h4.7v1.9h-4.7v2h4.9v2H53.5z"/>
<path class="st0" d="M69.3,11.3v6h-2.2v-6h-7V9.1h7V3.6H61V1.5h14.4v2.1h-6v5.5h7.1v2.2H69.3z M64.1,4.2c0.1,0.3,0.3,0.6,0.4,0.9
s0.3,0.6,0.4,1c0.1,0.3,0.2,0.6,0.4,0.9c0.1,0.3,0.2,0.6,0.2,0.8l-2.1,0.6c0-0.2-0.1-0.5-0.2-0.8s-0.2-0.6-0.3-1
c-0.1-0.3-0.2-0.7-0.4-1c-0.1-0.3-0.3-0.7-0.4-1L64.1,4.2z M74.6,4.7c-0.3,0.7-0.7,1.4-1,2.1c-0.3,0.7-0.7,1.2-1,1.7l-1.9-0.5
c0.1-0.3,0.3-0.6,0.4-0.9s0.3-0.6,0.4-1c0.1-0.3,0.3-0.7,0.4-1c0.1-0.3,0.2-0.6,0.3-0.9L74.6,4.7z"/>
<path class="st0" d="M89.4,2c0.4,0.4,0.9,0.8,1.3,1.3c0.5,0.5,0.9,0.9,1.3,1.4s0.8,0.9,1.2,1.4c0.4,0.5,0.7,0.9,0.9,1.3l-1.8,1.3
c-0.1-0.2-0.3-0.4-0.4-0.6c-0.2-0.2-0.3-0.5-0.5-0.7c-1.6,0.1-3,0.1-4.1,0.2c-1.2,0.1-2.1,0.1-3,0.2c-0.8,0-1.5,0.1-2,0.1
s-1,0.1-1.3,0.1s-0.6,0.1-0.8,0.1c-0.2,0-0.4,0.1-0.5,0.1c0-0.1-0.1-0.2-0.1-0.4c-0.1-0.2-0.1-0.4-0.2-0.6
c-0.1-0.2-0.1-0.4-0.2-0.6C79,6.3,78.9,6.2,78.9,6c0.3-0.1,0.6-0.2,0.9-0.5c0.3-0.3,0.7-0.6,1-1c0.4-0.4,0.7-0.8,1.1-1.2
c0.4-0.4,0.7-0.9,1-1.3c0.3-0.4,0.6-0.8,0.8-1.2c0.2-0.4,0.4-0.6,0.5-0.8L86.4,1c-0.6,0.8-1.2,1.7-1.9,2.4s-1.4,1.5-2,2.2l7.4-0.2
c-0.3-0.4-0.7-0.8-1.1-1.2c-0.4-0.4-0.7-0.7-1-1.1L89.4,2z M82.4,16.3v0.9h-2.2V9.3h12.2v7.9H90v-0.9H82.4z M82.4,14.2H90v-2.9
h-7.6V14.2z"/>
</g>
</svg>

+ 0
- 0
routers/org/members.go View File


+ 6
- 0
routers/repo/http.go View File

@@ -317,6 +317,12 @@ func HTTP(ctx *context.Context) {
go repo.IncreaseCloneCnt()
}

if ctx.Req.Method == "POST" {
if strings.HasSuffix(ctx.Req.URL.Path, "git-upload-pack") {
go repo.IncreaseGitCloneCnt()
}
}

w := ctx.Resp
r := ctx.Req.Request
cfg := &serviceConfig{


+ 14
- 0
routers/repo/user_data_analysis.go View File

@@ -1,13 +1,27 @@
package repo

import (
"fmt"
"net/http"
"time"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/git"
"code.gitea.io/gitea/modules/log"
)

func QueryUserStaticData(ctx *context.Context) {
startDate := ctx.Query("startDate")
endDate := ctx.Query("endDate")
log.Info("startDate=" + startDate + " endDate=" + endDate)
startTime, _ := time.Parse("2006-01-02", startDate)
endTime, _ := time.Parse("2006-01-02", endDate)
log.Info("startTime=" + fmt.Sprint(startTime.Unix()) + " endDate=" + fmt.Sprint(endTime.Unix()))
ctx.JSON(http.StatusOK, models.QueryUserStaticData(startTime.Unix(), endTime.Unix()))

}

func TimingCountDataByDate(date string) {

t, _ := time.Parse("2006-01-02", date)


+ 120
- 36
routers/repo/view.go View File

@@ -12,6 +12,7 @@ import (
"fmt"
gotemplate "html/template"
"io/ioutil"
"net/http"
"net/url"
"path"
"strings"
@@ -31,11 +32,12 @@ import (
)

const (
tplRepoEMPTY base.TplName = "repo/empty"
tplRepoHome base.TplName = "repo/home"
tplWatchers base.TplName = "repo/watchers"
tplForks base.TplName = "repo/forks"
tplMigrating base.TplName = "repo/migrating"
tplRepoEMPTY base.TplName = "repo/empty"
tplRepoHome base.TplName = "repo/home"
tplWatchers base.TplName = "repo/watchers"
tplForks base.TplName = "repo/forks"
tplMigrating base.TplName = "repo/migrating"
tplContributors base.TplName = "repo/contributors"
)

type namedBlob struct {
@@ -243,6 +245,11 @@ func renderDirectory(ctx *context.Context, treeLink string) {
ctx.Data["ReadmeInList"] = true
ctx.Data["ReadmeExist"] = true
ctx.Data["FileIsSymlink"] = readmeFile.isSymlink
ctx.Data["ReadmeName"] = readmeFile.name

if ctx.Repo.CanEnableEditor() {
ctx.Data["CanEditFile"] = true
}

dataRc, err := readmeFile.blob.DataAsync()
if err != nil {
@@ -570,19 +577,29 @@ func safeURL(address string) string {
}

type ContributorInfo struct {
UserInfo *models.User // nil for contributor who is not a registered user
Email string
CommitCnt int
UserInfo *models.User // nil for contributor who is not a registered user
RelAvatarLink string `json:"rel_avatar_link"`
UserName string `json:"user_name"`
Email string `json:"email"`
CommitCnt int `json:"commit_cnt"`
}

type GetContributorsInfo struct {
ErrorCode int `json:"error_code"`
ErrorMsg string `json:"error_msg"`
Count int `json:"count"`
ContributorInfo []*ContributorInfo `json:"contributor_info"`
}

func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo{
func getContributorInfo(contributorInfos []*ContributorInfo, email string) *ContributorInfo {
for _, c := range contributorInfos {
if strings.Compare(c.Email,email) == 0 {
if strings.Compare(c.Email, email) == 0 {
return c
}
}
return nil
}

// Home render repository home page
func Home(ctx *context.Context) {
if len(ctx.Repo.Units) > 0 {
@@ -591,35 +608,41 @@ func Home(ctx *context.Context) {
if err == nil && contributors != nil {
startTime := time.Now()
var contributorInfos []*ContributorInfo
contributorInfoHash:= make(map[string]*ContributorInfo)
contributorInfoHash := make(map[string]*ContributorInfo)
count := 0
for _, c := range contributors {
if strings.Compare(c.Email,"") == 0 {
if count >= 25 {
continue
}
if strings.Compare(c.Email, "") == 0 {
continue
}
// get user info from committer email
user, err := models.GetUserByActivateEmail(c.Email)
if err == nil {
// committer is system user, get info through user's primary email
if existedContributorInfo,ok:=contributorInfoHash[user.Email];ok {
if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
}else{
} else {
// new committer info
var newContributor = &ContributorInfo{
user, user.Email,c.CommitCnt,
user, user.RelAvatarLink(), user.Name, user.Email, c.CommitCnt,
}
contributorInfos = append(contributorInfos, newContributor )
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[user.Email] = newContributor
}
} else {
// committer is not system user
if existedContributorInfo,ok:=contributorInfoHash[c.Email];ok {
if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
}else{
} else {
var newContributor = &ContributorInfo{
user, c.Email,c.CommitCnt,
user, "", "",c.Email, c.CommitCnt,
}
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[c.Email] = newContributor
}
@@ -627,7 +650,7 @@ func Home(ctx *context.Context) {
}
ctx.Data["ContributorInfo"] = contributorInfos
var duration = time.Since(startTime)
log.Info("getContributorInfo cost: %v seconds",duration.Seconds())
log.Info("getContributorInfo cost: %v seconds", duration.Seconds())
}
if ctx.Repo.Repository.IsBeingCreated() {
task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
@@ -694,13 +717,13 @@ func renderLicense(ctx *context.Context) {
log.Error("failed to get license content: %v, err:%v", f, err)
continue
}
if bytes.Compare(buf,license) == 0 {
log.Info("got matched license:%v",f)
if bytes.Compare(buf, license) == 0 {
log.Info("got matched license:%v", f)
ctx.Data["LICENSE"] = f
return
}
}
log.Info("not found matched license,repo:%v",ctx.Repo.Repository.Name)
log.Info("not found matched license,repo:%v", ctx.Repo.Repository.Name)
}

func renderLanguageStats(ctx *context.Context) {
@@ -801,31 +824,31 @@ func renderCode(ctx *context.Context) {
baseGitRepo, err := git.OpenRepository(ctx.Repo.Repository.BaseRepo.RepoPath())
defer baseGitRepo.Close()
if err != nil {
log.Error("error open baseRepo:%s",ctx.Repo.Repository.BaseRepo.RepoPath())
log.Error("error open baseRepo:%s", ctx.Repo.Repository.BaseRepo.RepoPath())
ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error
}else{
if _,error:= baseGitRepo.GetBranch(ctx.Repo.BranchName);error==nil{
} else {
if _, error := baseGitRepo.GetBranch(ctx.Repo.BranchName); error == nil {
//base repo has the same branch, then compare between current repo branch and base repo's branch
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName
ctx.SetParams("*",compareUrl)
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.BranchName
ctx.SetParams("*", compareUrl)
ctx.Data["UpstreamSameBranchName"] = true
}else{
} else {
//else, compare between current repo branch and base repo's default branch
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch
ctx.SetParams("*",compareUrl)
compareUrl := ctx.Repo.BranchName + "..." + ctx.Repo.Repository.BaseRepo.OwnerName + "/" + ctx.Repo.Repository.BaseRepo.Name + ":" + ctx.Repo.Repository.BaseRepo.DefaultBranch
ctx.SetParams("*", compareUrl)
ctx.Data["UpstreamSameBranchName"] = false
}
_, _, headGitRepo, compareInfo, _, _ := ParseCompareInfo(ctx)
defer headGitRepo.Close()
if compareInfo!= nil {
if compareInfo.Commits!=nil {
log.Info("compareInfoCommits数量:%d",compareInfo.Commits.Len())
if compareInfo != nil {
if compareInfo.Commits != nil {
log.Info("compareInfoCommits数量:%d", compareInfo.Commits.Len())
ctx.Data["FetchUpstreamCnt"] = compareInfo.Commits.Len()
}else{
} else {
log.Info("compareInfo nothing different")
ctx.Data["FetchUpstreamCnt"] = 0
}
}else{
} else {
ctx.Data["FetchUpstreamCnt"] = -1 // minus value indicates error
}
}
@@ -893,3 +916,64 @@ func Forks(ctx *context.Context) {

ctx.HTML(200, tplForks)
}

func Contributors(ctx *context.Context) {
ctx.HTML(http.StatusOK, tplContributors)
}

func ContributorsAPI(ctx *context.Context) {
count := 0
errorCode := 0
errorMsg := ""
contributors, err := git.GetContributors(ctx.Repo.Repository.RepoPath())
var contributorInfos []*ContributorInfo
if err == nil && contributors != nil {
contributorInfoHash := make(map[string]*ContributorInfo)
for _, c := range contributors {
if strings.Compare(c.Email, "") == 0 {
continue
}
// get user info from committer email
user, err := models.GetUserByActivateEmail(c.Email)
if err == nil {
// committer is system user, get info through user's primary email
if existedContributorInfo, ok := contributorInfoHash[user.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
} else {
// new committer info
var newContributor = &ContributorInfo{
user, user.RelAvatarLink(),user.Name, user.Email,c.CommitCnt,
}
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[user.Email] = newContributor
}
} else {
// committer is not system user
if existedContributorInfo, ok := contributorInfoHash[c.Email]; ok {
// existed: same primary email, different committer name
existedContributorInfo.CommitCnt += c.CommitCnt
} else {
var newContributor = &ContributorInfo{
user, "", "",c.Email,c.CommitCnt,
}
count++
contributorInfos = append(contributorInfos, newContributor)
contributorInfoHash[c.Email] = newContributor
}
}
}
} else {
log.Error("GetContributors failed: %v", err)
errorCode = -1
errorMsg = err.Error()
}

ctx.JSON(http.StatusOK, GetContributorsInfo{
ErrorCode: errorCode,
ErrorMsg: errorMsg,
Count: count,
ContributorInfo: contributorInfos,
})
}

+ 10
- 3
routers/routes/routes.go View File

@@ -311,7 +311,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Head("/", func() string {
return ""
})
m.Get("/", routers.Dashboard)
m.Get("/", routers.Home)
m.Get("/dashboard", routers.Dashboard)
m.Group("/explore", func() {
m.Get("", func(ctx *context.Context) {
@@ -615,6 +615,11 @@ func RegisterRoutes(m *macaron.Macaron) {
//reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain)

// ***** START: Organization *****
m.Group("/org", func() {
m.Group("/:org", func() {
m.Get("/members", org.Members)
}, context.OrgAssignment())
})
m.Group("/org", func() {
m.Group("", func() {
m.Get("/create", org.Create)
@@ -625,7 +630,7 @@ func RegisterRoutes(m *macaron.Macaron) {
m.Get("/dashboard", user.Dashboard)
m.Get("/^:type(issues|pulls)$", user.Issues)
m.Get("/milestones", reqMilestonesDashboardPageEnabled, user.Milestones)
m.Get("/members", org.Members)
//m.Get("/members", org.Members)
m.Post("/members/action/:action", org.MembersAction)

m.Get("/teams", org.Teams)
@@ -786,9 +791,11 @@ func RegisterRoutes(m *macaron.Macaron) {
}, reqSignIn, context.RepoAssignment(), context.UnitTypes(), reqRepoAdmin, context.RepoRef())

m.Post("/:username/:reponame/action/:action", reqSignIn, context.RepoAssignment(), context.UnitTypes(), repo.Action)
m.Get("/tool/query_user_static", repo.QueryUserStaticData)
// Grouping for those endpoints not requiring authentication
m.Group("/:username/:reponame", func() {
m.Get("/contributors", repo.Contributors)
m.Get("/contributors/list", repo.ContributorsAPI)
m.Group("/milestone", func() {
m.Get("/:id", repo.MilestoneIssuesAndPulls)
}, reqRepoIssuesOrPullsReader, context.RepoRef())


+ 4
- 4
routers/user/auth.go View File

@@ -544,7 +544,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR

if err := models.UpdateUserCols(u, "language"); err != nil {
log.Error(fmt.Sprintf("Error updating user language [user: %d, locale: %s]", u.ID, u.Language))
return setting.AppSubURL + "/"
return setting.AppSubURL + "/dashboard"
}
} else {
// Language setting of the user use the one previously set
@@ -562,7 +562,7 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
u.SetLastLogin()
if err := models.UpdateUserCols(u, "last_login_unix"); err != nil {
ctx.ServerError("UpdateUserCols", err)
return setting.AppSubURL + "/"
return setting.AppSubURL + "/dashboard"
}

if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) {
@@ -574,9 +574,9 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR
}

if obeyRedirect {
ctx.Redirect(setting.AppSubURL + "/")
ctx.Redirect(setting.AppSubURL + "/dashboard")
}
return setting.AppSubURL + "/"
return setting.AppSubURL + "/dashboard"
}

// SignInOAuth handles the OAuth2 login buttons


+ 2
- 2
templates/base/head.tmpl View File

@@ -180,8 +180,8 @@
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?7c4ef0a24be6109ab22e63c832ab21cf";
var s = document.getElementsByTagName("script")[0];
hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>


+ 2
- 2
templates/base/head_home.tmpl View File

@@ -181,8 +181,8 @@
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?7c4ef0a24be6109ab22e63c832ab21cf";
var s = document.getElementsByTagName("script")[0];
hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>


+ 8
- 0
templates/base/head_navbar.tmpl View File

@@ -7,6 +7,14 @@
<i class="sidebar icon"></i>
</div>
</div>
<div style="width:1px;background:#606266;height:80%;margin:auto 0.5rem"></div>
<div class="item brand" style="margin-left: 0.9rem;">
<a href="/">
<img class="ui mini image" style="height: 1.3rem;" src="{{StaticUrlPrefix}}/img/git-logo.svg">
</a>
</div>


{{if .IsSigned}}
<a class="item {{if .PageIsDashboard}}active{{end}}" href="/dashboard">{{.i18n.Tr "index"}}</a>


+ 6
- 5
templates/org/home.tmpl View File

@@ -38,11 +38,12 @@
<div class="ui sixteen wide mobile six wide tablet five wide computer column">
<h4 class="ui top attached header">
<strong>{{.i18n.Tr "org.people"}}</strong>
{{if .IsOrganizationMember}}
<div class="ui right">
<a class="text grey" href="{{.OrgLink}}/members">{{.Org.NumMembers}} {{svg "octicon-chevron-right" 16}}</a>
</div>
{{end}}
<div class="ui right">
<a class="text grey" href="{{.OrgLink}}/members">{{.MembersTotal}} {{svg "octicon-chevron-right" 16}}</a>
</div>
<!-- {{if .IsOrganizationMember}} -->
<!-- {{end}} -->
</h4>
<div class="ui attached segment members">
{{$isMember := .IsOrganizationMember}}


+ 6
- 6
templates/org/navber.tmpl View File

@@ -3,10 +3,10 @@
<a class="{{if $.PageIsOrgHome}}active{{end}} item" href="{{.HomeLink}}">
{{svg "octicon-home" 16}}&nbsp;{{$.i18n.Tr "org.home"}}
</a>
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization" 16}}&nbsp;{{$.i18n.Tr "org.people"}}
</a>
{{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}}
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization" 16}}&nbsp;{{$.i18n.Tr "org.people"}}
</a>
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams">
{{svg "octicon-jersey" 16}}&nbsp;{{$.i18n.Tr "org.teams"}}
</a>
@@ -23,10 +23,10 @@
{{svg "octicon-home" 16}}&nbsp;{{$.i18n.Tr "org.home"}}
</a>
{{end}}
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization" 16}}&nbsp;{{$.i18n.Tr "org.people"}}
</a>
{{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}}
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members">
{{svg "octicon-organization" 16}}&nbsp;{{$.i18n.Tr "org.people"}}
</a>
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams">
{{svg "octicon-jersey" 16}}&nbsp;{{$.i18n.Tr "org.teams"}}
</a>


+ 13
- 3
templates/repo/activity.tmpl View File

@@ -2,8 +2,18 @@
<div class="repository commits">
{{template "repo/header" .}}
<div class="ui container">
<h2 class="ui header">{{.DateFrom}} - {{.DateUntil}}
<div class="ui right">
<div class="ui three column stackable grid" style="align-items: center;">
<div class="column">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a>
<div class="divider"> / </div>
<div class="active section" href="{{.RepoLink}}/activity">{{.i18n.Tr "repo.activity"}}</div>
</div>
</div>
<div class="column center aligned" style="font-weight: 800;">
{{.DateFrom}} - {{.DateUntil}}
</div>
<div class="column right aligned">
<!-- Period -->
<div class="ui floating dropdown jump filter">
<div class="ui basic compact button">
@@ -23,7 +33,7 @@
</div>
</div>
</div>
</h2>
</div>
<div class="ui divider"></div>

{{if (or (.Permission.CanRead $.UnitTypeIssues) (.Permission.CanRead $.UnitTypePullRequests))}}


+ 9
- 0
templates/repo/contributors.tmpl View File

@@ -0,0 +1,9 @@
{{template "base/head" .}}
<div class="repository watchers">
{{template "repo/header" .}}
<div class="ui container" id="Contributors">
</div>
</div>
{{template "base/footer" .}}

+ 31
- 28
templates/repo/header.tmpl View File

@@ -92,16 +92,27 @@
{{if not .Repository.IsBeingCreated}}
<div class="ui tabular stackable menu navbar">
{{if .Permission.CanRead $.UnitTypeCode}}
<a class="{{if .PageIsViewCode}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}}
<div class="dropdown-menu">
<a class="{{if or .PageIsViewCode .PageIsReleaseList .PageIsWiki .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">
<span>{{svg "octicon-code" 16}} {{.i18n.Tr "repo.code"}} <i class="dropdown icon"></i></span>
</a>
{{end}}
<div class="dropdown-content">
<a style="border: none;" class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases">
{{svg "octicon-tag" 16}} {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .NumReleases}}gray{{else}}blue{{end}} small label">{{.NumReleases}}</span>
</a>
<a style="border: none;" class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
{{svg "octicon-book" 16}} {{.i18n.Tr "repo.wiki"}}
</a>
<a style="border: none;" class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity">
{{svg "octicon-pulse" 16}} {{.i18n.Tr "repo.activity"}}
</a>

{{if .Permission.CanRead $.UnitTypeDatasets}}
<a class="{{if .PageIsDataset}}active{{end}} item" href="{{.RepoLink}}/datasets?type=0">
{{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}}
</a>
</div>
</div>
{{end}}


{{if .Permission.CanRead $.UnitTypeIssues}}
<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoLink}}/issues">
@@ -109,11 +120,11 @@
</a>
{{end}}

{{if .Permission.CanRead $.UnitTypeExternalTracker}}
<!-- {{if .Permission.CanRead $.UnitTypeExternalTracker}}
<a class="{{if .PageIsIssueList}}active{{end}} item" href="{{.RepoExternalIssuesLink}}" target="_blank" rel="noopener noreferrer">
{{svg "octicon-link-external" 16}} {{.i18n.Tr "repo.issues"}} </span>
</a>
{{end}}
{{end}} -->

{{if and .Repository.CanEnablePulls (.Permission.CanRead $.UnitTypePullRequests)}}
<a class="{{if .PageIsPullList}}active{{end}} item" href="{{.RepoLink}}/pulls">
@@ -121,35 +132,22 @@
</a>
{{end}}

{{if and (.Permission.CanRead $.UnitTypeReleases) (not .IsEmptyRepo) }}
<a class="{{if .PageIsReleaseList}}active{{end}} item" href="{{.RepoLink}}/releases">
{{svg "octicon-tag" 16}} {{.i18n.Tr "repo.releases"}} <span class="ui {{if not .NumReleases}}gray{{else}}blue{{end}} small label">{{.NumReleases}}</span>
{{if .Permission.CanRead $.UnitTypeDatasets}}
<a class="{{if .PageIsDataset}}active{{end}} item" href="{{.RepoLink}}/datasets?type=0">
{{svg "octicon-inbox" 16}} {{.i18n.Tr "datasets"}}
</a>
{{end}}

{{if or (.Permission.CanRead $.UnitTypeWiki) (.Permission.CanRead $.UnitTypeExternalWiki)}}
<a class="{{if .PageIsWiki}}active{{end}} item" href="{{.RepoLink}}/wiki" {{if (.Permission.CanRead $.UnitTypeExternalWiki)}} target="_blank" rel="noopener noreferrer" {{end}}>
{{svg "octicon-book" 16}} {{.i18n.Tr "repo.wiki"}}
</a>
{{end}}

{{if and (.Permission.CanReadAny $.UnitTypePullRequests $.UnitTypeIssues $.UnitTypeReleases) (not .IsEmptyRepo)}}
<a class="{{if .PageIsActivity}}active{{end}} item" href="{{.RepoLink}}/activity">
{{svg "octicon-pulse" 16}} {{.i18n.Tr "repo.activity"}}
</a>
{{end}}

{{if .Permission.CanRead $.UnitTypeCloudBrain}}
<a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/cloudbrain">
{{svg "octicon-server" 16}} {{.i18n.Tr "repo.cloudbrain"}}
<span>{{svg "octicon-server" 16}} {{.i18n.Tr "repo.cloudbrain"}}<i class="question circle icon link cloudbrain-question" data-content={{.i18n.Tr "repo.cloudbrain_helper"}} data-position="top center" data-variation="mini"></i></span>
</a>
{{end}}
{{if .IsSigned}}
<!-- {{if .IsSigned}}
<a class="{{if .PageIsBlockChain}}active{{end}} item " href="{{.RepoLink}}/blockchain">
{{svg "octicon-law" 16}}
{{.i18n.Tr "repo.balance"}}
</a>
{{end}}
{{end}} -->

{{template "custom/extra_tabs" .}}

@@ -243,4 +241,9 @@
window.location.href = repolink + "/datasets?type=" + checked_radio
})
})
$('.question.circle.icon').hover(function(){
$(this).popup('show')
$('.ui.popup.mini.top.center').css({"border-color":'rgba(50, 145, 248, 100)',"color":"rgba(3, 102, 214, 100)","border-radius":"5px"})
});
</script>

+ 13
- 7
templates/repo/home.tmpl View File

@@ -4,7 +4,7 @@
font-size: 1.0em;
margin-bottom: 1.0rem;
}
#contributorInfo > a:nth-child(n+25){
#contributorInfo > a:nth-child(n+26){
display:none;
}
#contributorInfo > a{
@@ -329,9 +329,15 @@

<div>
<h4 class="ui header">
{{$lenCon := len .ContributorInfo}}
{{if lt $lenCon 25 }}
<strong>贡献者 ({{len .ContributorInfo}})</strong>
{{else}}
<strong>贡献者 ({{len .ContributorInfo}}+)</strong>
{{end}}
<div class="ui right">
<a class="membersmore text grey" href="javascript:;">全部 {{svg "octicon-chevron-right" 16}}</a>
<a class="membersmore text grey" href="{{.RepoLink}}/contributors">全部 {{svg "octicon-chevron-right" 16}}</a>
</div>
</h4>
<div class="ui members" id="contributorInfo">
@@ -353,10 +359,10 @@
</div>

<script type="text/javascript">
$(document).ready(function(){
$(".membersmore").click(function(){
$("#contributorInfo > a:nth-child(n+25)").show();
});
});
// $(document).ready(function(){
// $(".membersmore").click(function(){
// $("#contributorInfo > a:nth-child(n+25)").show();
// });
// });
</script>
{{template "base/footer" .}}

+ 5
- 1
templates/repo/release/list.tmpl View File

@@ -4,7 +4,11 @@
<div class="ui container">
{{template "base/alert" .}}
<h2 class="ui header">
{{.i18n.Tr "repo.release.releases"}}
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a>
<div class="divider"> / </div>
<div class="active section" href="{{.RepoLink}}/releases">{{.i18n.Tr "repo.releases"}}</div>
</div>
{{if .CanCreateRelease}}
<div class="ui right">
<a class="ui small green button" href="{{$.RepoLink}}/releases/new">


+ 15
- 0
templates/repo/view_file.tmpl View File

@@ -34,6 +34,21 @@
</div>
{{end}}
</div>

{{if .ReadmeInList}}
<div class="file-header-right">
<div class="ui right file-actions">
{{if .Repository.CanEnableEditor}}
{{if .CanEditFile}}
<a href="{{.RepoLink}}/_edit/{{EscapePound .BranchName}}/{{EscapePound .ReadmeName}}"><span class="btn-octicon poping up" data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-pencil" 16}}</span></a>
{{else}}
<span class="btn-octicon poping up disabled" data-content="{{.EditFileTooltip}}" data-position="bottom center" data-variation="tiny inverted">{{svg "octicon-pencil" 16}}</span>
{{end}}
{{end}}
</div>
</div>
{{end}}
{{if not .ReadmeInList}}
<div class="file-header-right">
<div class="ui right file-actions">


+ 7
- 0
templates/repo/wiki/start.tmpl View File

@@ -2,6 +2,13 @@
<div class="repository wiki start">
{{template "repo/header" .}}
<div class="ui container">
<h2 class="ui header">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a>
<div class="divider"> / </div>
<div class="active section" href="{{.RepoLink}}/wiki">{{.i18n.Tr "repo.wiki"}}</div>
</div>
</h2>
<div class="ui center segment">
{{svg "octicon-book" 32}}
<h2>{{.i18n.Tr "repo.wiki.welcome"}}</h2>


+ 9
- 1
templates/repo/wiki/view.tmpl View File

@@ -3,6 +3,14 @@
{{template "repo/header" .}}
{{ $title := .title}}
<div class="ui container">
<h2 class="ui header">
<div class="ui breadcrumb">
<a class="section" href="{{.RepoLink}}{{if (ne .BranchName .Repository.DefaultBranch)}}/src/{{.BranchNameSubURL | EscapePound}}{{end}}">{{.i18n.Tr "repo.code"}}</a>
<div class="divider"> / </div>
<div class="active section" href="{{.RepoLink}}/wiki">{{.i18n.Tr "repo.wiki"}}</div>
</div>
</h2>
<div class="ui divider"></div>
<div class="ui stackable grid">
<div class="ui ten wide column">
<div class="choose page">
@@ -28,7 +36,7 @@
</div>
</div>
</div>
<div class="ui six wide column">
<div class="ui six wide column right aligned">
<div class="ui action small input" id="clone-panel">
{{if not $.DisableHTTP}}
<button class="ui basic clone button" id="repo-clone-https" data-link="{{.WikiCloneLink.HTTPS}}">


+ 109
- 0
web_src/js/components/Contributors.vue View File

@@ -0,0 +1,109 @@
<template>
<div class="ui container">
<div class="row git-user-content">
<h3 class="ui header">
<div class="ui breadcrumb">
<a class="section" href="/">代码</a>
<div class="divider"> / </div>
<div class="active section" >贡献者({{totalNum}})</div>
</div>
</h3>
<div class="ui horizontal relaxed list">
<div class="item user-list-item" v-for="(contributor,i) in contributors_list_page" >
<a v-if="contributor.user_name" :href="AppSubUrl +'/'+ contributor.user_name"><img class="ui avatar s16 image js-popover-card" :src="contributor.rel_avatar_link"></a>
<a v-else :href="'mailto:' + contributor.email "><img class="ui avatar s16 image js-popover-card" :avatar="contributor.email"></a>
<div class="content">
<div class="header" >
<a v-if="contributor.user_name" :href="AppSubUrl +'/'+ contributor.user_name">{{contributor.user_name}}</a>
<a v-else :href="'mailto:' + contributor.email ">{{contributor.email}}</a>
</div>
<span class="commit-btn">Commits: {{contributor.commit_cnt}}</span>
</div>
</div>
</div>
</div>
<div class="ui container" style="margin-top:50px;text-align:center">
<el-pagination
background
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-size="pageSize"
layout="total, prev, pager, next"
:total="totalNum">
</el-pagination>
</div>
</div>
</template>

<script>

const {AppSubUrl, StaticUrlPrefix, csrf} = window.config;

export default {
data() {
return {
url:'',
contributors_list:[],
contributors_list_page:[],
currentPage:1,
pageSize:50,
totalNum:0,
AppSubUrl:AppSubUrl
};
},
methods: {

getContributorsList(){
this.$axios.get(this.url+'/contributors/list').then((res)=>{
this.contributors_list = res.data.contributor_info
this.totalNum = this.contributors_list.length
this.contributors_list_page = this.contributors_list.slice(0,this.pageSize)
})
},
handleCurrentChange(val){
this.contributors_list_page = this.contributors_list.slice((val-1)*this.pageSize,val*this.pageSize)
},
},
computed:{
},
watch: {

},
created(){
this.url=document.head.querySelector("[property~='og:url'][content]").content
this.getContributorsList()
},

updated(){
if(document.querySelectorAll('img[avatar]').length!==0){
window.LetterAvatar.transform()
}
}
};
</script>

<style scoped>
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active {
background-color: #5bb973;
color: #FFF;
}
/deep/ .el-pagination.is-background .el-pager li.active {
color: #fff;
cursor: default;
}
/deep/ .el-pagination.is-background .el-pager li:hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled):hover {
color: #5bb973;
}
/deep/ .el-pagination.is-background .el-pager li:not(.disabled).active:hover {
background-color: #5bb973;
color: #FFF;
}
</style>

+ 13
- 15
web_src/js/components/EditAboutInfo.vue View File

@@ -1,9 +1,9 @@
<template>
<div>
<h4 id="about-desc" class="ui header">简介
<!-- <a class="edit-icon" href="javascript:void(0)" @click="editClick">
<i class="gray edit outline icon"></i>
</a> -->
<h4 id="about-desc" class="ui header desc-home">简介
<a class="edit-icon" href="javascript:void(0)" @click="editClick">
<i class="gray edit outline icon" style="margin-right: 0;"></i>
</a>
</h4>
<edit-dialog-cmpt
:vmContext="vmContext"
@@ -11,8 +11,8 @@
v-model="editDataDialog"
:deleteCallback="editDataFunc"
:deleteLoading ="editDataListLoading"
deleteParam = "ruleForm"
@input="initForm"
deleteParam = "ruleForm"
@input="initForm"
>
<div slot="title">
@@ -20,7 +20,7 @@
<div slot="content">
<el-form label-position="top" :model="info" :rules="rule" ref="ruleForm">
<el-form-item label="简介" prop="desc">
<el-input v-model="info.desc" type="textarea" :autosize="{minRows:2,maxRows:6}"></el-input>
<el-input v-model="info.desc" type="textarea" placeholder="请输入内容" :autosize="{minRows:4,maxRows:6}" maxlength="255" show-word-limit></el-input>
</el-form-item>
<el-form-item label="主页" prop="index_web" >
<el-input v-model="info.index_web" placeholder="主页(eg: https://git.openi.org.cn)"></el-input>
@@ -70,7 +70,7 @@ export default {
this.info.desc = el;
},
getWeb() {
const el = $('a.link').text();
const el = $('a.link.edit-link').text();
this.info.index_web = el;
},
getRepoName() {
@@ -79,7 +79,6 @@ export default {
},
initForm(diaolog) {
if (diaolog === false) {
console.log("--watch----------")
this.getRepoName();
this.getDesc();
this.getWeb();
@@ -133,12 +132,11 @@ export default {

<style scoped>
.edit-icon{
float: right;
font-size: 16px;
display: block;
top: -2px;
color: #8c92a4;
background-color: transparent;
}
}
.desc-home{
display: flex;
justify-content: space-between;
}

</style>

+ 15
- 10
web_src/js/components/basic/editDialog.vue View File

@@ -14,8 +14,8 @@
<slot name="content"></slot>
<div slot="footer" class="dialog-footer">
<el-button @click="deleteDialog = false">{{"取消"}}</el-button>
<el-button type="primary" @click="deleteCallback.call(vmContext,deleteParam)">{{"确定"}}</el-button>
<el-button size="small" @click="deleteDialog = false">{{"取消"}}</el-button>
<el-button size="small" style="background-color: #21ba45;color: #fff;" @click="deleteCallback.call(vmContext,deleteParam)">{{"确定"}}</el-button>
</div>
</el-dialog>
</template>
@@ -80,26 +80,31 @@ export default {
.message-box__content .message-box-title{font-size:16px;padding:2px 0;color:#333;}
.message-box__content .message-box-p{font-size:16px;padding:20px 0;color:#333;}
.message-box__content .message-box-info{color:#999;padding:2px 0;}
.dialog-footer,.el-message-box__btns{text-align:center;}


/deep/ .el-dialog__body{
padding: 1rem;
}
/deep/ .el-dialog__header {
background: #0067b3;
padding: 12px 30px;
line-height: 25px;}
background: #f0f0f0;
padding: 1rem;
}
/deep/ .el-dialog__title {
font-family: PingFangSC-Regular;
font-size: 18px;
color: #fff;
font-size: 1.28571429rem;
color: rgba(0,0,0,.87);
font-weight: 200;
line-height: 25px;
height: 25px;
}
/deep/ .el-dialog__footer {
background: #eff3f9;
padding: 20px 30px;
padding: 1rem;
}
/deep/ .el-dialog{
width: 40%;
width: 30%;
}
/deep/ .el-form-item__label{
padding: 0;
}
</style>

+ 74
- 0
web_src/js/features/letteravatar.js View File

@@ -0,0 +1,74 @@
/**
* LetterAvatar
*
* Artur Heinze
* Create Letter avatar based on Initials
* based on https://gist.github.com/leecrossley/6027780
*/
(function(w, d){
function LetterAvatar (name, size, color) {
name = name || '';
size = size || 60;
var colours = [
"#1abc9c", "#2ecc71", "#3498db", "#9b59b6", "#34495e", "#16a085", "#27ae60", "#2980b9", "#8e44ad", "#2c3e50",
"#f1c40f", "#e67e22", "#e74c3c", "#00bcd4", "#95a5a6", "#f39c12", "#d35400", "#c0392b", "#bdc3c7", "#7f8c8d"
],
nameSplit = String(name).split(' '),
initials, charIndex, colourIndex, canvas, context, dataURI;
if (nameSplit.length == 1) {
initials = nameSplit[0] ? nameSplit[0].charAt(0):'?';
} else {
initials = nameSplit[0].charAt(0) + nameSplit[1].charAt(0);
}
if (w.devicePixelRatio) {
size = (size * w.devicePixelRatio);
}
charIndex = (initials == '?' ? 72 : initials.charCodeAt(0)) - 64;
colourIndex = charIndex % 20;
canvas = d.createElement('canvas');
canvas.width = size;
canvas.height = size;
context = canvas.getContext("2d");
context.fillStyle = color ? color : colours[colourIndex - 1];
context.fillRect (0, 0, canvas.width, canvas.height);
context.font = Math.round(canvas.width/2)+"px 'Microsoft Yahei'";
context.textAlign = "center";
context.fillStyle = "#FFF";
context.fillText(initials, size / 2, size / 1.5);
dataURI = canvas.toDataURL();
canvas = null;
return dataURI;
}
LetterAvatar.transform = function() {
Array.prototype.forEach.call(d.querySelectorAll('img[avatar]'), function(img, name, color) {
name = img.getAttribute('avatar');
color = img.getAttribute('color');
img.src = LetterAvatar(name, img.getAttribute('width'), color);
img.removeAttribute('avatar');
img.setAttribute('alt', name);
});
};
// AMD support
if (typeof define === 'function' && define.amd) {
define(function () { return LetterAvatar; });
// CommonJS and Node.js module support.
} else if (typeof exports !== 'undefined') {
// Support Node.js specific `module.exports` (which can be a function)
if (typeof module != 'undefined' && module.exports) {
exports = module.exports = LetterAvatar;
}
// But always support CommonJS module 1.1.1 spec (`exports` cannot be a function)
exports.LetterAvatar = LetterAvatar;
} else {
window.LetterAvatar = LetterAvatar;
d.addEventListener('DOMContentLoaded', function(event) {
LetterAvatar.transform();
});
}
})(window, document);

+ 18
- 1
web_src/js/index.js View File

@@ -4,7 +4,7 @@

import './publicpath.js';
import './polyfills.js';
import './features/letteravatar.js'
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
@@ -37,6 +37,7 @@ import ObsUploader from './components/ObsUploader.vue';
import EditAboutInfo from './components/EditAboutInfo.vue';
import Images from './components/Images.vue'
import EditTopics from './components/EditTopics.vue'
import Contributors from './components/Contributors.vue'

Vue.use(ElementUI);
Vue.prototype.$axios = axios;
@@ -2969,6 +2970,7 @@ $(document).ready(async () => {
initObsUploader();
initVueEditAbout();
initVueEditTopic();
initVueContributors();
initVueImages();
initTeamSettings();
initCtrlEnterSubmit();
@@ -3682,6 +3684,21 @@ function initVueEditTopic() {
render:h=>h(EditTopics)
})
}

function initVueContributors() {
const el = document.getElementById('Contributors');
if (!el) {
return;
}

new Vue({
el:'#Contributors',
render:h=>h(Contributors)
})
}


function initVueImages() {
const el = document.getElementById('images');
console.log("el",el)


+ 73
- 0
web_src/less/openi.less View File

@@ -243,4 +243,77 @@ footer .column{margin-bottom:0!important; padding-bottom:0!important;}
display: inline-block;
width: 100%;
}
.git-user-content{
margin-bottom: 16px;
word-break: break-all;
}
.row.git-user-content .ui.avatar.s16.image {
width: 50px !important;
height: 50px !important;
vertical-align: middle;
border-radius: 500rem;}
.user-list-item {
padding: 0.3em 0px !important;
margin: 15px 0 !important;
width: 220px !important;
}
.row.git-user-content .content {
margin-left: 6px;
display: inline-block;
vertical-align: top !important;
overflow: hidden;
}
.row.git-user-content .content .header {
font-weight: bold;
}
.item.user-list-item .header {
line-height: 23px !important;
font-size: 16px !important;
text-overflow: ellipsis;
overflow: hidden;
width: 145px;
white-space:nowrap;
}
.item.user-list-item .header a {
color: #587284 !important;
}
.row.git-user-content .content .commit-btn {
margin-top: 6px;
float: left;
font-size: 11px;
color: #40485b !important;
}

.dropdown-menu {
position: relative;
display: inline-block;
margin-top: 4px;
}
.dropdown-menu:hover .dropdown-content {
display: block;
}
.dropdown-content{
display: none;
position: absolute;
background-color: #ffffff;
min-width: 180px;
z-index: 999;
border: 1px solid transparent;
border-color: #d4d4d5;
border-top: none;

}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {background-color: #f1f1f1}
.cloudbrain-question{
margin-left: 4px !important;
color: #3291F8;
}

Loading…
Cancel
Save