Browse Source

Merge remote-tracking branch 'origin/V20211213' into zouap

tags/v1.21.12.1^2
zouap 3 years ago
parent
commit
5903eeaf9d
14 changed files with 252 additions and 137 deletions
  1. +31
    -31
      custom/conf/app.ini.sample
  2. +1
    -0
      models/cloudbrain.go
  3. +16
    -6
      models/user.go
  4. +28
    -8
      modules/cloudbrain/cloudbrain.go
  5. +32
    -30
      modules/setting/setting.go
  6. +4
    -0
      modules/storage/local.go
  7. +6
    -0
      modules/storage/minio.go
  8. +1
    -0
      modules/storage/storage.go
  9. +37
    -4
      routers/repo/cloudbrain.go
  10. +11
    -1
      routers/repo/modelarts.go
  11. +27
    -21
      templates/base/head_navbar.tmpl
  12. +24
    -17
      templates/base/head_navbar_fluid.tmpl
  13. +25
    -19
      templates/base/head_navbar_home.tmpl
  14. +9
    -0
      web_src/js/index.js

+ 31
- 31
custom/conf/app.ini.sample View File

@@ -1059,50 +1059,50 @@ RESULT_BACKEND = redis://localhost:6379
[cloudbrain]
USERNAME =
PASSWORD =
REST_SERVER_HOST = http://192.168.202.73
JOB_PATH = /datasets/minio/data/opendata/jobs/
DEBUG_SERVER_HOST = http://192.168.202.73/
REST_SERVER_HOST =
JOB_PATH =
DEBUG_SERVER_HOST =
; cloudbrain visit opendata
USER = cW4cMtH24eoWPE7X
PWD = 4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DA.C
GPU_TYPE_DEFAULT = openidebug
GPU_TYPES = {"gpu_type":[{"id":1,"queue":"openidebug","value":"T4"},{"id":2,"queue":"openidgx","value":"V100"}]}
USER =
PWD =
GPU_TYPE_DEFAULT =
GPU_TYPES =

[benchmark]
ENABLED = true
BENCHMARKCODE = https://yangzhx:justfortest123@git.openi.org.cn/yangzhx/detection_benchmark_script.git
HOST = http://192.168.202.90:3366/
ENABLED =
BENCHMARKCODE =
HOST =

[snn4imagenet]
ENABLED = true
SNN4IMAGENETCODE = https://yult:eh2Ten4iLYjFkbj@git.openi.org.cn/ylt/snn4imagenet.git
HOST = http://192.168.202.90:3366/
ENABLED =
SNN4IMAGENETCODE =
HOST =

[decompress]
HOST = http://192.168.207.34:39987
USER = cW4cMtH24eoWPE7X
PASSWORD = 4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC
HOST =
USER =
PASSWORD =

[blockchain]
HOST = http://192.168.207.84:3002/
COMMIT_VALID_DATE = 2021-01-15
HOST =
COMMIT_VALID_DATE =

[obs]
ENDPOINT = https://obs.cn-south-222.ai.pcl.cn
ACCESS_KEY_ID = FDP3LRMHLB9S77VWEHE3
SECRET_ACCESS_KEY = LyM82Wk80pgjhs2z7AdDcsdpCWhbsJtSzQ7hkESN
BUCKET = testopendata
LOCATION = cn-south-222
BASE_PATH = attachment/
ENDPOINT =
ACCESS_KEY_ID =
SECRET_ACCESS_KEY =
BUCKET =
LOCATION =
BASE_PATH =

[modelarts]
ORGANIZATION = modelarts
ENDPOINT = https://modelarts.cn-south-222.ai.pcl.cn
PROJECT_ID = edfccf24aace4e17a56da6bcbb55a5aa
PROJECT_NAME = cn-south-222_test
USERNAME = test1
PASSWORD = Qizhi@test.
DOMAIN = cn-south-222
ORGANIZATION =
ENDPOINT =
PROJECT_ID =
PROJECT_NAME =
USERNAME =
PASSWORD =
DOMAIN =

[radar_map]
impact=0.3


+ 1
- 0
models/cloudbrain.go View File

@@ -91,6 +91,7 @@ type Cloudbrain struct {
DeletedAt time.Time `xorm:"deleted"`
CanDebug bool `xorm:"-"`
CanDel bool `xorm:"-"`
CanModify bool `xorm:"-"`
Type int

VersionID int64 //版本id


+ 16
- 6
models/user.go View File

@@ -145,7 +145,7 @@ type User struct {
AllowImportLocal bool // Allow migrate repository by local path
AllowCreateOrganization bool `xorm:"DEFAULT true"`
ProhibitLogin bool `xorm:"NOT NULL DEFAULT false"`
IsOperator bool `xorm:"NOT NULL DEFAULT false"` //运营人员
IsOperator bool `xorm:"NOT NULL DEFAULT false"` //运营人员

// Avatar
Avatar string `xorm:"VARCHAR(2048) NOT NULL"`
@@ -929,8 +929,17 @@ var (
"template",
"user",
"vendor",
}
reservedUserPatterns = []string{"*.keys", "*.gpg"}
"dashbord",
"operation",
"blockchain",
"avatar",
"swagger.v1.json",
"secure",
"serviceworker.js",
"self",
"repo-avatars",
}
reservedUserPatterns = []string{"*.keys", "*.gpg", "*.png"}
)

// isUsableName checks if name is reserved or pattern of name is not allowed
@@ -1552,11 +1561,11 @@ func GetUserByActivateEmail(email string) (*User, error) {
if err := ctx.e.Join("INNER", "email_address", "email_address.uid = \"user\".id").
Where("email_address.email= ?", email).
Find(&users); err != nil {
return nil,err
return nil, err
}
if len(users) >= 1 {
return &users[0],nil
}else {
return &users[0], nil
} else {
// Finally, if email address is the protected email address:用户邮件地址设置为隐藏电子邮件地址
if strings.HasSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress)) {
username := strings.TrimSuffix(email, fmt.Sprintf("@%s", setting.Service.NoReplyAddress))
@@ -1572,6 +1581,7 @@ func GetUserByActivateEmail(email string) (*User, error) {
return nil, errors.New("cannot find user by email")
}
}

// GetUserByEmail returns the user object by given e-mail if exists.
func GetUserByEmail(email string) (*User, error) {
return GetUserByEmailContext(DefaultDBContext(), email)


+ 28
- 8
modules/cloudbrain/cloudbrain.go View File

@@ -29,9 +29,7 @@ var (
ResourceSpecs *models.ResourceSpecs
)

func isAdminOrOwnerOrJobCreater(ctx *context.Context, jobId string) bool {

job, err := models.GetCloudbrainByJobID(jobId)
func isAdminOrOwnerOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool {

if err != nil {
return ctx.IsUserRepoOwner() || ctx.IsUserSiteAdmin()
@@ -41,9 +39,29 @@ func isAdminOrOwnerOrJobCreater(ctx *context.Context, jobId string) bool {

}

func isAdminOrJobCreater(ctx *context.Context, jobId string) bool {
func CanDeleteDebugJob(ctx *context.Context, job *models.Cloudbrain) bool {

if job.Status != string(models.JobStopped) && job.Status != string(models.JobFailed) && job.Status != string(models.ModelArtsStartFailed) && job.Status != string(models.ModelArtsCreateFailed) {
return false
}
return isAdminOrOwnerOrJobCreater(ctx, job, nil)
}

func CanDeleteTrainJob(ctx *context.Context, job *models.Cloudbrain) bool {

return isAdminOrOwnerOrJobCreater(ctx, job, nil)
}

func CanCreateOrDebugJob(ctx *context.Context) bool {
return ctx.Repo.CanWrite(models.UnitTypeCloudBrain)
}

func CanModifyJob(ctx *context.Context, job *models.Cloudbrain) bool {

job, err := models.GetCloudbrainByJobID(jobId)
return isAdminOrJobCreater(ctx, job, nil)
}

func isAdminOrJobCreater(ctx *context.Context, job *models.Cloudbrain, err error) bool {

if err != nil {
return ctx.IsUserSiteAdmin()
@@ -57,7 +75,9 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {

var jobID = ctx.Params(":jobid")

if !isAdminOrOwnerOrJobCreater(ctx, jobID) {
job, err := models.GetCloudbrainByJobID(jobID)

if !isAdminOrOwnerOrJobCreater(ctx, job, err) {

ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}
@@ -67,8 +87,8 @@ func AdminOrOwnerOrJobCreaterRight(ctx *context.Context) {
func AdminOrJobCreaterRight(ctx *context.Context) {

var jobID = ctx.Params(":jobid")
if !isAdminOrJobCreater(ctx, jobID) {
job, err := models.GetCloudbrainByJobID(jobID)
if !isAdminOrJobCreater(ctx, job, err) {

ctx.NotFound(ctx.Req.URL.RequestURI(), nil)
}


+ 32
- 30
modules/setting/setting.go View File

@@ -442,14 +442,15 @@ var (
DecompressOBSTaskName string

//cloudbrain config
CBAuthUser string
CBAuthPassword string
RestServerHost string
JobPath string
JobType string
GpuTypes string
DebugServerHost string
ResourceSpecs string
CBAuthUser string
CBAuthPassword string
RestServerHost string
JobPath string
CBCodePathPrefix string
JobType string
GpuTypes string
DebugServerHost string
ResourceSpecs string

//benchmark config
IsBenchmarkEnabled bool
@@ -1219,8 +1220,8 @@ func NewContext() {

sec = Cfg.Section("decompress")
DecompressAddress = sec.Key("HOST").MustString("http://192.168.207.34:39987")
AuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X")
AuthPassword = sec.Key("PASSWORD").MustString("4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC")
AuthUser = sec.Key("USER").MustString("")
AuthPassword = sec.Key("PASSWORD").MustString("")

sec = Cfg.Section("labelsystem")
LabelTaskName = sec.Key("LabelTaskName").MustString("LabelRedisQueue")
@@ -1231,10 +1232,11 @@ func NewContext() {
RecommentRepoAddr = sec.Key("Address").MustString("https://git.openi.org.cn/OpenIOSSG/promote/raw/branch/master/")

sec = Cfg.Section("cloudbrain")
CBAuthUser = sec.Key("USER").MustString("cW4cMtH24eoWPE7X")
CBAuthPassword = sec.Key("PWD").MustString("4BPmgvK2hb2Eywwyp4YZRY4B7yQf4DAC")
CBAuthUser = sec.Key("USER").MustString("")
CBAuthPassword = sec.Key("PWD").MustString("")
RestServerHost = sec.Key("REST_SERVER_HOST").MustString("http://192.168.202.73")
JobPath = sec.Key("JOB_PATH").MustString("/datasets/minio/data/opendata/jobs/")
CBCodePathPrefix = sec.Key("CODE_PATH_PREFIX").MustString("jobs/")
DebugServerHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73")
JobType = sec.Key("GPU_TYPE_DEFAULT").MustString("openidebug")
GpuTypes = sec.Key("GPU_TYPES").MustString("")
@@ -1242,31 +1244,31 @@ func NewContext() {

sec = Cfg.Section("benchmark")
IsBenchmarkEnabled = sec.Key("ENABLED").MustBool(false)
BenchmarkCode = sec.Key("BENCHMARKCODE").MustString("https://yangzhx:justfortest123@git.openi.org.cn/yangzhx/detection_benchmark_script.git")
BenchmarkServerHost = sec.Key("HOST").MustString("http://192.168.202.90:3366/")
BenchmarkCode = sec.Key("BENCHMARKCODE").MustString("")
BenchmarkServerHost = sec.Key("HOST").MustString("")
BenchmarkCategory = sec.Key("CATEGORY").MustString("")

sec = Cfg.Section("snn4imagenet")
IsSnn4imagenetEnabled = sec.Key("ENABLED").MustBool(false)
Snn4imagenetCode = sec.Key("SNN4IMAGENETCODE").MustString("https://yult:19910821ylt@git.openi.org.cn/yult/snn4imagenet_script.git")
Snn4imagenetServerHost = sec.Key("HOST").MustString("http://192.168.207.76:8080/")
Snn4imagenetCode = sec.Key("SNN4IMAGENETCODE").MustString("")
Snn4imagenetServerHost = sec.Key("HOST").MustString("")

sec = Cfg.Section("brainscore")
IsBrainScoreEnabled = sec.Key("ENABLED").MustBool(false)
BrainScoreCode = sec.Key("BRAINSCORECODE").MustString("https://yult:19910821ylt@git.openi.org.cn/yult/brainscore_script.git")
BrainScoreServerHost = sec.Key("HOST").MustString("http://192.168.207.76:8080/")
BrainScoreCode = sec.Key("BRAINSCORECODE").MustString("")
BrainScoreServerHost = sec.Key("HOST").MustString("")

sec = Cfg.Section("blockchain")
BlockChainHost = sec.Key("HOST").MustString("http://192.168.136.66:3302/")
CommitValidDate = sec.Key("COMMIT_VALID_DATE").MustString("2021-01-15")

sec = Cfg.Section("obs")
Endpoint = sec.Key("ENDPOINT").MustString("112.95.163.82")
Endpoint = sec.Key("ENDPOINT").MustString("")
AccessKeyID = sec.Key("ACCESS_KEY_ID").MustString("")
SecretAccessKey = sec.Key("SECRET_ACCESS_KEY").MustString("")
Bucket = sec.Key("BUCKET").MustString("testopendata")
Location = sec.Key("LOCATION").MustString("cn-south-222")
BasePath = sec.Key("BASE_PATH").MustString("attachment/")
Bucket = sec.Key("BUCKET").MustString("")
Location = sec.Key("LOCATION").MustString("")
BasePath = sec.Key("BASE_PATH").MustString("")
TrainJobModelPath = sec.Key("TrainJobModel_Path").MustString("job/")
OutPutPath = sec.Key("Output_Path").MustString("output/")
CodePathPrefix = sec.Key("CODE_PATH_PREFIX").MustString("code/")
@@ -1274,17 +1276,17 @@ func NewContext() {
PROXYURL = sec.Key("PROXY_URL").MustString("")

sec = Cfg.Section("modelarts")
ModelArtsHost = sec.Key("ENDPOINT").MustString("112.95.163.80")
IamHost = sec.Key("IAMHOST").MustString("112.95.163.80")
ModelArtsHost = sec.Key("ENDPOINT").MustString("")
IamHost = sec.Key("IAMHOST").MustString("")
ProjectID = sec.Key("PROJECT_ID").MustString("")
ProjectName = sec.Key("PROJECT_NAME").MustString("")
ModelArtsUsername = sec.Key("USERNAME").MustString("")
ModelArtsPassword = sec.Key("PASSWORD").MustString("")
ModelArtsDomain = sec.Key("DOMAIN").MustString("cn-south-222")
ModelArtsDomain = sec.Key("DOMAIN").MustString("")
AllowedOrg = sec.Key("ORGANIZATION").MustString("")
ProfileID = sec.Key("PROFILE_ID").MustString("")
PoolInfos = sec.Key("POOL_INFOS").MustString("")
Flavor = sec.Key("FLAVOR").MustString("modelarts.bm.910.arm.public.2")
Flavor = sec.Key("FLAVOR").MustString("")
ResourcePools = sec.Key("Resource_Pools").MustString("")
Engines = sec.Key("Engines").MustString("")
EngineVersions = sec.Key("Engine_Versions").MustString("")
@@ -1292,10 +1294,10 @@ func NewContext() {
TrainJobFLAVORINFOS = sec.Key("TrainJob_FLAVOR_INFOS").MustString("")

sec = Cfg.Section("elk")
ElkUrl = sec.Key("ELKURL").MustString("http://192.168.207.35:5601/internal/bsearch")
ElkUser = sec.Key("ELKUSER").MustString("Qizhi")
ElkPassword = sec.Key("ELKPASSWORD").MustString("Pcl2020")
Index = sec.Key("INDEX").MustString("filebeat-7.3.2*")
ElkUrl = sec.Key("ELKURL").MustString("")
ElkUser = sec.Key("ELKUSER").MustString("")
ElkPassword = sec.Key("ELKPASSWORD").MustString("")
Index = sec.Key("INDEX").MustString("")
TimeField = sec.Key("TIMEFIELD").MustString(" @timestamptest")
ElkTimeFormat = sec.Key("ELKTIMEFORMAT").MustString("date_time")



+ 4
- 0
modules/storage/local.go View File

@@ -76,3 +76,7 @@ func (l *LocalStorage) PresignedPutURL(path string) (string, error) {
func (l *LocalStorage) HasObject(path string) (bool, error) {
return false, nil
}

func (l *LocalStorage) UploadObject(fileName, filePath string) error {
return nil
}

+ 6
- 0
modules/storage/minio.go View File

@@ -122,3 +122,9 @@ func (m *MinioStorage) HasObject(path string) (bool, error) {

return hasObject, nil
}

//upload object
func (m *MinioStorage) UploadObject(fileName, filePath string) error {
_, err := m.client.FPutObject(m.bucket, fileName, filePath, minio.PutObjectOptions{})
return err
}

+ 1
- 0
modules/storage/storage.go View File

@@ -26,6 +26,7 @@ type ObjectStorage interface {
PresignedGetURL(path string, fileName string) (string, error)
PresignedPutURL(path string) (string, error)
HasObject(path string) (bool, error)
UploadObject(fileName, filePath string) error
}

// Copy copys a file from source ObjectStorage to dest ObjectStorage


+ 37
- 4
routers/repo/cloudbrain.go View File

@@ -74,12 +74,13 @@ func CloudBrainIndex(ctx *context.Context) {
timestamp := time.Now().Unix()
for i, task := range ciTasks {
if task.Status == string(models.JobRunning) && (timestamp-int64(task.Cloudbrain.CreatedUnix) > 10) {
ciTasks[i].CanDebug = true
ciTasks[i].CanDebug = cloudbrain.CanCreateOrDebugJob(ctx)
} else {
ciTasks[i].CanDebug = false
}

ciTasks[i].CanDel = models.CanDelJob(ctx.IsSigned, ctx.User, task)
ciTasks[i].CanDel = cloudbrain.CanDeleteDebugJob(ctx, &task.Cloudbrain)

}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
@@ -88,6 +89,7 @@ func CloudBrainIndex(ctx *context.Context) {

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.HTML(200, tplCloudBrainIndex)
}

@@ -247,6 +249,7 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
}
repo := ctx.Repo.Repository
downloadCode(repo, codePath)
uploadCodeToMinio(codePath + "/", jobName, "/code/")

modelPath := setting.JobPath + jobName + cloudbrain.ModelMountPath
err = os.MkdirAll(modelPath, os.ModePerm)
@@ -265,16 +268,19 @@ func CloudBrainCreate(ctx *context.Context, form auth.CreateCloudBrainForm) {
}
}
downloadRateCode(repo, jobName, setting.BenchmarkCode, benchmarkPath, form.BenchmarkCategory, gpuType)
uploadCodeToMinio(benchmarkPath + "/", jobName, cloudbrain.BenchMarkMountPath + "/")
}

snn4imagenetPath := setting.JobPath + jobName + cloudbrain.Snn4imagenetMountPath
if setting.IsSnn4imagenetEnabled && jobType == string(models.JobTypeSnn4imagenet) {
downloadRateCode(repo, jobName, setting.Snn4imagenetCode, snn4imagenetPath, "", "")
uploadCodeToMinio(snn4imagenetPath + "/", jobName, cloudbrain.Snn4imagenetMountPath + "/")
}

brainScorePath := setting.JobPath + jobName + cloudbrain.BrainScoreMountPath
if setting.IsBrainScoreEnabled && jobType == string(models.JobTypeBrainScore) {
downloadRateCode(repo, jobName, setting.BrainScoreCode, brainScorePath, "", "")
uploadCodeToMinio(brainScorePath + "/", jobName, cloudbrain.BrainScoreMountPath + "/")
}

err = cloudbrain.GenerateTask(ctx, jobName, image, command, uuid, codePath, modelPath, benchmarkPath, snn4imagenetPath, brainScorePath, jobType, gpuQueue, resourceSpecId)
@@ -701,8 +707,8 @@ func downloadRateCode(repo *models.Repository, taskName, gitPath, codePath, benc

command := "git clone " + gitPath + " " + codePath
cmd := exec.Command("/bin/bash", "-c", command)
output, err := cmd.Output()
log.Info(string(output))
_, err = cmd.Output()
if err != nil {
log.Error("exec.Command(%s) failed:%v", command, err)
return err
@@ -739,6 +745,33 @@ func downloadRateCode(repo *models.Repository, taskName, gitPath, codePath, benc
return nil
}

func uploadCodeToMinio(codePath, jobName, parentDir string) error {
files, err := readDir(codePath)
if err != nil {
log.Error("readDir(%s) failed: %s", codePath, err.Error())
return err
}

for _, file := range files {
if file.IsDir() {
if err = uploadCodeToMinio(codePath+file.Name()+"/", jobName, parentDir+file.Name()+"/"); err != nil {
log.Error("uploadCodeToMinio(%s) failed: %s", file.Name(), err.Error())
return err
}
} else {
destObject := setting.CBCodePathPrefix + jobName + parentDir + file.Name()
sourceFile := codePath + file.Name()
err = storage.Attachments.UploadObject(destObject, sourceFile)
if err != nil {
log.Error("UploadObject(%s) failed: %s", file.Name(), err.Error())
return err
}
}
}

return nil
}

func SyncCloudbrainStatus() {
cloudBrains, err := models.GetCloudBrainUnStoppedJob()
if err != nil {


+ 11
- 1
routers/repo/modelarts.go View File

@@ -11,6 +11,8 @@ import (
"strings"
"time"

"code.gitea.io/gitea/modules/cloudbrain"

"code.gitea.io/gitea/models"
"code.gitea.io/gitea/modules/auth"
"code.gitea.io/gitea/modules/base"
@@ -68,10 +70,11 @@ func NotebookIndex(ctx *context.Context) {

for i, task := range ciTasks {
if task.Status == string(models.JobRunning) {
ciTasks[i].CanDebug = true
ciTasks[i].CanDebug = cloudbrain.CanCreateOrDebugJob(ctx)
} else {
ciTasks[i].CanDebug = false
}
ciTasks[i].CanDel = cloudbrain.CanDeleteDebugJob(ctx, &task.Cloudbrain)
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
@@ -80,6 +83,7 @@ func NotebookIndex(ctx *context.Context) {

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = ciTasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.HTML(200, tplModelArtsNotebookIndex)
}

@@ -301,12 +305,18 @@ func TrainJobIndex(ctx *context.Context) {
return
}

for i, task := range tasks {
tasks[i].CanDel = cloudbrain.CanDeleteTrainJob(ctx, &task.Cloudbrain)
tasks[i].CanModify = cloudbrain.CanModifyJob(ctx, &task.Cloudbrain)
}

pager := context.NewPagination(int(count), setting.UI.IssuePagingNum, page, 5)
pager.SetDefaultParams(ctx)
ctx.Data["Page"] = pager

ctx.Data["PageIsCloudBrain"] = true
ctx.Data["Tasks"] = tasks
ctx.Data["CanCreate"] = cloudbrain.CanCreateOrDebugJob(ctx)
ctx.HTML(200, tplModelArtsTrainJobIndex)
}



+ 27
- 21
templates/base/head_navbar.tmpl View File

@@ -17,19 +17,23 @@

{{if .IsSigned}}

<div class="ui dropdown item">
{{.i18n.Tr "index"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
<div class=" item" >
<div class="dropdown-menu">
<a class=" item" href="/dashboard">
<span > {{.i18n.Tr "index"}} &nbsp <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
</div>

<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
<div class="ui dropdown item" id='dropdown_explore'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
@@ -43,24 +47,26 @@
</div>
</div>
{{else if .IsLandingPageHome}}


<div class="ui dropdown item">
{{.i18n.Tr "home"}}
<i class="dropdown icon"></i>
<div class="menu">
<!-- 未登录跳转登录界面 -->
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
<div class=" item">
<div class="dropdown-menu">
<a class=" item" href="/user/login">
<span > {{.i18n.Tr "home"}} &nbsp <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
</div>

<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
<div class="ui dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
<div class="menu" >
<a class="item" href="{{AppSubUrl}}/explore/users">{{.i18n.Tr "explore.users"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/organizations">{{.i18n.Tr "explore.organizations"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/images">{{.i18n.Tr "explore.images"}}</a>


+ 24
- 17
templates/base/head_navbar_fluid.tmpl View File

@@ -17,18 +17,22 @@

{{if .IsSigned}}
<div class="ui dropdown item">
{{.i18n.Tr "index"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
<div class="item" >
<div class="dropdown-menu">
<a class=" item" href="/dashboard">
<span > {{.i18n.Tr "index"}} &nbsp <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
<div class="ui dropdown item" id='dropdown_explore'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
@@ -42,19 +46,22 @@
</div>
</div>
{{else if .IsLandingPageHome}}
<div class="ui dropdown item">
{{.i18n.Tr "home"}}
<i class="dropdown icon"></i>
<div class="menu">
<!-- 未登录跳转登录界面 -->
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
<div class="item" >
<div class="dropdown-menu">
<a class=" item" href="/user/login">
<span > {{.i18n.Tr "home"}} &nbsp <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
<div class="ui dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">


+ 25
- 19
templates/base/head_navbar_home.tmpl View File

@@ -9,18 +9,22 @@
</div>

{{if .IsSigned}}
<div class="ui dropdown item">
{{.i18n.Tr "index"}}
<i class="dropdown icon"></i>
<div class="menu">
<a class="item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
<div class="item" >
<div class="dropdown-menu">
<a class=" item" href="/dashboard">
<span > {{.i18n.Tr "index"}} &nbsp <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/issues">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/pulls">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/milestones">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
</div>
<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
<div class="ui dropdown item" id='dropdown_explore'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">
@@ -34,21 +38,23 @@
</div>
</div>
{{else if .IsLandingPageHome}}

<div class="ui dropdown item">
{{.i18n.Tr "home"}}
<i class="dropdown icon"></i>
<div class="menu">
<!-- 未登录跳转登录界面 -->
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a class="item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
<div class="item" >
<div class="dropdown-menu">
<a class=" item" href="/user/login">
<span > {{.i18n.Tr "home"}} &nbsp <i class="dropdown icon"></i></span>
</a>
<div class="dropdown-content" style="min-width: 110px;border-radius:4px">
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "issues"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "pull_requests"}}</a>
<a style="border: none;color: #000;" class=" item" href="{{AppSubUrl}}/user/login">{{.i18n.Tr "milestones"}}</a>
</div>
</div>
</div>

<a class="item" href="{{AppSubUrl}}/explore/repos">{{.i18n.Tr "custom.head.project"}}</a>
<a class="item" href="{{AppSubUrl}}/explore/datasets">{{.i18n.Tr "custom.head.dataset"}}</a>
<div class="ui dropdown item">
<div class="ui dropdown item" id='dropdown_PageHome'>
{{.i18n.Tr "explore"}}
<i class="dropdown icon"></i>
<div class="menu">


+ 9
- 0
web_src/js/index.js View File

@@ -2935,6 +2935,7 @@ $(document).ready(async () => {
initNotificationsTable();
initNotificationCount();
initTribute();
initDropDown();

// Repo clone url.
if ($('#repo-clone-url').length > 0) {
@@ -4116,3 +4117,11 @@ $.get(`${window.config.StaticUrlPrefix}/img/svg/icons.svg`, (data) => {
div.innerHTML = new XMLSerializer().serializeToString(data.documentElement);
document.body.insertBefore(div, document.body.childNodes[0]);
});
function initDropDown() {
$("#dropdown_PageHome").dropdown({
on:'hover' ,//鼠标悬浮显示,默认值是click
});
$("#dropdown_explore").dropdown({
on:'hover' ,//鼠标悬浮显示,默认值是click
});
}

Loading…
Cancel
Save