@@ -419,6 +419,9 @@ | |||
.leftline02-2 { | |||
width: calc(50% - 8.0em); | |||
} | |||
.i-env .ui.cards>.card>.content .description{ | |||
display: none; | |||
} | |||
} | |||
@media only screen and (min-width: 768px) and (max-width: 991px) { | |||
@@ -58,7 +58,7 @@ func createOutdatedPR(t *testing.T, actor, forkOrg *models.User) *models.PullReq | |||
assert.NoError(t, err) | |||
assert.NotEmpty(t, baseRepo) | |||
headRepo, err := repo_module.ForkRepository(actor, forkOrg, baseRepo, "repo-pr-update", "desc") | |||
headRepo, err := repo_module.ForkRepository(actor, forkOrg, baseRepo, "repo-pr-update", "desc", "") | |||
assert.NoError(t, err) | |||
assert.NotEmpty(t, headRepo) | |||
@@ -172,12 +172,24 @@ func (a *Action) GetRepoName() string { | |||
return a.Repo.Name | |||
} | |||
// GetRepoName returns the name of the action repository. | |||
func (a *Action) GetRepoDisplayName() string { | |||
a.loadRepo() | |||
return a.Repo.DisplayName() | |||
} | |||
// ShortRepoName returns the name of the action repository | |||
// trimmed to max 33 chars. | |||
func (a *Action) ShortRepoName() string { | |||
return base.EllipsisString(a.GetRepoName(), 33) | |||
} | |||
// ShortRepoName returns the name of the action repository | |||
// trimmed to max 33 chars. | |||
func (a *Action) ShortRepoDisplayName() string { | |||
return base.EllipsisString(a.GetRepoDisplayName(), 33) | |||
} | |||
// GetRepoPath returns the virtual path to the action repository. | |||
func (a *Action) GetRepoPath() string { | |||
return path.Join(a.GetRepoUserName(), a.GetRepoName()) | |||
@@ -189,6 +201,12 @@ func (a *Action) ShortRepoPath() string { | |||
return path.Join(a.ShortRepoUserName(), a.ShortRepoName()) | |||
} | |||
// ShortRepoPath returns the virtual path to the action repository | |||
// trimmed to max 20 + 1 + 33 chars. | |||
func (a *Action) ShortRepoFullDisplayName() string { | |||
return path.Join(a.ShortRepoUserName(), a.ShortRepoDisplayName()) | |||
} | |||
// GetRepoLink returns relative link to action repository. | |||
func (a *Action) GetRepoLink() string { | |||
if len(setting.AppSubURL) > 0 { | |||
@@ -22,6 +22,16 @@ const ( | |||
NPUResource = "NPU" | |||
GPUResource = "CPU/GPU" | |||
//notebook storage category | |||
EVSCategory = "EVS" | |||
EFSCategory = "EFS" | |||
ManagedOwnership = "MANAGED" | |||
DetectedOwnership = "DEDICATED" | |||
NotebookFeature = "NOTEBOOK" | |||
DefaultFeature = "DEFAULT" | |||
JobWaiting CloudbrainStatus = "WAITING" | |||
JobStopped CloudbrainStatus = "STOPPED" | |||
JobSucceeded CloudbrainStatus = "SUCCEEDED" | |||
@@ -520,6 +530,25 @@ type CloudBrainResult struct { | |||
Msg string `json:"msg"` | |||
} | |||
type CreateNotebook2Params struct { | |||
JobName string `json:"name"` | |||
Description string `json:"description"` | |||
Duration int64 `json:"duration"` //ms | |||
Feature string `json:"feature"` | |||
PoolID string `json:"pool_id"` | |||
Flavor string `json:"flavor"` | |||
ImageID string `json:"image_id"` | |||
WorkspaceID string `json:"workspace_id"` | |||
Volume VolumeReq `json:"volume"` | |||
} | |||
type VolumeReq struct { | |||
Capacity int `json:"capacity"` | |||
Category string `json:"category"` | |||
Ownership string `json:"ownership"` | |||
Uri string `json:"uri"` | |||
} | |||
type CreateNotebookParams struct { | |||
JobName string `json:"name"` | |||
Description string `json:"description"` | |||
@@ -637,6 +666,42 @@ type GetNotebookResult struct { | |||
} `json:"spec"` | |||
} | |||
type GetNotebook2Result struct { | |||
ErrorCode string `json:"error_code"` | |||
ErrorMsg string `json:"error_msg"` | |||
FailReason string `json:"fail_reason"` | |||
ID string `json:"id"` | |||
Name string `json:"name"` | |||
Description string `json:"description"` | |||
Status string `json:"status"` | |||
Url string `json:"url"` //实例访问的URL | |||
Token string `json:"token"` //notebook鉴权使用的token信息 | |||
Flavor string `json:"flavor"` | |||
CreateTime string | |||
LatestUpdateTime string | |||
CreateAt int64 `json:"create_at"` //实例创建的时间,UTC毫秒 | |||
UpdateAt int64 `json:"update_at"` //实例最后更新(不包括保活心跳)的时间,UTC毫秒 | |||
Image struct { | |||
Name string `json:"name"` | |||
Status string `json:"status"` | |||
QueuingNum int `json:"queuing_num"` | |||
QueueLeftTime int `json:"queue_left_time"` //s | |||
Duration int `json:"duration"` //auto_stop_time s | |||
} `json:"image"` | |||
Lease struct { | |||
CreateTime int64 `json:"create_at"` //实例创建的时间,UTC毫秒 | |||
Duration int64 `json:"duration"` //实例运行时长,以创建时间为起点计算,即“创建时间+duration > 当前时刻”时,系统会自动停止实例 | |||
UpdateTime int64 `json:"update_at"` //实例最后更新(不包括保活心跳)的时间,UTC毫秒 | |||
} `json:"lease"` //实例自动停止的倒计时信息 | |||
VolumeRes struct { | |||
Capacity int `json:"capacity"` | |||
Category string `json:"category"` | |||
MountPath string `json:"mount_path"` | |||
Ownership string `json:"ownership"` | |||
Status string `json:"status"` | |||
} `json:"volume"` | |||
} | |||
type GetTokenParams struct { | |||
Auth Auth `json:"auth"` | |||
} | |||
@@ -690,6 +755,7 @@ type NotebookActionResult struct { | |||
ErrorMsg string `json:"error_msg"` | |||
CurrentStatus string `json:"current_status"` | |||
PreviousState string `json:"previous_state"` | |||
Status string `json:"status"` | |||
} | |||
type NotebookGetJobTokenResult struct { | |||
@@ -12,6 +12,7 @@ import ( | |||
"errors" | |||
"fmt" | |||
"html/template" | |||
"math/rand" | |||
"xorm.io/xorm" | |||
"code.gitea.io/gitea/modules/blockchain" | |||
@@ -139,6 +140,7 @@ func NewRepoContext() { | |||
// RepositoryStatus defines the status of repository | |||
type RepositoryStatus int | |||
type RepoBlockChainStatus int | |||
type RepoType int | |||
// all kinds of RepositoryStatus | |||
const ( | |||
@@ -152,6 +154,11 @@ const ( | |||
RepoBlockChainFailed | |||
) | |||
const ( | |||
RepoNormal RepoType = iota | |||
RepoCourse | |||
) | |||
// Repository represents a git repository. | |||
type Repository struct { | |||
ID int64 `xorm:"pk autoincr"` | |||
@@ -165,7 +172,8 @@ type Repository struct { | |||
OriginalServiceType api.GitServiceType `xorm:"index"` | |||
OriginalURL string `xorm:"VARCHAR(2048)"` | |||
DefaultBranch string | |||
CreatorID int64 `xorm:"INDEX NOT NULL DEFAULT 0"` | |||
Creator *User `xorm:"-"` | |||
NumWatches int | |||
NumStars int | |||
NumForks int | |||
@@ -174,11 +182,12 @@ type Repository struct { | |||
NumOpenIssues int `xorm:"-"` | |||
NumPulls int | |||
NumClosedPulls int | |||
NumOpenPulls int `xorm:"-"` | |||
NumMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||
NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||
NumOpenMilestones int `xorm:"-"` | |||
NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` | |||
NumOpenPulls int `xorm:"-"` | |||
NumMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||
NumClosedMilestones int `xorm:"NOT NULL DEFAULT 0"` | |||
NumOpenMilestones int `xorm:"-"` | |||
NumCommit int64 `xorm:"NOT NULL DEFAULT 0"` | |||
RepoType RepoType `xorm:"NOT NULL DEFAULT 0"` | |||
IsPrivate bool `xorm:"INDEX"` | |||
IsEmpty bool `xorm:"INDEX"` | |||
@@ -221,8 +230,10 @@ type Repository struct { | |||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` | |||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` | |||
Hot int64 `xorm:"-"` | |||
Active int64 `xorm:"-"` | |||
Hot int64 `xorm:"-"` | |||
Active int64 `xorm:"-"` | |||
Alias string `xorm:"INDEX"` | |||
LowerAlias string `xorm:"INDEX"` | |||
} | |||
// SanitizedOriginalURL returns a sanitized OriginalURL | |||
@@ -233,6 +244,14 @@ func (repo *Repository) SanitizedOriginalURL() string { | |||
return util.SanitizeURLCredentials(repo.OriginalURL, false) | |||
} | |||
// GetAlias returns a sanitized OriginalURL | |||
func (repo *Repository) DisplayName() string { | |||
if repo.Alias == "" { | |||
return repo.Name | |||
} | |||
return repo.Alias | |||
} | |||
// ColorFormat returns a colored string to represent this repo | |||
func (repo *Repository) ColorFormat(s fmt.State) { | |||
var ownerName interface{} | |||
@@ -286,6 +305,11 @@ func (repo *Repository) FullName() string { | |||
return repo.OwnerName + "/" + repo.Name | |||
} | |||
// FullDisplayName returns the repository full display name | |||
func (repo *Repository) FullDisplayName() string { | |||
return repo.OwnerName + "/" + repo.DisplayName() | |||
} | |||
// HTMLURL returns the repository HTML URL | |||
func (repo *Repository) HTMLURL() string { | |||
return setting.AppURL + repo.FullName() | |||
@@ -385,7 +409,9 @@ func (repo *Repository) innerAPIFormat(e Engine, mode AccessMode, isParent bool) | |||
ID: repo.ID, | |||
Owner: repo.Owner.APIFormat(), | |||
Name: repo.Name, | |||
Alias: repo.Alias, | |||
FullName: repo.FullName(), | |||
FullDisplayName: repo.FullDisplayName(), | |||
Description: repo.Description, | |||
Private: repo.IsPrivate, | |||
Template: repo.IsTemplate, | |||
@@ -548,6 +574,19 @@ func (repo *Repository) GetOwner() error { | |||
return repo.getOwner(x) | |||
} | |||
func (repo *Repository) getCreator(e Engine) (err error) { | |||
if repo.CreatorID == 0 { | |||
return nil | |||
} | |||
repo.Creator, err = getUserByID(e, repo.CreatorID) | |||
return err | |||
} | |||
func (repo *Repository) GetCreator() error { | |||
return repo.getCreator(x) | |||
} | |||
func (repo *Repository) mustOwner(e Engine) *User { | |||
if err := repo.getOwner(e); err != nil { | |||
return &User{ | |||
@@ -921,17 +960,50 @@ func (repo *Repository) DescriptionHTML() template.HTML { | |||
return template.HTML(markup.Sanitize(string(desc))) | |||
} | |||
func isRepositoryExist(e Engine, u *User, repoName string) (bool, error) { | |||
has, err := e.Get(&Repository{ | |||
OwnerID: u.ID, | |||
LowerName: strings.ToLower(repoName), | |||
}) | |||
return has && com.IsDir(RepoPath(u.Name, repoName)), err | |||
func isRepositoryExist(e Engine, u *User, repoName string, alias string) (bool, error) { | |||
var cond = builder.NewCond() | |||
cond = cond.And(builder.Eq{"owner_id": u.ID}) | |||
if alias != "" { | |||
subCon := builder.NewCond() | |||
subCon = subCon.Or(builder.Eq{"lower_alias": strings.ToLower(alias)}, builder.Eq{"lower_name": strings.ToLower(repoName)}) | |||
cond = cond.And(subCon) | |||
} else { | |||
cond = cond.And(builder.Eq{"lower_name": strings.ToLower(repoName)}) | |||
} | |||
count, err := e.Where(cond).Count(&Repository{}) | |||
return count > 0 || com.IsDir(RepoPath(u.Name, repoName)), err | |||
} | |||
// IsRepositoryExist returns true if the repository with given name under user has already existed. | |||
func IsRepositoryExist(u *User, repoName string) (bool, error) { | |||
return isRepositoryExist(x, u, repoName) | |||
func IsRepositoryExist(u *User, repoName string, alias string) (bool, error) { | |||
return isRepositoryExist(x, u, repoName, alias) | |||
} | |||
// IsRepositoryAliasExist returns true if the repository with given alias under user has already existed. | |||
func IsRepositoryAliasExist(u *User, alias string) (bool, error) { | |||
return isRepositoryAliasExist(x, u, alias) | |||
} | |||
func isRepositoryAliasExist(e Engine, u *User, alias string) (bool, error) { | |||
var cond = builder.NewCond() | |||
cond = cond.And(builder.Eq{"owner_id": u.ID}) | |||
cond = cond.And(builder.Eq{"lower_alias": strings.ToLower(alias)}) | |||
count, err := e.Where(cond).Count(&Repository{}) | |||
return count > 0, err | |||
} | |||
func IsRepositoryAliasAvailable(doer *User, alias string) error { | |||
if err := IsUsableRepoAlias(alias); err != nil { | |||
return err | |||
} | |||
has, err := IsRepositoryAliasExist(doer, alias) | |||
if err != nil { | |||
return fmt.Errorf("IsRepositoryExist: %v", err) | |||
} else if has { | |||
return ErrRepoAlreadyExist{doer.Name, alias} | |||
} | |||
return nil | |||
} | |||
// CloneLink represents different types of clone URLs of repository. | |||
@@ -975,20 +1047,24 @@ func (repo *Repository) CloneLink() (cl *CloneLink) { | |||
} | |||
// CheckCreateRepository check if could created a repository | |||
func CheckCreateRepository(doer, u *User, name string) error { | |||
func CheckCreateRepository(doer, u *User, repoName, alias string) error { | |||
if !doer.CanCreateRepo() { | |||
return ErrReachLimitOfRepo{u.MaxRepoCreation} | |||
} | |||
if err := IsUsableRepoName(name); err != nil { | |||
if err := IsUsableRepoName(repoName); err != nil { | |||
return err | |||
} | |||
if err := IsUsableRepoAlias(alias); err != nil { | |||
return err | |||
} | |||
has, err := isRepositoryExist(x, u, name) | |||
has, err := isRepositoryExist(x, u, repoName, alias) | |||
if err != nil { | |||
return fmt.Errorf("IsRepositoryExist: %v", err) | |||
} else if has { | |||
return ErrRepoAlreadyExist{u.Name, name} | |||
return ErrRepoAlreadyExist{u.Name, repoName} | |||
} | |||
return nil | |||
} | |||
@@ -996,6 +1072,7 @@ func CheckCreateRepository(doer, u *User, name string) error { | |||
// CreateRepoOptions contains the create repository options | |||
type CreateRepoOptions struct { | |||
Name string | |||
Alias string | |||
Description string | |||
OriginalURL string | |||
GitServiceType api.GitServiceType | |||
@@ -1008,6 +1085,8 @@ type CreateRepoOptions struct { | |||
IsMirror bool | |||
AutoInit bool | |||
Status RepositoryStatus | |||
IsCourse bool | |||
Topics []string | |||
} | |||
// GetRepoInitFile returns repository init files | |||
@@ -1036,8 +1115,10 @@ func GetRepoInitFile(tp, name string) ([]byte, error) { | |||
} | |||
var ( | |||
reservedRepoNames = []string{".", ".."} | |||
reservedRepoPatterns = []string{"*.git", "*.wiki"} | |||
reservedRepoNames = []string{".", ".."} | |||
reservedRepoPatterns = []string{"*.git", "*.wiki"} | |||
reservedRepoAliasNames = []string{} | |||
reservedRepoAliasPatterns = []string{} | |||
) | |||
// IsUsableRepoName returns true when repository is usable | |||
@@ -1045,19 +1126,34 @@ func IsUsableRepoName(name string) error { | |||
return isUsableName(reservedRepoNames, reservedRepoPatterns, name) | |||
} | |||
// IsUsableRepoAlias returns true when repository alias is usable | |||
func IsUsableRepoAlias(name string) error { | |||
return isUsableName(reservedRepoAliasNames, reservedRepoAliasPatterns, name) | |||
} | |||
// CreateRepository creates a repository for the user/organization. | |||
func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error) { | |||
func CreateRepository(ctx DBContext, doer, u *User, repo *Repository, opts ...CreateRepoOptions) (err error) { | |||
if repo.Alias == "" { | |||
repo.Alias = repo.Name | |||
} | |||
repo.LowerAlias = strings.ToLower(repo.Alias) | |||
if err = IsUsableRepoName(repo.Name); err != nil { | |||
return err | |||
} | |||
has, err := isRepositoryExist(ctx.e, u, repo.Name) | |||
if err := IsUsableRepoAlias(repo.Alias); err != nil { | |||
return err | |||
} | |||
has, err := isRepositoryExist(ctx.e, u, repo.Name, repo.Alias) | |||
if err != nil { | |||
return fmt.Errorf("IsRepositoryExist: %v", err) | |||
} else if has { | |||
return ErrRepoAlreadyExist{u.Name, repo.Name} | |||
} | |||
isCourse := isCourse(opts) | |||
if isCourse { | |||
repo.CreatorID = doer.ID | |||
} | |||
if _, err = ctx.e.Insert(repo); err != nil { | |||
return err | |||
} | |||
@@ -1091,17 +1187,23 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error | |||
Config: &PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true}, | |||
}) | |||
} else if tp == UnitTypeDatasets { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &DatasetConfig{EnableDataset: true}, | |||
}) | |||
if !isCourse { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &DatasetConfig{EnableDataset: true}, | |||
}) | |||
} | |||
} else if tp == UnitTypeCloudBrain { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &CloudBrainConfig{EnableCloudBrain: true}, | |||
}) | |||
if !isCourse { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &CloudBrainConfig{EnableCloudBrain: true}, | |||
}) | |||
} | |||
} else if tp == UnitTypeBlockChain { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
@@ -1109,11 +1211,13 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error | |||
Config: &BlockChainConfig{EnableBlockChain: true}, | |||
}) | |||
} else if tp == UnitTypeModelManage { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &ModelManageConfig{EnableModelManage: true}, | |||
}) | |||
if !isCourse { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
Type: tp, | |||
Config: &ModelManageConfig{EnableModelManage: true}, | |||
}) | |||
} | |||
} else { | |||
units = append(units, RepoUnit{ | |||
RepoID: repo.ID, | |||
@@ -1183,6 +1287,14 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error | |||
return nil | |||
} | |||
func isCourse(opts []CreateRepoOptions) bool { | |||
var isCourse = false | |||
if len(opts) > 0 { | |||
isCourse = opts[0].IsCourse | |||
} | |||
return isCourse | |||
} | |||
func countRepositories(userID int64, private bool) int64 { | |||
sess := x.Where("id > 0") | |||
@@ -1233,7 +1345,7 @@ func TransferOwnership(doer *User, newOwnerName string, repo *Repository) error | |||
} | |||
// Check if new owner has repository with same name. | |||
has, err := IsRepositoryExist(newOwner, repo.Name) | |||
has, err := IsRepositoryExist(newOwner, repo.Name, repo.Alias) | |||
if err != nil { | |||
return fmt.Errorf("IsRepositoryExist: %v", err) | |||
} else if has { | |||
@@ -1366,7 +1478,7 @@ func ChangeRepositoryName(doer *User, repo *Repository, newRepoName string) (err | |||
return err | |||
} | |||
has, err := IsRepositoryExist(repo.Owner, newRepoName) | |||
has, err := IsRepositoryExist(repo.Owner, newRepoName, "") | |||
if err != nil { | |||
return fmt.Errorf("IsRepositoryExist: %v", err) | |||
} else if has { | |||
@@ -1848,6 +1960,26 @@ func getRepositoryByOwnerAndName(e Engine, ownerName, repoName string) (*Reposit | |||
return &repo, nil | |||
} | |||
// GetRepositoryByOwnerAndAlias returns the repository by given ownername and reponame. | |||
func GetRepositoryByOwnerAndAlias(ownerName, alias string) (*Repository, error) { | |||
return getRepositoryByOwnerAndAlias(x, ownerName, alias) | |||
} | |||
func getRepositoryByOwnerAndAlias(e Engine, ownerName, alias string) (*Repository, error) { | |||
var repo Repository | |||
has, err := e.Table("repository").Select("repository.*"). | |||
Join("INNER", "`user`", "`user`.id = repository.owner_id"). | |||
Where("repository.lower_alias = ?", strings.ToLower(alias)). | |||
And("`user`.lower_name = ?", strings.ToLower(ownerName)). | |||
Get(&repo) | |||
if err != nil { | |||
return nil, err | |||
} else if !has { | |||
return nil, ErrRepoNotExist{0, 0, ownerName, alias} | |||
} | |||
return &repo, nil | |||
} | |||
// GetRepositoryByName returns the repository by given name under user if exists. | |||
func GetRepositoryByName(ownerID int64, name string) (*Repository, error) { | |||
repo := &Repository{ | |||
@@ -2521,6 +2653,14 @@ func UpdateRepositoryCommitNum(repo *Repository) error { | |||
return nil | |||
} | |||
func GenerateDefaultRepoName(ownerName string) string { | |||
if len(ownerName) > 5 { | |||
ownerName = ownerName[:5] | |||
} | |||
now := time.Now().Format("20060102150405") | |||
return ownerName + now + fmt.Sprint(rand.Intn(10)) | |||
} | |||
type RepoFile struct { | |||
CommitId string | |||
Content []byte | |||
@@ -19,6 +19,7 @@ import ( | |||
// GenerateRepoOptions contains the template units to generate | |||
type GenerateRepoOptions struct { | |||
Name string | |||
Alias string | |||
Description string | |||
Private bool | |||
GitContent bool | |||
@@ -48,9 +48,12 @@ func (repos RepositoryList) loadAttributes(e Engine) error { | |||
set := make(map[int64]struct{}) | |||
repoIDs := make([]int64, len(repos)) | |||
setCreator := make(map[int64]struct{}) | |||
for i := range repos { | |||
set[repos[i].OwnerID] = struct{}{} | |||
repoIDs[i] = repos[i].ID | |||
setCreator[repos[i].CreatorID] = struct{}{} | |||
} | |||
// Load owners. | |||
@@ -61,8 +64,18 @@ func (repos RepositoryList) loadAttributes(e Engine) error { | |||
Find(&users); err != nil { | |||
return fmt.Errorf("find users: %v", err) | |||
} | |||
//Load creator | |||
creators := make(map[int64]*User, len(set)) | |||
if err := e. | |||
Where("id > 0"). | |||
In("id", keysInt64(setCreator)). | |||
Find(&creators); err != nil { | |||
return fmt.Errorf("find create repo users: %v", err) | |||
} | |||
for i := range repos { | |||
repos[i].Owner = users[repos[i].OwnerID] | |||
repos[i].Creator = creators[repos[i].CreatorID] | |||
} | |||
// Load primary language. | |||
@@ -174,6 +187,10 @@ type SearchRepoOptions struct { | |||
// True -> include just has milestones | |||
// False -> include just has no milestone | |||
HasMilestones util.OptionalBool | |||
// None -> include all repos | |||
// True -> include just courses | |||
// False -> include just no courses | |||
Course util.OptionalBool | |||
} | |||
//SearchOrderBy is used to sort the result | |||
@@ -200,8 +217,8 @@ const ( | |||
SearchOrderByForks SearchOrderBy = "num_forks ASC" | |||
SearchOrderByForksReverse SearchOrderBy = "num_forks DESC" | |||
SearchOrderByDownloadTimes SearchOrderBy = "download_times DESC" | |||
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC" | |||
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC" | |||
SearchOrderByHot SearchOrderBy = "(num_watches + num_stars + num_forks + clone_cnt) DESC" | |||
SearchOrderByActive SearchOrderBy = "(num_issues + num_pulls + num_commit) DESC" | |||
) | |||
// SearchRepositoryCondition creates a query condition according search repository options | |||
@@ -321,6 +338,7 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { | |||
var likes = builder.NewCond() | |||
for _, v := range strings.Split(opts.Keyword, ",") { | |||
likes = likes.Or(builder.Like{"lower_name", strings.ToLower(v)}) | |||
likes = likes.Or(builder.Like{"alias", v}) | |||
if opts.IncludeDescription { | |||
likes = likes.Or(builder.Like{"LOWER(description)", strings.ToLower(v)}) | |||
} | |||
@@ -350,6 +368,10 @@ func SearchRepositoryCondition(opts *SearchRepoOptions) builder.Cond { | |||
cond = cond.And(builder.Eq{"is_mirror": opts.Mirror == util.OptionalBoolTrue}) | |||
} | |||
if opts.Course == util.OptionalBoolTrue { | |||
cond = cond.And(builder.Eq{"repo_type": RepoCourse}) | |||
} | |||
if opts.Actor != nil && opts.Actor.IsRestricted { | |||
cond = cond.And(accessibleRepositoryCondition(opts.Actor)) | |||
} | |||
@@ -28,6 +28,7 @@ type OfficialTagRepos struct { | |||
type TagReposBrief struct { | |||
RepoID int64 | |||
RepoName string | |||
Alias string | |||
TagID int64 | |||
} | |||
@@ -41,7 +42,7 @@ type TagsDetail struct { | |||
TagId int64 | |||
TagName string | |||
TagLimit int | |||
RepoList []Repository | |||
RepoList []*Repository | |||
} | |||
func GetTagByID(id int64) (*OfficialTag, error) { | |||
@@ -97,7 +98,7 @@ func UpdateTagReposByID(tagID, orgID int64, repoIdList []int64) error { | |||
func GetTagRepos(tagID, orgID int64) ([]TagReposSelected, error) { | |||
t := make([]TagReposBrief, 0) | |||
const SQLCmd = "select t1.id as repo_id,t1.name as repo_name,t2.id as tag_id from repository t1 left join official_tag_repos t2 on (t1.id = t2.repo_id and t2.tag_id = ?) where t1.owner_id = ? and t1.is_private = false order by t1.updated_unix desc" | |||
const SQLCmd = "select t1.id as repo_id,t1.name as repo_name,t1.alias,t2.id as tag_id from repository t1 left join official_tag_repos t2 on (t1.id = t2.repo_id and t2.tag_id = ?) where t1.owner_id = ? and t1.is_private = false order by t1.updated_unix desc" | |||
if err := x.SQL(SQLCmd, tagID, orgID).Find(&t); err != nil { | |||
return nil, err | |||
@@ -108,9 +109,13 @@ func GetTagRepos(tagID, orgID int64) ([]TagReposSelected, error) { | |||
if v.TagID > 0 { | |||
selected = true | |||
} | |||
repoName := v.Alias | |||
if v.Alias == "" { | |||
repoName = v.RepoName | |||
} | |||
r = append(r, TagReposSelected{ | |||
RepoID: v.RepoID, | |||
RepoName: v.RepoName, | |||
RepoName: repoName, | |||
Selected: selected, | |||
}) | |||
} | |||
@@ -141,8 +146,8 @@ func GetAllOfficialTagRepos(orgID int64, isOwner bool) ([]TagsDetail, error) { | |||
return result, nil | |||
} | |||
func GetOfficialTagDetail(orgID, tagId int64) ([]Repository, error) { | |||
t := make([]Repository, 0) | |||
func GetOfficialTagDetail(orgID, tagId int64) ([]*Repository, error) { | |||
t := make([]*Repository, 0) | |||
const SQLCmd = "select t2.* from official_tag_repos t1 inner join repository t2 on t1.repo_id = t2.id where t1.org_id = ? and t1.tag_id=? order by t2.updated_unix desc" | |||
if err := x.SQL(SQLCmd, orgID, tagId).Find(&t); err != nil { | |||
@@ -186,6 +186,8 @@ func validate(errs binding.Errors, data map[string]interface{}, f Form, l macaro | |||
data["ErrorMsg"] = trName + l.Tr("form.include_error", GetInclude(field)) | |||
case validation.ErrGlobPattern: | |||
data["ErrorMsg"] = trName + l.Tr("form.glob_pattern_error", errs[0].Message) | |||
case validation.ErrAlphaDashDotChinese: | |||
data["ErrorMsg"] = trName + l.Tr("form.alpha_dash_dot_chinese_error") | |||
default: | |||
data["ErrorMsg"] = l.Tr("form.unknown_error") + " " + errs[0].Classification | |||
} | |||
@@ -29,6 +29,7 @@ import ( | |||
type CreateRepoForm struct { | |||
UID int64 `binding:"Required"` | |||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
Alias string `binding:"Required;MaxSize(100);AlphaDashDotChinese"` | |||
Private bool | |||
Description string `binding:"MaxSize(1024)"` | |||
DefaultBranch string `binding:"GitRefName;MaxSize(100)"` | |||
@@ -62,6 +63,7 @@ type MigrateRepoForm struct { | |||
UID int64 `json:"uid" binding:"Required"` | |||
// required: true | |||
RepoName string `json:"repo_name" binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
Alias string `json:"alias" binding:"Required;AlphaDashDotChinese;MaxSize(100)"` | |||
Mirror bool `json:"mirror"` | |||
Private bool `json:"private"` | |||
Description string `json:"description" binding:"MaxSize(255)"` | |||
@@ -109,6 +111,7 @@ func (f MigrateRepoForm) ParseRemoteAddr(user *models.User) (string, error) { | |||
// RepoSettingForm form for changing repository settings | |||
type RepoSettingForm struct { | |||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
Alias string `binding:"Required;AlphaDashDotChinese;MaxSize(100)"` | |||
Description string `binding:"MaxSize(255)"` | |||
Website string `binding:"ValidUrl;MaxSize(255)"` | |||
Interval string | |||
@@ -725,3 +728,15 @@ type DeadlineForm struct { | |||
func (f *DeadlineForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
return validate(errs, ctx.Data, f, ctx.Locale) | |||
} | |||
type CreateCourseForm struct { | |||
RepoName string `binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
Alias string `binding:"Required;MaxSize(100);AlphaDashDotChinese"` | |||
Topics string | |||
Description string `binding:"MaxSize(1024)"` | |||
} | |||
// Validate validates the fields | |||
func (f *CreateCourseForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors { | |||
return validate(errs, ctx.Data, f, ctx.Locale) | |||
} |
@@ -105,6 +105,11 @@ func (f RegisterForm) IsEmailDomainWhitelisted() bool { | |||
} | |||
domain := strings.ToLower(f.Email[n+1:]) | |||
//support edu.cn | |||
if strings.HasSuffix(domain, "edu.cn") { | |||
return true | |||
} | |||
for _, v := range setting.Service.EmailDomainWhitelist { | |||
if strings.ToLower(v) == domain { | |||
@@ -64,6 +64,11 @@ func Toggle(options *ToggleOptions) macaron.Handler { | |||
ctx.Redirect(setting.AppSubURL + "/") | |||
return | |||
} | |||
if ctx.QueryBool("course") { | |||
ctx.Redirect(setting.AppSubURL + "/" + setting.Course.OrgName) | |||
return | |||
} | |||
} | |||
// Redirect to dashboard if user tries to visit any non-login page. | |||
@@ -45,8 +45,8 @@ type Context struct { | |||
IsSigned bool | |||
IsBasicAuth bool | |||
Repo *Repository | |||
Org *Organization | |||
Repo *Repository | |||
Org *Organization | |||
Cloudbrain *models.Cloudbrain | |||
} | |||
@@ -328,7 +328,7 @@ func Contexter() macaron.Handler { | |||
} | |||
} | |||
ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | |||
//ctx.Resp.Header().Set(`X-Frame-Options`, `SAMEORIGIN`) | |||
ctx.Data["CsrfToken"] = html.EscapeString(x.GetToken()) | |||
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`) | |||
@@ -347,9 +347,9 @@ func Contexter() macaron.Handler { | |||
ctx.Data["EnableSwagger"] = setting.API.EnableSwagger | |||
ctx.Data["EnableOpenIDSignIn"] = setting.Service.EnableOpenIDSignIn | |||
notice, _ := notice.GetNewestNotice() | |||
if notice != nil { | |||
ctx.Data["notice"] = *notice | |||
notices, _ := notice.GetNewestNotice() | |||
if notices != nil { | |||
ctx.Data["notices"] = notices | |||
} | |||
c.Map(ctx) | |||
} | |||
@@ -63,6 +63,8 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { | |||
org := ctx.Org.Organization | |||
ctx.Data["Org"] = org | |||
ctx.Data["IsCourse"] = ctx.Org.Organization.Name == setting.Course.OrgName | |||
// Force redirection when username is actually a user. | |||
if !org.IsOrganization() { | |||
ctx.Redirect(setting.AppSubURL + "/" + org.Name) | |||
@@ -402,6 +402,7 @@ func RepoAssignment() macaron.Handler { | |||
} | |||
ctx.Repo.Owner = owner | |||
ctx.Data["Username"] = ctx.Repo.Owner.Name | |||
ctx.Data["IsCourse"] = owner.Name == setting.Course.OrgName | |||
// Get repository. | |||
repo, err := models.GetRepositoryByName(owner.ID, repoName) | |||
@@ -18,6 +18,7 @@ const ( | |||
//notebook | |||
storageTypeOBS = "obs" | |||
autoStopDuration = 4 * 60 * 60 | |||
autoStopDurationMs = 4 * 60 * 60 * 1000 | |||
DataSetMountPath = "/home/ma-user/work" | |||
NotebookEnv = "Python3" | |||
@@ -263,6 +264,48 @@ func GenerateTask(ctx *context.Context, jobName, uuid, description, flavor strin | |||
return nil | |||
} | |||
func GenerateNotebook2(ctx *context.Context, jobName, uuid, description, flavor string) error { | |||
if poolInfos == nil { | |||
json.Unmarshal([]byte(setting.PoolInfos), &poolInfos) | |||
} | |||
jobResult, err := createNotebook2(models.CreateNotebook2Params{ | |||
JobName: jobName, | |||
Description: description, | |||
Flavor: flavor, | |||
Duration: autoStopDurationMs, | |||
ImageID: "59a6e9f5-93c0-44dd-85b0-82f390c5d53a", | |||
PoolID: poolInfos.PoolInfo[0].PoolId, | |||
Feature: models.NotebookFeature, | |||
Volume: models.VolumeReq{ | |||
Capacity: 100, | |||
Category: models.EVSCategory, | |||
Ownership: models.ManagedOwnership, | |||
}, | |||
WorkspaceID: "0", | |||
}) | |||
if err != nil { | |||
log.Error("createNotebook2 failed: %v", err.Error()) | |||
return err | |||
} | |||
err = models.CreateCloudbrain(&models.Cloudbrain{ | |||
Status: string(models.JobWaiting), | |||
UserID: ctx.User.ID, | |||
RepoID: ctx.Repo.Repository.ID, | |||
JobID: jobResult.ID, | |||
JobName: jobName, | |||
JobType: string(models.JobTypeDebug), | |||
Type: models.TypeCloudBrainTwo, | |||
Uuid: uuid, | |||
ComputeResource: models.NPUResource, | |||
}) | |||
if err != nil { | |||
return err | |||
} | |||
return nil | |||
} | |||
func GenerateTrainJob(ctx *context.Context, req *GenerateTrainJobReq) (err error) { | |||
jobResult, err := createTrainJob(models.CreateTrainJobParams{ | |||
JobName: req.JobName, | |||
@@ -28,6 +28,13 @@ const ( | |||
urlResourceSpecs = "/job/resource-specs" | |||
urlTrainJobConfig = "/training-job-configs" | |||
errorCodeExceedLimit = "ModelArts.0118" | |||
//notebook 2.0 | |||
urlNotebook2 = "/notebooks" | |||
//error code | |||
modelartsIllegalToken = "ModelArts.6401" | |||
NotebookNotFound = "ModelArts.6404" | |||
) | |||
func getRestyClient() *resty.Client { | |||
@@ -174,6 +181,50 @@ sendjob: | |||
return &result, nil | |||
} | |||
func GetNotebook2(jobID string) (*models.GetNotebook2Result, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.GetNotebook2Result | |||
retry := 0 | |||
sendjob: | |||
res, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Get(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID) | |||
if err != nil { | |||
return nil, fmt.Errorf("resty GetJob: %v", err) | |||
} | |||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
var response models.NotebookResult | |||
err = json.Unmarshal(res.Body(), &response) | |||
if err != nil { | |||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||
} | |||
if len(response.ErrorCode) != 0 { | |||
log.Error("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
return &result, fmt.Errorf("GetJob failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func ManageNotebook(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
@@ -214,6 +265,50 @@ sendjob: | |||
return &result, nil | |||
} | |||
func ManageNotebook2(jobID string, param models.NotebookAction) (*models.NotebookActionResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.NotebookActionResult | |||
retry := 0 | |||
sendjob: | |||
res, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID + "/" + param.Action + "?duration=" + strconv.Itoa(autoStopDurationMs)) | |||
if err != nil { | |||
return &result, fmt.Errorf("resty ManageNotebook2: %v", err) | |||
} | |||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
var response models.NotebookResult | |||
err = json.Unmarshal(res.Body(), &response) | |||
if err != nil { | |||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||
} | |||
if len(response.ErrorCode) != 0 { | |||
log.Error("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
return &result, fmt.Errorf("ManageNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func DelNotebook(jobID string) (*models.NotebookDelResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
@@ -253,6 +348,50 @@ sendjob: | |||
return &result, nil | |||
} | |||
func DelNotebook2(jobID string) (*models.NotebookDelResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.NotebookDelResult | |||
retry := 0 | |||
sendjob: | |||
res, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetAuthToken(TOKEN). | |||
SetResult(&result). | |||
Delete(HOST + "/v1/" + setting.ProjectID + urlNotebook2 + "/" + jobID) | |||
if err != nil { | |||
return &result, fmt.Errorf("resty DelJob: %v", err) | |||
} | |||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
var response models.NotebookResult | |||
err = json.Unmarshal(res.Body(), &response) | |||
if err != nil { | |||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||
} | |||
if len(response.ErrorCode) != 0 { | |||
log.Error("DelNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
return &result, fmt.Errorf("DelNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
} | |||
return &result, nil | |||
} | |||
func DelJob(jobID string) (*models.NotebookDelResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
@@ -930,3 +1069,51 @@ sendjob: | |||
return &result, nil | |||
} | |||
func createNotebook2(createJobParams models.CreateNotebook2Params) (*models.CreateNotebookResult, error) { | |||
checkSetting() | |||
client := getRestyClient() | |||
var result models.CreateNotebookResult | |||
retry := 0 | |||
sendjob: | |||
res, err := client.R(). | |||
SetHeader("Content-Type", "application/json"). | |||
SetAuthToken(TOKEN). | |||
SetBody(createJobParams). | |||
SetResult(&result). | |||
Post(HOST + "/v1/" + setting.ProjectID + urlNotebook2) | |||
if err != nil { | |||
return nil, fmt.Errorf("resty create notebook2: %s", err) | |||
} | |||
if res.StatusCode() == http.StatusUnauthorized && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
var response models.NotebookResult | |||
err = json.Unmarshal(res.Body(), &response) | |||
if err != nil { | |||
log.Error("json.Unmarshal failed: %s", err.Error()) | |||
return &result, fmt.Errorf("son.Unmarshal failed: %s", err.Error()) | |||
} | |||
if len(response.ErrorCode) != 0 { | |||
log.Error("createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
if response.ErrorCode == errorCodeExceedLimit { | |||
response.ErrorMsg = "所选规格使用数量已超过最大配额限制。" | |||
} | |||
if response.ErrorCode == modelartsIllegalToken && retry < 1 { | |||
retry++ | |||
_ = getToken() | |||
goto sendjob | |||
} | |||
return &result, fmt.Errorf("createNotebook2 failed(%s): %s", response.ErrorCode, response.ErrorMsg) | |||
} | |||
return &result, nil | |||
} |
@@ -154,6 +154,22 @@ func (a *actionNotifier) NotifyRenameRepository(doer *models.User, repo *models. | |||
} | |||
} | |||
func (a *actionNotifier) NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) { | |||
log.Trace("action.ChangeRepositoryAlias: %s/%s", doer.Name, repo.Alias) | |||
if err := models.NotifyWatchers(&models.Action{ | |||
ActUserID: doer.ID, | |||
ActUser: doer, | |||
OpType: models.ActionRenameRepo, | |||
RepoID: repo.ID, | |||
Repo: repo, | |||
IsPrivate: repo.IsPrivate, | |||
Content: oldAlias, | |||
}); err != nil { | |||
log.Error("NotifyWatchers: %v", err) | |||
} | |||
} | |||
func (a *actionNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { | |||
if err := models.NotifyWatchers(&models.Action{ | |||
ActUserID: doer.ID, | |||
@@ -18,6 +18,7 @@ type Notifier interface { | |||
NotifyDeleteRepository(doer *models.User, repo *models.Repository) | |||
NotifyForkRepository(doer *models.User, oldRepo, repo *models.Repository) | |||
NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) | |||
NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) | |||
NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) | |||
NotifyNewIssue(*models.Issue) | |||
@@ -135,6 +135,10 @@ func (*NullNotifier) NotifyDeleteRef(doer *models.User, repo *models.Repository, | |||
func (*NullNotifier) NotifyRenameRepository(doer *models.User, repo *models.Repository, oldRepoName string) { | |||
} | |||
func (a *NullNotifier) NotifyAliasRepository(doer *models.User, repo *models.Repository, oldAlias string) { | |||
} | |||
// NotifyTransferRepository places a place holder function | |||
func (*NullNotifier) NotifyTransferRepository(doer *models.User, repo *models.Repository, oldOwnerName string) { | |||
} | |||
@@ -22,12 +22,17 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m | |||
Limit: u.MaxRepoCreation, | |||
} | |||
} | |||
var RepoType = models.RepoNormal | |||
if opts.IsCourse { | |||
RepoType = models.RepoCourse | |||
} | |||
repo := &models.Repository{ | |||
OwnerID: u.ID, | |||
Owner: u, | |||
OwnerName: u.Name, | |||
Name: opts.Name, | |||
Alias: opts.Alias, | |||
LowerName: strings.ToLower(opts.Name), | |||
Description: opts.Description, | |||
OriginalURL: opts.OriginalURL, | |||
@@ -37,10 +42,15 @@ func CreateRepository(doer, u *models.User, opts models.CreateRepoOptions) (_ *m | |||
CloseIssuesViaCommitInAnyBranch: setting.Repository.DefaultCloseIssuesViaCommitsInAnyBranch, | |||
Status: opts.Status, | |||
IsEmpty: !opts.AutoInit, | |||
RepoType: RepoType, | |||
Topics: opts.Topics, | |||
} | |||
err = models.WithTx(func(ctx models.DBContext) error { | |||
if err = models.CreateRepository(ctx, doer, u, repo); err != nil { | |||
if err = models.CreateRepository(ctx, doer, u, repo, opts); err != nil { | |||
return err | |||
} | |||
if err = models.SaveTopics(repo.ID, opts.Topics...); err != nil { | |||
return err | |||
} | |||
@@ -15,7 +15,7 @@ import ( | |||
) | |||
// ForkRepository forks a repository | |||
func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc string) (_ *models.Repository, err error) { | |||
func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, desc, alias string) (_ *models.Repository, err error) { | |||
forkedRepo, err := oldRepo.GetUserFork(owner.ID) | |||
if err != nil { | |||
return nil, err | |||
@@ -33,6 +33,7 @@ func ForkRepository(doer, owner *models.User, oldRepo *models.Repository, name, | |||
Owner: owner, | |||
OwnerName: owner.Name, | |||
Name: name, | |||
Alias: alias, | |||
LowerName: strings.ToLower(name), | |||
Description: desc, | |||
DefaultBranch: oldRepo.DefaultBranch, | |||
@@ -18,7 +18,7 @@ func TestForkRepository(t *testing.T) { | |||
user := models.AssertExistsAndLoadBean(t, &models.User{ID: 13}).(*models.User) | |||
repo := models.AssertExistsAndLoadBean(t, &models.Repository{ID: 10}).(*models.Repository) | |||
fork, err := ForkRepository(user, user, repo, "test", "test") | |||
fork, err := ForkRepository(user, user, repo, "test", "test", "test") | |||
assert.Nil(t, fork) | |||
assert.Error(t, err) | |||
assert.True(t, models.IsErrForkAlreadyExist(err)) | |||
@@ -236,6 +236,7 @@ func GenerateRepository(ctx models.DBContext, doer, owner *models.User, template | |||
Owner: owner, | |||
OwnerName: owner.Name, | |||
Name: opts.Name, | |||
Alias: opts.Alias, | |||
LowerName: strings.ToLower(opts.Name), | |||
Description: opts.Description, | |||
IsPrivate: opts.Private, | |||
@@ -51,7 +51,7 @@ func prepareRepoCommit(ctx models.DBContext, repo *models.Repository, tmpDir, re | |||
cloneLink := repo.CloneLink() | |||
match := map[string]string{ | |||
"Name": repo.Name, | |||
"Name": repo.DisplayName(), | |||
"Description": repo.Description, | |||
"CloneURL.SSH": cloneLink.SSH, | |||
"CloneURL.HTTPS": cloneLink.HTTPS, | |||
@@ -512,6 +512,7 @@ var ( | |||
ProfileID string | |||
PoolInfos string | |||
Flavor string | |||
DebugHost string | |||
//train-job | |||
ResourcePools string | |||
Engines string | |||
@@ -572,6 +573,11 @@ var ( | |||
}{} | |||
Warn_Notify_Mails []string | |||
Course = struct { | |||
OrgName string | |||
TeamName string | |||
}{} | |||
) | |||
// DateLang transforms standard language locale name to corresponding value in datetime plugin. | |||
@@ -1320,6 +1326,7 @@ func NewContext() { | |||
ProfileID = sec.Key("PROFILE_ID").MustString("") | |||
PoolInfos = sec.Key("POOL_INFOS").MustString("") | |||
Flavor = sec.Key("FLAVOR").MustString("") | |||
DebugHost = sec.Key("DEBUG_SERVER_HOST").MustString("http://192.168.202.73") | |||
ResourcePools = sec.Key("Resource_Pools").MustString("") | |||
Engines = sec.Key("Engines").MustString("") | |||
EngineVersions = sec.Key("Engine_Versions").MustString("") | |||
@@ -1339,6 +1346,11 @@ func NewContext() { | |||
sec = Cfg.Section("warn_mail") | |||
Warn_Notify_Mails = strings.Split(sec.Key("mails").MustString(""), ",") | |||
sec = Cfg.Section("course") | |||
Course.OrgName = sec.Key("org_name").MustString("") | |||
Course.TeamName = sec.Key("team_name").MustString("") | |||
} | |||
func SetRadarMapConfig() { | |||
@@ -57,8 +57,8 @@ func ObsHasObject(path string) (bool, error) { | |||
return hasObject, nil | |||
} | |||
func GetObsPartInfos(uuid string, uploadID string) (string, error) { | |||
key := strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, uuid)), "/") | |||
func GetObsPartInfos(uuid, uploadID, fileName string) (string, error) { | |||
key := strings.TrimPrefix(path.Join(setting.BasePath, path.Join(uuid[0:1], uuid[1:2], uuid, fileName)), "/") | |||
output, err := ObsCli.ListParts(&obs.ListPartsInput{ | |||
Bucket: setting.Bucket, | |||
@@ -46,31 +46,33 @@ type ExternalWiki struct { | |||
// Repository represents a repository | |||
type Repository struct { | |||
ID int64 `json:"id"` | |||
Owner *User `json:"owner"` | |||
Name string `json:"name"` | |||
FullName string `json:"full_name"` | |||
Description string `json:"description"` | |||
Empty bool `json:"empty"` | |||
Private bool `json:"private"` | |||
Fork bool `json:"fork"` | |||
Template bool `json:"template"` | |||
Parent *Repository `json:"parent"` | |||
Mirror bool `json:"mirror"` | |||
Size int `json:"size"` | |||
HTMLURL string `json:"html_url"` | |||
SSHURL string `json:"ssh_url"` | |||
CloneURL string `json:"clone_url"` | |||
OriginalURL string `json:"original_url"` | |||
Website string `json:"website"` | |||
Stars int `json:"stars_count"` | |||
Forks int `json:"forks_count"` | |||
Watchers int `json:"watchers_count"` | |||
OpenIssues int `json:"open_issues_count"` | |||
OpenPulls int `json:"open_pr_counter"` | |||
Releases int `json:"release_counter"` | |||
DefaultBranch string `json:"default_branch"` | |||
Archived bool `json:"archived"` | |||
ID int64 `json:"id"` | |||
Owner *User `json:"owner"` | |||
Name string `json:"name"` | |||
Alias string `json:"alias"` | |||
FullName string `json:"full_name"` | |||
FullDisplayName string `json:"full_display_name"` | |||
Description string `json:"description"` | |||
Empty bool `json:"empty"` | |||
Private bool `json:"private"` | |||
Fork bool `json:"fork"` | |||
Template bool `json:"template"` | |||
Parent *Repository `json:"parent"` | |||
Mirror bool `json:"mirror"` | |||
Size int `json:"size"` | |||
HTMLURL string `json:"html_url"` | |||
SSHURL string `json:"ssh_url"` | |||
CloneURL string `json:"clone_url"` | |||
OriginalURL string `json:"original_url"` | |||
Website string `json:"website"` | |||
Stars int `json:"stars_count"` | |||
Forks int `json:"forks_count"` | |||
Watchers int `json:"watchers_count"` | |||
OpenIssues int `json:"open_issues_count"` | |||
OpenPulls int `json:"open_pr_counter"` | |||
Releases int `json:"release_counter"` | |||
DefaultBranch string `json:"default_branch"` | |||
Archived bool `json:"archived"` | |||
// swagger:strfmt date-time | |||
Created time.Time `json:"created_at"` | |||
// swagger:strfmt date-time | |||
@@ -98,6 +100,10 @@ type CreateRepoOption struct { | |||
// required: true | |||
// unique: true | |||
Name string `json:"name" binding:"Required;AlphaDashDot;MaxSize(100)"` | |||
// Alias of the repository to create | |||
// required: false | |||
// unique: true | |||
Alias string `json:"alias" binding:"AlphaDashDotChinese;MaxSize(100)"` | |||
// Description of the repository to create | |||
Description string `json:"description" binding:"MaxSize(255)"` | |||
// Whether the repository is private | |||
@@ -217,6 +223,7 @@ type MigrateRepoOption struct { | |||
UID int `json:"uid" binding:"Required"` | |||
// required: true | |||
RepoName string `json:"repo_name" binding:"Required"` | |||
Alias string `json:"alias" binding:"Required"` | |||
Mirror bool `json:"mirror"` | |||
Private bool `json:"private"` | |||
Description string `json:"description"` | |||
@@ -84,6 +84,7 @@ func CreateMigrateTask(doer, u *models.User, opts base.MigrateOptions) (*models. | |||
repo, err := repo_module.CreateRepository(doer, u, models.CreateRepoOptions{ | |||
Name: opts.RepoName, | |||
Alias: opts.Alias, | |||
Description: opts.Description, | |||
OriginalURL: opts.OriginalURL, | |||
GitServiceType: opts.GitServiceType, | |||
@@ -93,6 +93,7 @@ func NewFuncMap() []template.FuncMap { | |||
"TimeSince": timeutil.TimeSince, | |||
"TimeSinceUnix": timeutil.TimeSinceUnix, | |||
"TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort, | |||
"RawTimeSince": timeutil.RawTimeSince, | |||
"FileSize": base.FileSize, | |||
"PrettyNumber": base.PrettyNumber, | |||
@@ -342,6 +343,7 @@ func NewTextFuncMap() []texttmpl.FuncMap { | |||
"TimeSince": timeutil.TimeSince, | |||
"TimeSinceUnix": timeutil.TimeSinceUnix, | |||
"TimeSinceUnix1": timeutil.TimeSinceUnix1, | |||
"TimeSinceUnixShort": timeutil.TimeSinceUnixShort, | |||
"RawTimeSince": timeutil.RawTimeSince, | |||
"DateFmtLong": func(t time.Time) string { | |||
return t.Format(time.RFC1123Z) | |||
@@ -165,5 +165,8 @@ func htmlTimeSinceUnix(then, now TimeStamp, lang string) template.HTML { | |||
func TimeSinceUnix1(then TimeStamp) string { | |||
format := time.Unix(int64(then), 0).Format("2006-01-02 15:04:05") | |||
return format | |||
} | |||
func TimeSinceUnixShort(then TimeStamp) string { | |||
format := time.Unix(int64(then), 0).Format("2006-01-02") | |||
return format | |||
} |
@@ -19,6 +19,8 @@ const ( | |||
// ErrGlobPattern is returned when glob pattern is invalid | |||
ErrGlobPattern = "GlobPattern" | |||
ErrAlphaDashDotChinese = "AlphaDashDotChineseError" | |||
) | |||
var ( | |||
@@ -26,6 +28,8 @@ var ( | |||
// They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere. | |||
// They cannot have question-mark ?, asterisk *, or open bracket [ anywhere | |||
GitRefNamePatternInvalid = regexp.MustCompile(`[\000-\037\177 \\~^:?*[]+`) | |||
AlphaDashDotChinese = regexp.MustCompile("^[\u4e00-\u9fa5\\.\\-_A-Za-z0-9]+$") | |||
) | |||
// CheckGitRefAdditionalRulesValid check name is valid on additional rules | |||
@@ -53,6 +57,7 @@ func AddBindingRules() { | |||
addGitRefNameBindingRule() | |||
addValidURLBindingRule() | |||
addGlobPatternRule() | |||
addAlphaDashDotChineseRule() | |||
} | |||
func addGitRefNameBindingRule() { | |||
@@ -117,6 +122,24 @@ func addGlobPatternRule() { | |||
}) | |||
} | |||
func addAlphaDashDotChineseRule() { | |||
binding.AddRule(&binding.Rule{ | |||
IsMatch: func(rule string) bool { | |||
return strings.HasPrefix(rule, "AlphaDashDotChinese") | |||
}, | |||
IsValid: func(errs binding.Errors, name string, val interface{}) (bool, binding.Errors) { | |||
if val == "" { | |||
return true, errs | |||
} | |||
if !ValidAlphaDashDotChinese(fmt.Sprintf("%v", val)) { | |||
errs.Add([]string{name}, ErrAlphaDashDotChinese, "ErrAlphaDashDotChinese") | |||
return false, errs | |||
} | |||
return true, errs | |||
}, | |||
}) | |||
} | |||
func portOnly(hostport string) string { | |||
colon := strings.IndexByte(hostport, ':') | |||
if colon == -1 { | |||
@@ -139,3 +162,7 @@ func validPort(p string) bool { | |||
} | |||
return true | |||
} | |||
func ValidAlphaDashDotChinese(value string) bool { | |||
return AlphaDashDotChinese.MatchString(value) | |||
} |
@@ -50,6 +50,8 @@ repository = Repository | |||
organization = Organization | |||
mirror = Mirror | |||
new_repo = New Repository | |||
new_course=Publish Course | |||
course_desc = Course Description | |||
new_migrate = New Migration | |||
new_dataset = New Dataset | |||
edit_dataset = Edit Dataset | |||
@@ -219,6 +221,7 @@ show_only_public = Showing only public | |||
issues.in_your_repos = In your repositories | |||
contributors = Contributors | |||
contributor = Contributor | |||
page_title=Explore Better AI | |||
page_small_title=OpenI AI Development Cooperation Platform | |||
@@ -268,8 +271,8 @@ org_no_results = No matching organizations found. | |||
code_no_results = No source code matching your search term found. | |||
code_search_results = Search results for '%s' | |||
code_last_indexed_at = Last indexed %s | |||
save=save | |||
cancel=cancel | |||
save=Save | |||
cancel=Cancel | |||
[auth] | |||
create_new_account = Register Account | |||
@@ -323,7 +326,7 @@ openid_register_title = Create new account | |||
openid_register_desc = The chosen OpenID URI is unknown. Associate it with a new account here. | |||
openid_signin_desc = Enter your OpenID URI. For example: https://anne.me, bob.openid.org.cn or gnusocial.net/carry. | |||
disable_forgot_password_mail = Account recovery is disabled. Please contact your site administrator. | |||
email_domain_blacklisted = You cannot register with your email address. | |||
email_domain_blacklisted = You cannot register with this kind of email address. | |||
authorize_application = Authorize Application | |||
authorize_redirect_notice = You will be redirected to %s if you authorize this application. | |||
authorize_application_created_by = This application was created by %s. | |||
@@ -348,7 +351,12 @@ modify = Update | |||
[form] | |||
UserName = Username | |||
RepoName = Repository name | |||
Alias = Repository name | |||
courseAlias = Course Name | |||
courseAdress = Course Path | |||
RepoPath = Repository path | |||
RepoAdress = Repository Adress | |||
course_Adress = Course Address | |||
Email = Email address | |||
Password = Password | |||
Retype = Re-Type Password | |||
@@ -372,7 +380,10 @@ SSPIDefaultLanguage = Default Language | |||
require_error = ` cannot be empty.` | |||
alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` | |||
alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` | |||
reponame_dash_dot_error=` Please enter Chinese, alphanumeric, dash ('-') ,underscore ('_') and dot ('.')characters, up to 100 characters. ` | |||
repoadd_dash_dot_error=` Path only allows input alphanumeric, dash ('-') ,underscore ('_') and dot ('.')characters, up to 100 characters. ` | |||
git_ref_name_error = ` must be a well-formed Git reference name.` | |||
alpha_dash_dot_chinese_error= ` should contain only alphanumeric, chinese, dash ('-') and underscore ('_') characters.` | |||
size_error = ` must be size %s.` | |||
min_size_error = ` must contain at least %s characters.` | |||
max_size_error = ` must contain at most %s characters.` | |||
@@ -386,7 +397,8 @@ password_not_match = The passwords do not match. | |||
lang_select_error = Select a language from the list. | |||
username_been_taken = The username is already taken. | |||
repo_name_been_taken = The repository name is already used. | |||
repo_name_been_taken = The repository name or path is already used. | |||
course_name_been_taken=The course name or path is already used. | |||
visit_rate_limit = Remote visit addressed rate limitation. | |||
2fa_auth_required = Remote visit required two factors authentication. | |||
org_name_been_taken = The organization name is already taken. | |||
@@ -791,6 +803,7 @@ generate_from = Generate From | |||
repo_desc = Description | |||
repo_lang = Language | |||
repo_gitignore_helper = Select .gitignore templates. | |||
repo_label_helpe = Press Enter to complete | |||
issue_labels = Issue Labels | |||
issue_labels_helper = Select an issue label set. | |||
license = License | |||
@@ -799,6 +812,8 @@ readme = README | |||
readme_helper = Select a README file template. | |||
auto_init = Initialize Repository (Adds .gitignore, License and README) | |||
create_repo = Create Repository | |||
create_course = Publish Course | |||
failed_to_create_course=Fail to publish course, please try again later. | |||
default_branch = Default Branch | |||
mirror_prune = Prune | |||
mirror_prune_desc = Remove obsolete remote-tracking references | |||
@@ -862,6 +877,11 @@ get_repo_info_error=Can not get the information of the repository. | |||
generate_statistic_file_error=Fail to generate file. | |||
repo_stat_inspect=ProjectAnalysis | |||
all=All | |||
computing.all = All | |||
computing.Introduction=Introduction | |||
computing.success=Join Success | |||
modelarts.status=Status | |||
modelarts.createtime=CreateTime | |||
modelarts.version_nums = Version Nums | |||
@@ -899,7 +919,7 @@ modelarts.train_job.description=Description | |||
modelarts.train_job.parameter_setting=Parameter setting | |||
modelarts.train_job.parameter_setting_info=Parameter Info | |||
modelarts.train_job.fast_parameter_setting=fast_parameter_setting | |||
modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config | |||
modelarts.train_job.fast_parameter_setting_config=fast_parameter_setting_config | |||
modelarts.train_job.fast_parameter_setting_config_link=fast_parameter_setting_config_link | |||
modelarts.train_job.frames=frames | |||
modelarts.train_job.algorithm_origin=Algorithm Origin | |||
@@ -974,14 +994,21 @@ template.avatar = Avatar | |||
template.issue_labels = Issue Labels | |||
template.one_item = Must select at least one template item | |||
template.invalid = Must select a template repository | |||
template.repo_adress=Adress | |||
template.repo_path=path | |||
template.repo_name=Name | |||
archive.title = This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests. | |||
archive.issue.nocomment = This repo is archived. You cannot comment on issues. | |||
archive.pull.nocomment = This repo is archived. You cannot comment on pull requests. | |||
form.reach_limit_of_creation = You have already reached your limit of %d repositories. | |||
form.reach_limit_of_course_creation=You have already reached your limit of %d courses or repositories. | |||
form.name_reserved = The repository name '%s' is reserved. | |||
form.course_name_reserved=The course name '%s' is reserved. | |||
form.name_pattern_not_allowed = The pattern '%s' is not allowed in a repository name. | |||
form.course_name_pattern_not_allowed=The pattern '%s' is not allowed in a course name. | |||
add_course_org_fail=Fail to add organization, please try again later. | |||
need_auth = Clone Authorization | |||
migrate_type = Migration Type | |||
@@ -1194,7 +1221,7 @@ issues.filter_label_exclude = `Use <code>alt</code> + <code>click/enter</code> t | |||
issues.filter_label_no_select = All labels | |||
issues.filter_milestone = Milestone | |||
issues.filter_milestone_no_select = All milestones | |||
issues.filter_milestone_no_add = Not add milestones | |||
issues.filter_milestone_no_add = Not add milestones | |||
issues.filter_assignee = Assignee | |||
issues.filter_assginee_no_select = All assignees | |||
issues.filter_type = Type | |||
@@ -2026,6 +2053,7 @@ org_full_name_holder = Organization Full Name | |||
org_name_helper = Organization names should be short and memorable. | |||
create_org = Create Organization | |||
repo_updated = Updated | |||
repo_released = Post | |||
home = Home | |||
people = People | |||
teams = Teams | |||
@@ -2042,6 +2070,17 @@ team_access_desc = Repository access | |||
team_permission_desc = Permission | |||
team_unit_desc = Allow Access to Repository Sections | |||
team_unit_disabled = (Disabled) | |||
selected_couse=Selected Courses | |||
release_course = Publish Course | |||
all_keywords=All keywords | |||
max_selectedPro= Select up to 9 public projects | |||
custom_select_courses = Customize selected courses | |||
recommend_remain_pro = Remain | |||
save_fail_tips = The upper limit is exceeded | |||
select_again = Select more than 9, please select again! | |||
custom_select_projects = Customize selected projects | |||
customize = Customize | |||
selected_project=Selected Projects | |||
form.name_reserved = The organization name '%s' is reserved. | |||
form.name_pattern_not_allowed = The pattern '%s' is not allowed in an organization name. | |||
@@ -2085,6 +2124,8 @@ members.remove = Remove | |||
members.leave = Leave | |||
members.invite_desc = Add a new member to %s: | |||
members.invite_now = Invite Now | |||
course_members.remove = Remove | |||
course_members.leave = Leave | |||
teams.join = Join | |||
teams.leave = Leave | |||
@@ -2127,6 +2168,7 @@ teams.all_repositories_helper = Team has access to all repositories. Selecting t | |||
teams.all_repositories_read_permission_desc = This team grants <strong>Read</strong> access to <strong>all repositories</strong>: members can view and clone repositories. | |||
teams.all_repositories_write_permission_desc = This team grants <strong>Write</strong> access to <strong>all repositories</strong>: members can read from and push to repositories. | |||
teams.all_repositories_admin_permission_desc = This team grants <strong>Admin</strong> access to <strong>all repositories</strong>: members can read from, push to and add collaborators to repositories. | |||
teams.join_teams=Join in | |||
[admin] | |||
dashboard = Dashboard | |||
@@ -50,6 +50,8 @@ repository=项目 | |||
organization=组织 | |||
mirror=镜像 | |||
new_repo=创建项目 | |||
new_course=发布课程 | |||
course_desc=课程描述 | |||
new_dataset=创建数据集 | |||
new_migrate=迁移外部项目 | |||
edit_dataset = Edit Dataset | |||
@@ -221,6 +223,7 @@ show_only_public=只显示公开的 | |||
issues.in_your_repos=属于该用户项目的 | |||
contributors=贡献者 | |||
contributor=贡献者 | |||
page_title=探索更好的AI | |||
page_small_title=启智AI开发协作平台 | |||
@@ -325,7 +328,7 @@ openid_register_title=创建新帐户 | |||
openid_register_desc=所选的 OpenID URI 未知。在这里关联一个新帐户。 | |||
openid_signin_desc=输入您的 OpenID URI。例如: https://anne.me、bob.openid.org.cn 或 gnusocial.net/carry。 | |||
disable_forgot_password_mail=帐户恢复功能已被禁用。请与网站管理员联系。 | |||
email_domain_blacklisted=您不能使用您的电子邮件地址注册。 | |||
email_domain_blacklisted=暂不支持此类电子邮件地址注册。 | |||
authorize_application=应用授权 | |||
authorize_redirect_notice=如果您授权此应用,您将会被重定向到 %s。 | |||
authorize_application_created_by=此应用由%s创建。 | |||
@@ -350,7 +353,13 @@ modify=更新 | |||
[form] | |||
UserName=用户名 | |||
RepoName=项目名称 | |||
RepoName=项目路径 | |||
Alias=项目名称 | |||
courseAlias=课程名称 | |||
courseAdress=课程路径 | |||
RepoPath=项目路径 | |||
RepoAdress=项目地址 | |||
course_Adress = 课程地址 | |||
Email=邮箱地址 | |||
Password=密码 | |||
Retype=重新输入密码 | |||
@@ -374,7 +383,10 @@ SSPIDefaultLanguage=默认语言 | |||
require_error=不能为空。 | |||
alpha_dash_error=应该只包含字母数字、破折号 ('-') 和下划线 ('_') 字符。 | |||
alpha_dash_dot_error=应该只包含字母数字, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 | |||
reponame_dash_dot_error=请输入中文、字母、数字和-_ .,最多100个字符。 | |||
repoadd_dash_dot_error=路径只允许字母、数字和-_ .,最多100个字符。 | |||
git_ref_name_error=` 必须是格式良好的 git 引用名称。` | |||
alpha_dash_dot_chinese_error=应该只包含字母数字中文, 破折号 ('-'), 下划线 ('_') 和点 ('. ') 。 | |||
size_error=长度必须为 %s。 | |||
min_size_error=长度最小为 %s 个字符。 | |||
max_size_error=长度最大为 %s 个字符。 | |||
@@ -388,7 +400,8 @@ password_not_match=密码不匹配。 | |||
lang_select_error=从列表中选出语言 | |||
username_been_taken=用户名已被使用。 | |||
repo_name_been_taken=项目名称已被使用。 | |||
repo_name_been_taken=项目名称或项目路径已被使用。 | |||
course_name_been_taken=课程名称或地址已被使用。 | |||
visit_rate_limit=远程访问达到速度限制。 | |||
2fa_auth_required=远程访问需要双重验证。 | |||
org_name_been_taken=组织名称已被使用。 | |||
@@ -794,6 +807,7 @@ generate_from=生成自 | |||
repo_desc=项目描述 | |||
repo_lang=项目语言 | |||
repo_gitignore_helper=选择 .gitignore 模板。 | |||
repo_label_helpe=输入完成后回车键完成标签确定。 | |||
issue_labels=任务标签 | |||
issue_labels_helper=选择一个任务标签集 | |||
license=授权许可 | |||
@@ -802,6 +816,8 @@ readme=自述 | |||
readme_helper=选择自述文件模板。 | |||
auto_init=初始化存储库 (添加. gitignore、许可证和自述文件) | |||
create_repo=创建项目 | |||
create_course=发布课程 | |||
failed_to_create_course=发布课程失败,请稍后再试。 | |||
default_branch=默认分支 | |||
mirror_prune=修剪 | |||
mirror_prune_desc=删除过时的远程跟踪引用 | |||
@@ -867,6 +883,10 @@ generate_statistic_file_error=生成文件失败。 | |||
repo_stat_inspect=项目分析 | |||
all=所有 | |||
computing.all=全部 | |||
computing.Introduction=简介 | |||
computing.success=加入成功 | |||
modelarts.status=状态 | |||
modelarts.createtime=创建时间 | |||
modelarts.version_nums=版本数 | |||
@@ -984,14 +1004,21 @@ template.avatar=头像 | |||
template.issue_labels=任务标签 | |||
template.one_item=必须至少选择一个模板项 | |||
template.invalid=必须选择一个模板项目 | |||
template.repo_adress=项目地址 | |||
template.repo_path=项目地址 | |||
template.repo_name=项目名称 | |||
archive.title=此项目已存档。您可以查看文件和克隆,但不能推送或创建任务/合并请求。 | |||
archive.issue.nocomment=此项目已存档,您不能在此任务添加评论。 | |||
archive.pull.nocomment=此项目已存档,您不能在此合并请求添加评论。 | |||
form.reach_limit_of_creation=你已经达到了您的 %d 项目的限制。 | |||
form.reach_limit_of_course_creation=你已经达到了您的 %d 课程的限制。 | |||
form.name_reserved=项目名称 '%s' 是被保留的。 | |||
form.course_name_reserved=课程名称 '%s' 是被保留的。 | |||
form.name_pattern_not_allowed=项目名称中不允许使用模式 "%s"。 | |||
form.course_name_pattern_not_allowed=课程名称中不允许使用模式 "%s"。 | |||
add_course_org_fail=加入组织失败,请稍后重试。 | |||
need_auth=需要授权验证 | |||
migrate_type=迁移类型 | |||
@@ -2036,6 +2063,7 @@ org_full_name_holder=组织全名 | |||
org_name_helper=组织名字应该简单明了。 | |||
create_org=创建组织 | |||
repo_updated=最后更新于 | |||
repo_released=发布于 | |||
home=组织主页 | |||
people=组织成员 | |||
teams=组织团队 | |||
@@ -2052,6 +2080,17 @@ team_access_desc=项目权限 | |||
team_permission_desc=权限 | |||
team_unit_desc=允许访问项目单元 | |||
team_unit_disabled=(已禁用) | |||
selected_couse=精选课程 | |||
release_course = 发布课程 | |||
all_keywords=全部关键字 | |||
max_selectedPro= 最多可选9个公开项目 | |||
custom_select_courses = 自定义精选课程 | |||
recommend_remain_pro = 还能推荐 | |||
save_fail_tips = 最多可选9个,保存失败 | |||
select_again = 选择超过9个,请重新选择! | |||
custom_select_projects = 自定义精选项目 | |||
customize = 自定义 | |||
selected_project=精选项目 | |||
form.name_reserved=组织名称 '%s' 是被保留的。 | |||
form.name_pattern_not_allowed=组织名称中不允许使用 "%s"。 | |||
@@ -2095,6 +2134,8 @@ members.remove=移除成员 | |||
members.leave=离开组织 | |||
members.invite_desc=邀请新的用户加入 %s: | |||
members.invite_now=立即邀请 | |||
course_members.remove=移除 | |||
course_members.leave=离开 | |||
teams.join=加入团队 | |||
teams.leave=离开团队 | |||
@@ -2138,6 +2179,10 @@ teams.all_repositories_read_permission_desc=此团队授予<strong>读取</stron | |||
teams.all_repositories_write_permission_desc=此团队授予<strong>修改</strong><strong>所有项目</strong>的访问权限: 成员可以查看和推送至项目。 | |||
teams.all_repositories_admin_permission_desc=该团队拥有 <strong>管理</strong> <strong>所有项目</strong>的权限:团队成员可以读取、克隆、推送以及添加其它项目协作者。 | |||
teams.join_teams=加入该组织 | |||
[admin] | |||
dashboard=管理面板 | |||
users=帐户管理 | |||
@@ -4,10 +4,8 @@ if(isEmpty(token)){ | |||
var meta = $("meta[name=_uid]"); | |||
if(!isEmpty(meta)){ | |||
token = meta.attr("content"); | |||
console.log("token is uid:" + token); | |||
} | |||
} | |||
var swiperNewMessage = new Swiper(".newslist", { | |||
direction: "vertical", | |||
slidesPerView: 10, | |||
@@ -18,7 +16,7 @@ var swiperNewMessage = new Swiper(".newslist", { | |||
}, | |||
}); | |||
var swiperRepo = new Swiper(".homepro-list", { | |||
slidesPerView: 3, | |||
slidesPerView: 1, | |||
slidesPerColumn: 2, | |||
slidesPerColumnFill:'row', | |||
spaceBetween: 30, | |||
@@ -30,11 +28,40 @@ var swiperRepo = new Swiper(".homepro-list", { | |||
delay: 2500, | |||
disableOnInteraction: false, | |||
}, | |||
breakpoints: { | |||
768: { | |||
slidesPerView: 2, | |||
}, | |||
1024: { | |||
slidesPerView: 3, | |||
}, | |||
}, | |||
}); | |||
var swiperOrg = new Swiper(".homeorg-list", { | |||
slidesPerView: 1, | |||
slidesPerColumn: 4, | |||
slidesPerColumnFill:'row', | |||
spaceBetween: 15, | |||
pagination: { | |||
el: ".swiper-pagination", | |||
clickable: true, | |||
}, | |||
autoplay: { | |||
delay: 4500, | |||
disableOnInteraction: false, | |||
}, | |||
breakpoints: { | |||
768: { | |||
slidesPerView: 2, | |||
}, | |||
1024: { | |||
slidesPerView: 3, | |||
}, | |||
}, | |||
}); | |||
var output = document.getElementById("newmessage"); | |||
console.log("document.location.host="+document.location.host); | |||
console.log("document.URL="+document.URL); | |||
var url = "ws://" + document.location.host + "/action/notification"; | |||
if(document.location.host == "git.openi.org.cn" || document.URL.startsWith("https")){ | |||
url = "wss://" + document.location.host + "/action/notification" | |||
@@ -51,18 +78,14 @@ var html =document.documentElement; | |||
var lang = html.attributes["lang"] | |||
var isZh = true; | |||
if(lang != null && lang.nodeValue =="en-US" ){ | |||
console.log("the language is " + lang.nodeValue); | |||
isZh=false; | |||
}else{ | |||
console.log("default lang=zh"); | |||
} | |||
socket.onmessage = function (e) { | |||
var data =JSON.parse(e.data) | |||
console.log("recevie data=" + e.data) | |||
var html = ""; | |||
if (data != null){ | |||
console.log("queue length=" + messageQueue.length); | |||
if(messageQueue.length > maxSize){ | |||
delete messageQueue[0]; | |||
}else{ | |||
@@ -71,31 +94,31 @@ socket.onmessage = function (e) { | |||
var currentTime = new Date().getTime(); | |||
for(var i = 0; i < messageQueue.length; i++){ | |||
var record = messageQueue[i]; | |||
var recordPrefix = getMsg(record); | |||
var actionName = getAction(record.OpType,isZh); | |||
if(record.OpType == "6" || record.OpType == "10" || record.OpType == "12" || record.OpType == "13"){ | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>" | |||
html += " <a href=\"" + getIssueLink(record) + "\" rel=\"nofollow\">" + getIssueText(record) + "</a>" | |||
} | |||
else if(record.OpType == "7" || record.OpType == "11" || record.OpType == "14" || record.OpType == "15" || record.OpType == "22" | |||
|| record.OpType == "23"){ | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getPRLink(record) + "\" rel=\"nofollow\">" + getPRText(record) + "</a>" | |||
html += " <a href=\"" + getPRLink(record) + "\" rel=\"nofollow\">" + getPRText(record) + "</a>" | |||
} | |||
else if(record.OpType == "1"){ | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" +getRepotext(record) + "</a>" | |||
} | |||
else if(record.OpType == "9" || record.OpType == "5"){ | |||
branch = "<a href=\"" + getRepoLink(record) + "/src/branch/" + record.RefName + "\" rel=\"nofollow\">" + record.RefName + "</a>" | |||
actionName = actionName.replace("{branch}",branch); | |||
html += recordPrefix + actionName; | |||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||
html += " <a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>" | |||
}else if(record.OpType == "17"){ | |||
actionName = actionName.replace("{deleteBranchName}",record.RefName); | |||
var repoLink = "<a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepoLink(record) + "</a>" | |||
var repoLink = "<a href=\"" + getRepoLink(record) + "\" rel=\"nofollow\">" + getRepotext(record) + "</a>" | |||
actionName = actionName.replace("{repoName}",repoLink); | |||
html += recordPrefix + actionName; | |||
} | |||
@@ -119,7 +142,6 @@ socket.onmessage = function (e) { | |||
html += "</div>"; | |||
} | |||
} | |||
console.log("html=" + html) | |||
output.innerHTML = html; | |||
swiperNewMessage.updateSlides(); | |||
swiperNewMessage.updateProgress(); | |||
@@ -150,15 +172,20 @@ function getMsg(record){ | |||
html += "<div class=\"swiper-slide item\">"; | |||
html += " <img class=\"ui avatar image\" src=\"/user/avatar/" + record.ActUser.Name + "/-1\" alt=\"\">" | |||
html += " <div class=\"middle aligned content nowrap\">" | |||
html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>" | |||
html += " <a href=\"/" + record.ActUser.Name + "\" title=\"\">" + record.ActUser.Name + "</a>" | |||
return html; | |||
} | |||
function getRepoLink(record){ | |||
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name; | |||
function getRepotext(record){ | |||
if(record.Repo.Alias){ | |||
return record.Repo.OwnerName + "/" + record.Repo.Alias; | |||
}else{ | |||
return record.Repo.OwnerName + "/" + record.Repo.Name; | |||
} | |||
} | |||
function getRepoLink(record){ | |||
return record.Repo.OwnerName + "/" + record.Repo.Name; | |||
} | |||
function getTime(UpdatedUnix,currentTime){ | |||
@@ -168,8 +195,7 @@ function getTime(UpdatedUnix,currentTime){ | |||
if( timeEscSecond < 0){ | |||
timeEscSecond = 1; | |||
} | |||
console.log("currentTime=" + currentTime + " updateUnix=" + UpdatedUnix); | |||
var hours= Math.floor(timeEscSecond / 3600); | |||
//计算相差分钟数 | |||
var leave2 = Math.floor(timeEscSecond % (3600)); //计算小时数后剩余的秒数 | |||
@@ -193,11 +219,16 @@ function getPRLink(record){ | |||
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/pulls/" + getIssueId(record); | |||
} | |||
function getPRText(record){ | |||
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||
if(record.Repo.Alias){ | |||
return record.Repo.OwnerName + "/" + record.Repo.Alias + "#" + getIssueId(record); | |||
}else{ | |||
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||
} | |||
} | |||
function getIssueLink(record){ | |||
return "/" + record.Repo.OwnerName + "/" + record.Repo.Name + "/issues/" + getIssueId(record); | |||
} | |||
@@ -218,7 +249,12 @@ function getIssueId(record){ | |||
} | |||
function getIssueText(record){ | |||
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||
if(record.Repo.Alias){ | |||
return record.Repo.OwnerName + "/" + record.Repo.Alias + "#" + getIssueId(record); | |||
}else{ | |||
return record.Repo.OwnerName + "/" + record.Repo.Name + "#" + getIssueId(record); | |||
} | |||
} | |||
/* | |||
@@ -348,11 +384,9 @@ function queryRecommendData(){ | |||
dataType:"json", | |||
async:false, | |||
success:function(json){ | |||
console.log(json); | |||
displayOrg(json); | |||
}, | |||
error:function(response) { | |||
console.log(response); | |||
} | |||
}); | |||
@@ -365,40 +399,14 @@ function queryRecommendData(){ | |||
dataType:"json", | |||
async:false, | |||
success:function(json){ | |||
console.log(json); | |||
displayRepo(json); | |||
}, | |||
error:function(response) { | |||
console.log(response); | |||
} | |||
}); | |||
} | |||
/* | |||
<div class="swiper-slide"> | |||
<div class="ui fluid card"> | |||
<div class="content"> | |||
<span class="right floated meta"> | |||
<i class="star icon"></i>276 <i class="star icon"></i>32 | |||
</span> | |||
<img class="left floated mini ui image" src="/repo-avatars/278-a9f45e21b92b86dbf969c9f70dff1efc"> | |||
<a class="header nowrap" href="/OpenI/aiforge">aiforge </a> | |||
<div class="description nowrap-2"> | |||
本项目是群体化方法与技术的开源实现案例,在基于Gitea的基础上,进一步支持社交化的协同开发、协同学习、协同研究等群体创新实践服务,特别是针对新一代人工智能技术特点,重点支持项目管理、git代码管理、大数据集存储管理与智能计算平台接入。 | |||
</div> | |||
<div class="ui tags nowrap am-mt-10"> | |||
<a class="ui small label topic" href="/explore/repos?q=ai%e5%bc%80%e5%8f%91%e5%b7%a5%e5%85%b7&topic=">ai开发工具</a> | |||
<a class="ui small label topic" href="/explore/repos?q=openi&topic=">openi</a> | |||
<a class="ui small label topic" href="/explore/repos?q=golang&topic=">golang</a> | |||
<a class="ui small label topic" href="/explore/repos?q=git&topic=">git</a> | |||
<a class="ui small label topic" href="/explore/repos?q=pcl&topic=">pcl</a> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
*/ | |||
function displayRepo(json){ | |||
var orgRepo = document.getElementById("recommendrepo"); | |||
var html = ""; | |||
@@ -412,7 +420,7 @@ function displayRepo(json){ | |||
html += " <i class=\"ri-star-line\"></i>" + record["NumStars"] + "<i class=\"ri-git-branch-line am-ml-10\"></i>" + record["NumForks"]; | |||
html += " </span>"; | |||
html += " <img class=\"left floated mini ui image\" src=\"" + record["Avatar"] + "\">"; | |||
html += " <a class=\"header nowrap\" href=\"/" + record["OwnerName"] + "/" + record["Name"] + "\">" + record["Name"] +"</a>"; | |||
html += " <a class=\"header nowrap\" href=\"/" + record["OwnerName"] + "/" + record["Name"] + "\">" + record["Alias"] +"</a>"; | |||
html += " <div class=\"description nowrap-2\">" + record["Description"] + " </div>"; | |||
html += " <div class=\"ui tags nowrap am-mt-10\">" | |||
if(record["Topics"] != null){ | |||
@@ -433,27 +441,6 @@ function displayRepo(json){ | |||
swiperRepo.updateProgress(); | |||
} | |||
/** | |||
* | |||
* <div class="column"> | |||
<div class="ui fluid card"> | |||
<div class="content"> | |||
<div class="ui small header"> | |||
<img class="ui image" src="/user/avatar/OpenI/-1"> | |||
<div class="content nowrap"> | |||
<a href="/OpenI">OpenI</a> 启智社区 | |||
<div class="sub header">39 项目 ・ 60 成员 ・ 23 团队</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
*/ | |||
//var repoAndOrgZH = new Map([['1', "项目"], ['2', "成员"], ['3', "团队"]]); | |||
//var repoAndOrgEN = new Map([['1', "Repository"], ['2', "Members"], ['3', "Teams"]]); | |||
function getRepoOrOrg(key,isZhLang,numbers=1){ | |||
if(numbers > 1){ | |||
@@ -465,14 +452,13 @@ function getRepoOrOrg(key,isZhLang,numbers=1){ | |||
return repoAndOrgEN[key]; | |||
} | |||
} | |||
function displayOrg(json){ | |||
var orgDiv = document.getElementById("recommendorg"); | |||
var html = ""; | |||
if (json != null && json.length > 0){ | |||
for(var i = 0; i < json.length;i++){ | |||
var record = json[i] | |||
html += "<div class=\"column\">"; | |||
html += "<div class=\"swiper-slide\">"; | |||
html += " <a href=\"/" + record["Name"] + "\" class=\"ui fluid card\">"; | |||
html += " <div class=\"content\">"; | |||
html += " <div class=\"ui small header\">"; | |||
@@ -488,4 +474,5 @@ function displayOrg(json){ | |||
} | |||
} | |||
orgDiv.innerHTML = html; | |||
} | |||
swiperOrg.updateSlides(); | |||
} |
@@ -883,7 +883,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}, reqRepoReader(models.UnitTypeCloudBrain)) | |||
m.Group("/modelarts", func() { | |||
m.Group("/notebook", func() { | |||
m.Get("/:jobid", repo.GetModelArtsNotebook) | |||
//m.Get("/:jobid", repo.GetModelArtsNotebook) | |||
m.Get("/:jobid", repo.GetModelArtsNotebook2) | |||
}) | |||
m.Group("/train-job", func() { | |||
m.Group("/:jobid", func() { | |||
@@ -118,7 +118,7 @@ func CreateFork(ctx *context.APIContext, form api.CreateForkOption) { | |||
forker = org | |||
} | |||
fork, err := repo_service.ForkRepository(ctx.User, forker, repo, repo.Name, repo.Description) | |||
fork, err := repo_service.ForkRepository(ctx.User, forker, repo, repo.Name, repo.Description, repo.Alias) | |||
if err != nil { | |||
ctx.Error(http.StatusInternalServerError, "ForkRepository", err) | |||
return | |||
@@ -51,6 +51,37 @@ func GetModelArtsNotebook(ctx *context.APIContext) { | |||
} | |||
func GetModelArtsNotebook2(ctx *context.APIContext) { | |||
var ( | |||
err error | |||
) | |||
jobID := ctx.Params(":jobid") | |||
repoID := ctx.Repo.Repository.ID | |||
job, err := models.GetRepoCloudBrainByJobID(repoID, jobID) | |||
if err != nil { | |||
ctx.NotFound(err) | |||
return | |||
} | |||
result, err := modelarts.GetNotebook2(jobID) | |||
if err != nil { | |||
ctx.NotFound(err) | |||
return | |||
} | |||
job.Status = result.Status | |||
err = models.UpdateJob(job) | |||
if err != nil { | |||
log.Error("UpdateJob failed:", err) | |||
} | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"JobID": jobID, | |||
"JobStatus": result.Status, | |||
}) | |||
} | |||
func GetModelArtsTrainJob(ctx *context.APIContext) { | |||
var ( | |||
err error | |||
@@ -232,6 +232,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *models.User, opt api.CreateR | |||
} | |||
repo, err := repo_service.CreateRepository(ctx.User, owner, models.CreateRepoOptions{ | |||
Name: opt.Name, | |||
Alias: opt.Alias, | |||
Description: opt.Description, | |||
IssueLabels: opt.IssueLabels, | |||
Gitignores: opt.Gitignores, | |||
@@ -7,11 +7,11 @@ package routers | |||
import ( | |||
"bytes" | |||
"fmt" | |||
"io/ioutil" | |||
"net/http" | |||
"strings" | |||
"code.gitea.io/gitea/services/repository" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
@@ -133,6 +133,7 @@ type RepoSearchOptions struct { | |||
Restricted bool | |||
PageSize int | |||
TplName base.TplName | |||
Course util.OptionalBool | |||
} | |||
var ( | |||
@@ -211,6 +212,7 @@ func RenderRepoSearch(ctx *context.Context, opts *RepoSearchOptions) { | |||
AllLimited: true, | |||
TopicName: topic, | |||
IncludeDescription: setting.UI.SearchRepoDescription, | |||
Course: opts.Course, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("SearchRepository", err) | |||
@@ -559,7 +561,7 @@ func NotFound(ctx *context.Context) { | |||
func RecommendOrgFromPromote(ctx *context.Context) { | |||
url := setting.RecommentRepoAddr + "organizations" | |||
result, err := recommendFromPromote(url) | |||
result, err := repository.RecommendFromPromote(url) | |||
if err != nil { | |||
ctx.ServerError("500", err) | |||
return | |||
@@ -586,62 +588,11 @@ func RecommendOrgFromPromote(ctx *context.Context) { | |||
ctx.JSON(200, resultOrg) | |||
} | |||
func recommendFromPromote(url string) ([]string, error) { | |||
resp, err := http.Get(url) | |||
if err != nil || resp.StatusCode != 200 { | |||
log.Info("Get organizations url error=" + err.Error()) | |||
return nil, err | |||
} | |||
bytes, err := ioutil.ReadAll(resp.Body) | |||
resp.Body.Close() | |||
if err != nil { | |||
log.Info("Get organizations url error=" + err.Error()) | |||
return nil, err | |||
} | |||
allLineStr := string(bytes) | |||
lines := strings.Split(allLineStr, "\n") | |||
result := make([]string, len(lines)) | |||
for i, line := range lines { | |||
log.Info("i=" + fmt.Sprint(i) + " line=" + line) | |||
result[i] = strings.Trim(line, " ") | |||
} | |||
return result, nil | |||
} | |||
func RecommendRepoFromPromote(ctx *context.Context) { | |||
url := setting.RecommentRepoAddr + "projects" | |||
result, err := recommendFromPromote(url) | |||
result, err := repository.GetRecommendRepoFromPromote("projects") | |||
if err != nil { | |||
ctx.ServerError("500", err) | |||
return | |||
} | |||
resultRepo := make([]map[string]interface{}, 0) | |||
//resultRepo := make([]*models.Repository, 0) | |||
for _, repoName := range result { | |||
tmpIndex := strings.Index(repoName, "/") | |||
if tmpIndex == -1 { | |||
log.Info("error repo name format.") | |||
} else { | |||
ownerName := strings.Trim(repoName[0:tmpIndex], " ") | |||
repoName := strings.Trim(repoName[tmpIndex+1:], " ") | |||
repo, err := models.GetRepositoryByOwnerAndName(ownerName, repoName) | |||
if err == nil { | |||
repoMap := make(map[string]interface{}) | |||
repoMap["ID"] = fmt.Sprint(repo.ID) | |||
repoMap["Name"] = repo.Name | |||
repoMap["OwnerName"] = repo.OwnerName | |||
repoMap["NumStars"] = repo.NumStars | |||
repoMap["NumForks"] = repo.NumForks | |||
repoMap["Description"] = repo.Description | |||
repoMap["NumWatchs"] = repo.NumWatches | |||
repoMap["Topics"] = repo.Topics | |||
repoMap["Avatar"] = repo.RelAvatarLink() | |||
resultRepo = append(resultRepo, repoMap) | |||
} else { | |||
log.Info("query repo error," + err.Error()) | |||
} | |||
} | |||
} else { | |||
ctx.JSON(200, result) | |||
} | |||
ctx.JSON(200, resultRepo) | |||
} |
@@ -16,27 +16,31 @@ const ( | |||
) | |||
type Notice struct { | |||
Title string | |||
Link string | |||
Visible int //0 invisible, 1 visible | |||
Title string | |||
Link string | |||
Visible int //0 invisible, 1 visible | |||
} | |||
type NoticeResponse struct { | |||
Notices []*Notice | |||
CommitId string | |||
} | |||
var lock int32 = 0 | |||
func GetNewestNotice() (*Notice, error) { | |||
func GetNewestNotice() (*NoticeResponse, error) { | |||
defer func() { | |||
if err := recover(); err != nil { | |||
log.Error("recover error", err) | |||
} | |||
}() | |||
var notice *Notice | |||
var notice *NoticeResponse | |||
var err error | |||
if setting.CacheOn { | |||
notice, err = getNewestNoticeFromCacheAndDisk() | |||
notice, err = getNewestNoticesFromCacheAndDisk() | |||
} else { | |||
notice, err = getNewestNoticeFromDisk() | |||
notice, err = getNewestNoticesFromDisk() | |||
} | |||
if err != nil { | |||
@@ -49,34 +53,39 @@ func getNoticeTimeout() time.Duration { | |||
return time.Duration(setting.CacheTimeOutSecond) * time.Second | |||
} | |||
func getNewestNoticeFromDisk() (*Notice, error) { | |||
func getNewestNoticesFromDisk() (*NoticeResponse, error) { | |||
log.Debug("Get notice from disk") | |||
repoFile, err := models.ReadLatestFileInRepo(setting.UserNameOfNoticeRepo, setting.RepoNameOfNoticeRepo, setting.RefNameOfNoticeRepo, setting.TreePathOfNoticeRepo) | |||
repo, err := models.GetRepositoryByOwnerAndAlias(setting.UserNameOfNoticeRepo, setting.RepoNameOfNoticeRepo) | |||
if err != nil { | |||
log.Error("get notice repo failed, error=%v", err) | |||
return nil, err | |||
} | |||
repoFile, err := models.ReadLatestFileInRepo(repo.OwnerName, repo.Name, setting.RefNameOfNoticeRepo, setting.TreePathOfNoticeRepo) | |||
if err != nil { | |||
log.Error("GetNewestNotice failed, error=%v", err) | |||
return nil, err | |||
} | |||
notice := &Notice{} | |||
json.Unmarshal(repoFile.Content, notice) | |||
if notice.Title == "" { | |||
res := &NoticeResponse{} | |||
json.Unmarshal(repoFile.Content, res) | |||
if res == nil || len(res.Notices) == 0 { | |||
return nil, err | |||
} | |||
notice.CommitId = repoFile.CommitId | |||
return notice, nil | |||
res.CommitId = repoFile.CommitId | |||
return res, nil | |||
} | |||
func getNewestNoticeFromCacheAndDisk() (*Notice, error) { | |||
func getNewestNoticesFromCacheAndDisk() (*NoticeResponse, error) { | |||
v, success := noticeCache.Get(NOTICE_CACHE_KEY) | |||
if success { | |||
log.Debug("Get notice from cache,value = %v", v) | |||
if v == nil { | |||
return nil, nil | |||
} | |||
n := v.(*Notice) | |||
n := v.(*NoticeResponse) | |||
return n, nil | |||
} | |||
notice, err := getNewestNoticeFromDisk() | |||
notice, err := getNewestNoticesFromDisk() | |||
if err != nil { | |||
log.Error("GetNewestNotice failed, error=%v", err) | |||
noticeCache.Set(NOTICE_CACHE_KEY, nil, 30*time.Second) | |||
@@ -7,6 +7,10 @@ package org | |||
import ( | |||
"strings" | |||
"code.gitea.io/gitea/services/repository" | |||
"code.gitea.io/gitea/modules/util" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
@@ -14,7 +18,8 @@ import ( | |||
) | |||
const ( | |||
tplOrgHome base.TplName = "org/home" | |||
tplOrgHome base.TplName = "org/home" | |||
tplOrgCourseHome base.TplName = "org/home_courses" | |||
) | |||
// Home show organization home page | |||
@@ -59,10 +64,16 @@ func Home(ctx *context.Context) { | |||
case "fewestforks": | |||
orderBy = models.SearchOrderByForks | |||
default: | |||
ctx.Data["SortType"] = "recentupdate" | |||
orderBy = models.SearchOrderByRecentUpdated | |||
} | |||
if setting.Course.OrgName == org.Name { | |||
ctx.Data["SortType"] = "newest" | |||
orderBy = models.SearchOrderByNewest | |||
} else { | |||
ctx.Data["SortType"] = "recentupdate" | |||
orderBy = models.SearchOrderByRecentUpdated | |||
} | |||
} | |||
orderBy = orderBy + ",id" | |||
keyword := strings.Trim(ctx.Query("q"), " ") | |||
ctx.Data["Keyword"] = keyword | |||
@@ -76,9 +87,18 @@ func Home(ctx *context.Context) { | |||
count int64 | |||
err error | |||
) | |||
pageSize := setting.UI.User.RepoPagingNum | |||
var CourseOptional util.OptionalBool = util.OptionalBoolNone | |||
if setting.Course.OrgName == org.Name { | |||
pageSize = 15 | |||
recommendCourseKeyWords, _ := repository.GetRecommendCourseKeyWords() | |||
ctx.Data["CoursesKeywords"] = recommendCourseKeyWords | |||
} | |||
repos, count, err = models.SearchRepository(&models.SearchRepoOptions{ | |||
ListOptions: models.ListOptions{ | |||
PageSize: setting.UI.User.RepoPagingNum, | |||
PageSize: pageSize, | |||
Page: page, | |||
}, | |||
Keyword: keyword, | |||
@@ -87,6 +107,7 @@ func Home(ctx *context.Context) { | |||
Private: ctx.IsSigned, | |||
Actor: ctx.User, | |||
IncludeDescription: setting.UI.SearchRepoDescription, | |||
Course: CourseOptional, | |||
}) | |||
if err != nil { | |||
ctx.ServerError("SearchRepository", err) | |||
@@ -132,11 +153,27 @@ func Home(ctx *context.Context) { | |||
//find org tag info | |||
tags, err := models.GetAllOfficialTagRepos(org.ID, ctx.Org.IsOwner) | |||
if setting.Course.OrgName == org.Name { | |||
for _, tag := range tags { | |||
for _, repo := range tag.RepoList { | |||
repo.GetCreator() | |||
repo.GetOwner() | |||
} | |||
} | |||
} | |||
if err != nil { | |||
ctx.ServerError("GetAllOfficialTagRepos", err) | |||
return | |||
} | |||
ctx.Data["tags"] = tags | |||
ctx.HTML(200, tplOrgHome) | |||
if setting.Course.OrgName == org.Name { | |||
ctx.HTML(200, tplOrgCourseHome) | |||
} else { | |||
ctx.HTML(200, tplOrgHome) | |||
} | |||
} |
@@ -17,7 +17,8 @@ import ( | |||
const ( | |||
// tplMembers template for organization members page | |||
tplMembers base.TplName = "org/member/members" | |||
tplMembers base.TplName = "org/member/members" | |||
tplCourseMembers base.TplName = "org/member/course_members" | |||
) | |||
// Members render organization users page | |||
@@ -64,8 +65,11 @@ func Members(ctx *context.Context) { | |||
ctx.Data["MembersIsPublicMember"] = membersIsPublic | |||
ctx.Data["MembersIsUserOrgOwner"] = members.IsUserOrgOwner(org.ID) | |||
ctx.Data["MembersTwoFaStatus"] = members.GetTwoFaStatus() | |||
ctx.HTML(200, tplMembers) | |||
if setting.Course.OrgName == org.Name { | |||
ctx.HTML(200, tplCourseMembers) | |||
} else { | |||
ctx.HTML(200, tplMembers) | |||
} | |||
} | |||
// MembersAction response for operation to a member of organization | |||
@@ -10,6 +10,8 @@ import ( | |||
"path" | |||
"strings" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/auth" | |||
"code.gitea.io/gitea/modules/base" | |||
@@ -22,7 +24,8 @@ import ( | |||
const ( | |||
// tplTeams template path for teams list page | |||
tplTeams base.TplName = "org/team/teams" | |||
tplTeams base.TplName = "org/team/teams" | |||
tplCourseTeams base.TplName = "org/team/courseTeams" | |||
// tplTeamNew template path for create new team page | |||
tplTeamNew base.TplName = "org/team/new" | |||
// tplTeamMembers template path for showing team members page | |||
@@ -44,8 +47,12 @@ func Teams(ctx *context.Context) { | |||
} | |||
} | |||
ctx.Data["Teams"] = org.Teams | |||
if setting.Course.OrgName == org.Name { | |||
ctx.HTML(200, tplCourseTeams) | |||
} else { | |||
ctx.HTML(200, tplTeams) | |||
} | |||
ctx.HTML(200, tplTeams) | |||
} | |||
// TeamsAction response for join, leave, remove, add operations to team | |||
@@ -143,7 +150,7 @@ func TeamsRepoAction(ctx *context.Context) { | |||
case "add": | |||
repoName := path.Base(ctx.Query("repo_name")) | |||
var repo *models.Repository | |||
repo, err = models.GetRepositoryByName(ctx.Org.Organization.ID, repoName) | |||
repo, err = models.GetRepositoryByOwnerAndAlias(ctx.Org.Organization.Name, repoName) | |||
if err != nil { | |||
if models.IsErrRepoNotExist(err) { | |||
ctx.Flash.Error(ctx.Tr("org.teams.add_nonexistent_repo")) | |||
@@ -542,7 +542,7 @@ func GetSuccessChunks(ctx *context.Context) { | |||
log.Error("GetPartInfos failed:%v", err.Error()) | |||
} | |||
} else { | |||
chunks, err = storage.GetObsPartInfos(fileChunk.UUID, fileChunk.UploadID) | |||
chunks, err = storage.GetObsPartInfos(fileChunk.UUID, fileChunk.UploadID, fileName) | |||
if err != nil { | |||
log.Error("GetObsPartInfos failed:%v", err.Error()) | |||
} | |||
@@ -336,6 +336,11 @@ func CloudBrainRestart(ctx *context.Context) { | |||
} | |||
func CloudBrainBenchMarkShow(ctx *context.Context) { | |||
if benchmarkTypes == nil { | |||
if err := json.Unmarshal([]byte(setting.BenchmarkTypes), &benchmarkTypes); err != nil { | |||
log.Error("json.Unmarshal BenchmarkTypes(%s) failed:%v", setting.BenchmarkTypes, err, ctx.Data["MsgID"]) | |||
} | |||
} | |||
cloudBrainShow(ctx, tplCloudBrainBenchmarkShow) | |||
} | |||
@@ -400,6 +405,21 @@ func cloudBrainShow(ctx *context.Context, tpName base.TplName) { | |||
} else { | |||
duration = int64(task.UpdatedUnix) - int64(task.CreatedUnix) | |||
} | |||
if benchmarkTypes != nil { | |||
for _, benchmarkType := range benchmarkTypes.BenchmarkType { | |||
if task.BenchmarkTypeID == benchmarkType.Id { | |||
ctx.Data["BenchmarkTypeName"] = benchmarkType.First | |||
for _, benchmarkChildType := range benchmarkType.Second { | |||
if task.BenchmarkChildTypeID == benchmarkChildType.Id { | |||
ctx.Data["BenchmarkChildTypeName"] = benchmarkChildType.Value | |||
break | |||
} | |||
} | |||
break | |||
} | |||
} | |||
} | |||
ctx.Data["duration"] = util.AddZero(duration/3600000) + ":" + util.AddZero(duration%3600000/60000) + ":" + util.AddZero(duration%60000/1000) | |||
ctx.Data["task"] = task | |||
ctx.Data["jobID"] = jobID | |||
@@ -947,7 +967,8 @@ func SyncCloudbrainStatus() { | |||
} | |||
} else if task.Type == models.TypeCloudBrainTwo { | |||
if task.JobType == string(models.JobTypeDebug) { | |||
result, err := modelarts.GetJob(task.JobID) | |||
//result, err := modelarts.GetJob(task.JobID) | |||
result, err := modelarts.GetNotebook2(task.JobID) | |||
if err != nil { | |||
log.Error("GetJob(%s) failed:%v", task.JobName, err) | |||
continue | |||
@@ -1180,6 +1201,7 @@ func CloudBrainBenchmarkCreate(ctx *context.Context, form auth.CreateCloudBrainF | |||
benchmarkChildTypeID := form.BenchmarkChildTypeID | |||
if !jobNamePattern.MatchString(jobName) { | |||
cloudBrainNewDataPrepare(ctx) | |||
ctx.RenderWithErr(ctx.Tr("repo.cloudbrain_jobname_err"), tplCloudBrainBenchmarkNew, &form) | |||
return | |||
} | |||
@@ -0,0 +1,196 @@ | |||
package repo | |||
import ( | |||
"net/http" | |||
"strings" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/auth" | |||
"code.gitea.io/gitea/modules/base" | |||
"code.gitea.io/gitea/modules/context" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/setting" | |||
repo_service "code.gitea.io/gitea/services/repository" | |||
) | |||
const ( | |||
tplCreateCourse base.TplName = "repo/createCourse" | |||
) | |||
func CreateCourse(ctx *context.Context) { | |||
ctx.Data["Title"] = ctx.Tr("new_course") | |||
org, _ := models.GetUserByName(setting.Course.OrgName) | |||
ctx.Data["Owner"] = org | |||
ctx.Data["IsCourse"] = true | |||
ctx.HTML(200, tplCreateCourse) | |||
} | |||
func CreateCoursePost(ctx *context.Context, form auth.CreateCourseForm) { | |||
ctx.Data["Title"] = ctx.Tr("new_course") | |||
if ctx.Written() { | |||
return | |||
} | |||
org, _ := models.GetUserByName(setting.Course.OrgName) | |||
ctx.Data["Owner"] = org | |||
ctx.Data["IsCourse"] = true | |||
var topics = make([]string, 0) | |||
var topicsStr = strings.TrimSpace(form.Topics) | |||
if len(topicsStr) > 0 { | |||
topics = strings.Split(topicsStr, ",") | |||
} | |||
validTopics, invalidTopics := models.SanitizeAndValidateTopics(topics) | |||
if len(validTopics) > 25 { | |||
ctx.RenderWithErr(ctx.Tr("repo.topic.count_prompt"), tplCreateCourse, form) | |||
return | |||
} | |||
if len(invalidTopics) > 0 { | |||
ctx.RenderWithErr(ctx.Tr("repo.topic.format_prompt"), tplCreateCourse, form) | |||
return | |||
} | |||
var repo *models.Repository | |||
var err error | |||
if setting.Course.OrgName == "" || setting.Course.TeamName == "" { | |||
log.Error("then organization name or team name of course is empty.") | |||
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) | |||
return | |||
} | |||
org, team, err := getOrgAndTeam() | |||
if err != nil { | |||
log.Error("Failed to get team from db.", err) | |||
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) | |||
return | |||
} | |||
isInTeam, err := models.IsUserInTeams(ctx.User.ID, []int64{team.ID}) | |||
if err != nil { | |||
log.Error("Failed to get user in team from db.") | |||
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) | |||
return | |||
} | |||
if !isInTeam { | |||
err = models.AddTeamMember(team, ctx.User.ID) | |||
if err != nil { | |||
log.Error("Failed to add user to team.") | |||
ctx.RenderWithErr(ctx.Tr("repo.failed_to_create_course"), tplCreateCourse, form) | |||
return | |||
} | |||
} | |||
if ctx.HasError() { | |||
ctx.HTML(200, tplCreateCourse) | |||
return | |||
} | |||
repo, err = repo_service.CreateRepository(ctx.User, org, models.CreateRepoOptions{ | |||
Name: form.RepoName, | |||
Alias: form.Alias, | |||
Description: form.Description, | |||
Gitignores: "", | |||
IssueLabels: "", | |||
License: "", | |||
Readme: "Default", | |||
IsPrivate: false, | |||
DefaultBranch: "master", | |||
AutoInit: true, | |||
IsCourse: true, | |||
Topics: validTopics, | |||
}) | |||
if err == nil { | |||
log.Trace("Repository created [%d]: %s/%s", repo.ID, org.Name, repo.Name) | |||
ctx.Redirect(setting.AppSubURL + "/" + org.Name + "/" + repo.Name) | |||
return | |||
} | |||
handleCreateCourseError(ctx, org, err, "CreateCoursePost", tplCreateCourse, &form) | |||
} | |||
func AddCourseOrg(ctx *context.Context) { | |||
_, team, err := getOrgAndTeam() | |||
if err != nil { | |||
log.Error("Failed to get team from db.", err) | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"code": 1, | |||
"message": ctx.Tr("repo.addCourseOrgFail"), | |||
}) | |||
return | |||
} | |||
isInTeam, err := models.IsUserInTeams(ctx.User.ID, []int64{team.ID}) | |||
if err != nil { | |||
log.Error("Failed to get user in team from db.", err) | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"code": 1, | |||
"message": ctx.Tr("repo.add_course_org_fail"), | |||
}) | |||
return | |||
} | |||
if !isInTeam { | |||
err = models.AddTeamMember(team, ctx.User.ID) | |||
if err != nil { | |||
log.Error("Failed to add user to team.", err) | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"code": 1, | |||
"message": ctx.Tr("repo.add_course_org_fail"), | |||
}) | |||
return | |||
} | |||
} | |||
ctx.JSON(http.StatusOK, map[string]interface{}{ | |||
"code": 0, | |||
"message": "", | |||
}) | |||
} | |||
func getOrgAndTeam() (*models.User, *models.Team, error) { | |||
org, err := models.GetUserByName(setting.Course.OrgName) | |||
if err != nil { | |||
log.Error("Failed to get organization from db.", err) | |||
return nil, nil, err | |||
} | |||
team, err := models.GetTeam(org.ID, setting.Course.TeamName) | |||
if err != nil { | |||
log.Error("Failed to get team from db.", err) | |||
return nil, nil, err | |||
} | |||
return org, team, nil | |||
} | |||
func handleCreateCourseError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) { | |||
switch { | |||
case models.IsErrReachLimitOfRepo(err): | |||
ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_course_creation", owner.MaxCreationLimit()), tpl, form) | |||
case models.IsErrRepoAlreadyExist(err): | |||
ctx.Data["Err_RepoName"] = true | |||
ctx.RenderWithErr(ctx.Tr("form.course_name_been_taken"), tpl, form) | |||
case models.IsErrNameReserved(err): | |||
ctx.Data["Err_RepoName"] = true | |||
ctx.RenderWithErr(ctx.Tr("repo.form.course_name_reserved", err.(models.ErrNameReserved).Name), tpl, form) | |||
case models.IsErrNamePatternNotAllowed(err): | |||
ctx.Data["Err_RepoName"] = true | |||
ctx.RenderWithErr(ctx.Tr("repo.form.course_name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form) | |||
default: | |||
ctx.ServerError(name, err) | |||
} | |||
} |
@@ -28,7 +28,8 @@ func ServeData(ctx *context.Context, name string, reader io.Reader) error { | |||
buf = buf[:n] | |||
} | |||
ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") | |||
//ctx.Resp.Header().Set("Cache-Control", "public,max-age=86400") | |||
ctx.Resp.Header().Set("Cache-Control", "max-age=0") | |||
name = path.Base(name) | |||
// Google Chrome dislike commas in filenames, so let's change it to a space | |||
@@ -25,8 +25,6 @@ import ( | |||
"code.gitea.io/gitea/modules/obs" | |||
"code.gitea.io/gitea/modules/setting" | |||
"code.gitea.io/gitea/modules/storage" | |||
"github.com/unknwon/com" | |||
) | |||
const ( | |||
@@ -129,6 +127,20 @@ func NotebookNew(ctx *context.Context) { | |||
ctx.HTML(200, tplModelArtsNotebookNew) | |||
} | |||
func notebookNewDataPrepare(ctx *context.Context) error { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
t := time.Now() | |||
var jobName = jobNamePrefixValid(cutString(ctx.User.Name, 5)) + t.Format("2006010215") + strconv.Itoa(int(t.Unix()))[5:] | |||
ctx.Data["job_name"] = jobName | |||
if modelarts.FlavorInfos == nil { | |||
json.Unmarshal([]byte(setting.FlavorInfos), &modelarts.FlavorInfos) | |||
} | |||
ctx.Data["flavors"] = modelarts.FlavorInfos.FlavorInfo | |||
return nil | |||
} | |||
func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { | |||
ctx.Data["PageIsNotebook"] = true | |||
jobName := form.JobName | |||
@@ -173,6 +185,54 @@ func NotebookCreate(ctx *context.Context, form auth.CreateModelArtsNotebookForm) | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | |||
} | |||
func Notebook2Create(ctx *context.Context, form auth.CreateModelArtsNotebookForm) { | |||
ctx.Data["PageIsNotebook"] = true | |||
jobName := form.JobName | |||
uuid := form.Attachment | |||
description := form.Description | |||
flavor := form.Flavor | |||
flavor = "modelarts.bm.910.arm.public.1" | |||
count, err := models.GetCloudbrainNotebookCountByUserID(ctx.User.ID) | |||
if err != nil { | |||
log.Error("GetCloudbrainNotebookCountByUserID failed:%v", err, ctx.Data["MsgID"]) | |||
notebookNewDataPrepare(ctx) | |||
ctx.RenderWithErr("system error", tplModelArtsNotebookNew, &form) | |||
return | |||
} else { | |||
if count >= 1 { | |||
log.Error("the user already has running or waiting task", ctx.Data["MsgID"]) | |||
notebookNewDataPrepare(ctx) | |||
ctx.RenderWithErr("you have already a running or waiting task, can not create more", tplModelArtsNotebookNew, &form) | |||
return | |||
} | |||
} | |||
_, err = models.GetCloudbrainByName(jobName) | |||
if err == nil { | |||
log.Error("the job name did already exist", ctx.Data["MsgID"]) | |||
notebookNewDataPrepare(ctx) | |||
ctx.RenderWithErr("the job name did already exist", tplModelArtsNotebookNew, &form) | |||
return | |||
} else { | |||
if !models.IsErrJobNotExist(err) { | |||
log.Error("system error, %v", err, ctx.Data["MsgID"]) | |||
notebookNewDataPrepare(ctx) | |||
ctx.RenderWithErr("system error", tplModelArtsNotebookNew, &form) | |||
return | |||
} | |||
} | |||
err = modelarts.GenerateNotebook2(ctx, jobName, uuid, description, flavor) | |||
if err != nil { | |||
log.Error("GenerateNotebook2 failed, %v", err, ctx.Data["MsgID"]) | |||
notebookNewDataPrepare(ctx) | |||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookNew, &form) | |||
return | |||
} | |||
ctx.Redirect(setting.AppSubURL + ctx.Repo.RepoLink + "/debugjob?debugListType=all") | |||
} | |||
func NotebookShow(ctx *context.Context) { | |||
ctx.Data["PageIsCloudBrain"] = true | |||
@@ -184,7 +244,7 @@ func NotebookShow(ctx *context.Context) { | |||
return | |||
} | |||
result, err := modelarts.GetJob(jobID) | |||
result, err := modelarts.GetNotebook2(jobID) | |||
if err != nil { | |||
ctx.Data["error"] = err.Error() | |||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookShow, nil) | |||
@@ -200,12 +260,10 @@ func NotebookShow(ctx *context.Context) { | |||
return | |||
} | |||
createTime, _ := com.StrTo(result.CreationTimestamp).Int64() | |||
result.CreateTime = time.Unix(int64(createTime/1000), 0).Format("2006-01-02 15:04:05") | |||
endTime, _ := com.StrTo(result.LatestUpdateTimestamp).Int64() | |||
result.LatestUpdateTime = time.Unix(int64(endTime/1000), 0).Format("2006-01-02 15:04:05") | |||
result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
result.CreateTime = time.Unix(int64(result.CreateAt/1000), 0).Format("2006-01-02 15:04:05") | |||
result.LatestUpdateTime = time.Unix(int64(result.UpdateAt/1000), 0).Format("2006-01-02 15:04:05") | |||
//result.QueuingInfo.BeginTime = time.Unix(int64(result.QueuingInfo.BeginTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
//result.QueuingInfo.EndTime = time.Unix(int64(result.QueuingInfo.EndTimestamp/1000), 0).Format("2006-01-02 15:04:05") | |||
} | |||
ctx.Data["task"] = task | |||
@@ -241,6 +299,18 @@ func NotebookDebug(ctx *context.Context) { | |||
ctx.Redirect(debugUrl) | |||
} | |||
func NotebookDebug2(ctx *context.Context) { | |||
var jobID = ctx.Params(":jobid") | |||
result, err := modelarts.GetNotebook2(jobID) | |||
if err != nil { | |||
ctx.RenderWithErr(err.Error(), tplModelArtsNotebookIndex, nil) | |||
return | |||
} | |||
ctx.Redirect(result.Url) | |||
} | |||
func NotebookManage(ctx *context.Context) { | |||
var jobID = ctx.Params(":jobid") | |||
var action = ctx.Params(":action") | |||
@@ -312,15 +382,18 @@ func NotebookManage(ctx *context.Context) { | |||
param := models.NotebookAction{ | |||
Action: action, | |||
} | |||
res, err := modelarts.ManageNotebook(jobID, param) | |||
res, err := modelarts.ManageNotebook2(jobID, param) | |||
if err != nil { | |||
log.Error("ManageNotebook(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | |||
log.Error("ManageNotebook2(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | |||
resultCode = "-1" | |||
errorMsg = "启动失败" | |||
errorMsg = err.Error() | |||
if strings.Contains(err.Error(), modelarts.NotebookNotFound) { | |||
errorMsg = "the job's version is too old and can not be restarted" | |||
} | |||
break | |||
} | |||
task.Status = res.CurrentStatus | |||
task.Status = res.Status | |||
err = models.UpdateJob(task) | |||
if err != nil { | |||
log.Error("UpdateJob(%s) failed:%v", task.JobName, err.Error(), ctx.Data["MsgID"]) | |||
@@ -352,11 +425,15 @@ func NotebookDel(ctx *context.Context) { | |||
return | |||
} | |||
_, err := modelarts.DelNotebook(jobID) | |||
_, err := modelarts.DelNotebook2(jobID) | |||
if err != nil { | |||
log.Error("DelJob(%s) failed:%v", task.JobName, err.Error()) | |||
ctx.ServerError("DelJob failed", err) | |||
return | |||
log.Error("DelNotebook2(%s) failed:%v", task.JobName, err.Error()) | |||
if strings.Contains(err.Error(), modelarts.NotebookNotFound) { | |||
log.Info("old notebook version") | |||
} else { | |||
ctx.ServerError("DelNotebook2 failed", err) | |||
return | |||
} | |||
} | |||
err = models.DeleteJob(task) | |||
@@ -105,6 +105,7 @@ func getForkRepository(ctx *context.Context) *models.Repository { | |||
return nil | |||
} | |||
ctx.Data["ForkFrom"] = forkRepo.Owner.Name + "/" + forkRepo.Name | |||
ctx.Data["ForkDisplayName"] = forkRepo.FullDisplayName() | |||
ctx.Data["ForkFromOwnerID"] = forkRepo.Owner.ID | |||
if err := ctx.User.GetOwnedOrganizations(); err != nil { | |||
@@ -221,7 +222,7 @@ func ForkPost(ctx *context.Context, form auth.CreateRepoForm) { | |||
} | |||
} | |||
repo, err := repo_service.ForkRepository(ctx.User, ctxUser, forkRepo, form.RepoName, form.Description) | |||
repo, err := repo_service.ForkRepository(ctx.User, ctxUser, forkRepo, form.RepoName, form.Description, form.Alias) | |||
if err != nil { | |||
ctx.Data["Err_RepoName"] = true | |||
switch { | |||
@@ -6,11 +6,14 @@ | |||
package repo | |||
import ( | |||
"code.gitea.io/gitea/modules/validation" | |||
"fmt" | |||
"net/url" | |||
"os" | |||
"path" | |||
"regexp" | |||
"strings" | |||
"unicode/utf8" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/auth" | |||
@@ -201,6 +204,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | |||
if form.RepoTemplate > 0 { | |||
opts := models.GenerateRepoOptions{ | |||
Name: form.RepoName, | |||
Alias: form.Alias, | |||
Description: form.Description, | |||
Private: form.Private, | |||
GitContent: form.GitContent, | |||
@@ -235,6 +239,7 @@ func CreatePost(ctx *context.Context, form auth.CreateRepoForm) { | |||
} else { | |||
repo, err = repo_service.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{ | |||
Name: form.RepoName, | |||
Alias: form.Alias, | |||
Description: form.Description, | |||
Gitignores: form.Gitignores, | |||
IssueLabels: form.IssueLabels, | |||
@@ -358,6 +363,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { | |||
GitServiceType: gitServiceType, | |||
CloneAddr: remoteAddr, | |||
RepoName: form.RepoName, | |||
Alias: form.Alias, | |||
Description: form.Description, | |||
Private: form.Private || setting.Repository.ForcePrivate, | |||
Mirror: form.Mirror, | |||
@@ -380,7 +386,7 @@ func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) { | |||
opts.Releases = false | |||
} | |||
err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName) | |||
err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, opts.Alias) | |||
if err != nil { | |||
handleMigrateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form) | |||
return | |||
@@ -552,3 +558,27 @@ func Status(ctx *context.Context) { | |||
"err": task.Errors, | |||
}) | |||
} | |||
var repoNamePattern = regexp.MustCompile("^[0-9a-zA-Z\\.\\-_]{1,100}$") | |||
var repoAliasPattern = regexp.MustCompile("^[\u4e00-\u9fa5\\.\\-_A-Za-z0-9]{1,100}$") | |||
// CheckName returns repository's default name(by given alias) | |||
func CheckName(ctx *context.Context) { | |||
var r = make(map[string]string, 1) | |||
q := ctx.Query("q") | |||
owner := ctx.Query("owner") | |||
if q == "" || owner == "" || utf8.RuneCountInString(q) > 100 || !validation.ValidAlphaDashDotChinese(q) { | |||
r["name"] = "" | |||
ctx.JSON(200, r) | |||
return | |||
} | |||
if repoNamePattern.MatchString(q) { | |||
r["name"] = q | |||
ctx.JSON(200, r) | |||
return | |||
} | |||
n := models.GenerateDefaultRepoName(owner) | |||
r["name"] = n | |||
ctx.JSON(200, r) | |||
return | |||
} |
@@ -106,7 +106,7 @@ func RepoStatisticDaily(date string) { | |||
repoStat := models.RepoStatistic{ | |||
RepoID: repo.ID, | |||
Date: date, | |||
Name: repo.Name, | |||
Name: repo.Alias, | |||
IsPrivate: repo.IsPrivate, | |||
IsMirror: repo.IsMirror, | |||
OwnerName: repo.OwnerName, | |||
@@ -282,7 +282,7 @@ func RepoStatisticDaily(date string) { | |||
} | |||
func getDistinctProjectName(repo *models.Repository) string { | |||
return repo.OwnerName + "/" + repo.Name | |||
return repo.OwnerName + "/" + repo.Alias | |||
} | |||
func getDatasetSize(repo *models.Repository) (int64, error) { | |||
@@ -6,6 +6,7 @@ | |||
package repo | |||
import ( | |||
"code.gitea.io/gitea/modules/notification" | |||
"errors" | |||
"fmt" | |||
"io/ioutil" | |||
@@ -70,6 +71,30 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
return | |||
} | |||
newAlias := form.Alias | |||
var aliasChanged = false | |||
// Check if repository alias has been changed. | |||
if strings.ToLower(repo.Alias) != strings.ToLower(newAlias) { | |||
aliasChanged = true | |||
//check new alias is available or not | |||
if err := models.IsRepositoryAliasAvailable(ctx.Repo.Owner, newAlias); err != nil { | |||
ctx.Data["Err_Alias"] = true | |||
switch { | |||
case models.IsErrRepoAlreadyExist(err): | |||
ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tplSettingsOptions, &form) | |||
case models.IsErrNameReserved(err): | |||
ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tplSettingsOptions, &form) | |||
case models.IsErrNamePatternNotAllowed(err): | |||
ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tplSettingsOptions, &form) | |||
default: | |||
ctx.ServerError("ChangeRepositoryName", err) | |||
} | |||
return | |||
} | |||
log.Trace("Repository alias changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Alias, newAlias) | |||
} | |||
newRepoName := form.RepoName | |||
// Check if repository name has been changed. | |||
if repo.LowerName != strings.ToLower(newRepoName) { | |||
@@ -95,12 +120,19 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
log.Trace("Repository name changed: %s/%s -> %s", ctx.Repo.Owner.Name, repo.Name, newRepoName) | |||
} | |||
//notify | |||
if aliasChanged { | |||
notification.NotifyRenameRepository(ctx.Repo.Owner, repo, repo.Alias) | |||
} | |||
// In case it's just a case change. | |||
repo.Name = newRepoName | |||
repo.LowerName = strings.ToLower(newRepoName) | |||
repo.Description = form.Description | |||
repo.Website = form.Website | |||
repo.IsTemplate = form.Template | |||
repo.Alias = newAlias | |||
repo.LowerAlias = strings.ToLower(newAlias) | |||
// Visibility of forked repository is forced sync with base repository. | |||
if repo.IsFork { | |||
@@ -380,7 +412,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
ctx.Error(404) | |||
return | |||
} | |||
if repo.Name != form.RepoName { | |||
if repo.Alias != form.RepoName { | |||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) | |||
return | |||
} | |||
@@ -407,7 +439,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
ctx.Error(404) | |||
return | |||
} | |||
if repo.Name != form.RepoName { | |||
if repo.Alias != form.RepoName { | |||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) | |||
return | |||
} | |||
@@ -445,7 +477,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
ctx.Error(404) | |||
return | |||
} | |||
if repo.Name != form.RepoName { | |||
if repo.Alias != form.RepoName { | |||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) | |||
return | |||
} | |||
@@ -465,7 +497,7 @@ func SettingsPost(ctx *context.Context, form auth.RepoSettingForm) { | |||
ctx.Error(404) | |||
return | |||
} | |||
if repo.Name != form.RepoName { | |||
if repo.Alias != form.RepoName { | |||
ctx.RenderWithErr(ctx.Tr("form.enterred_invalid_repo_name"), tplSettingsOptions, nil) | |||
return | |||
} | |||
@@ -63,11 +63,13 @@ func queryUserDataPage(ctx *context.Context, tableName string, queryObj interfac | |||
_, count := models.QueryUserStaticDataByTableName(1, 1, tableName, queryObj, userName) | |||
var indexTotal int64 | |||
indexTotal = 0 | |||
row := 1 | |||
for { | |||
re, _ := models.QueryUserStaticDataByTableName(int(indexTotal), PAGE_SIZE, tableName, queryObj, "") | |||
log.Info("return count=" + fmt.Sprint(count)) | |||
for i, userRecord := range re { | |||
rows := fmt.Sprint(i + 2) | |||
for _, userRecord := range re { | |||
row++ | |||
rows := fmt.Sprint(row) | |||
xlsx.SetCellValue(sheetName, "A"+rows, userRecord.ID) | |||
xlsx.SetCellValue(sheetName, "B"+rows, userRecord.Name) | |||
xlsx.SetCellValue(sheetName, "C"+rows, userRecord.CodeMergeCount) | |||
@@ -35,6 +35,7 @@ import ( | |||
const ( | |||
tplRepoEMPTY base.TplName = "repo/empty" | |||
tplRepoHome base.TplName = "repo/home" | |||
tplCourseHome base.TplName = "repo/courseHome" | |||
tplWatchers base.TplName = "repo/watchers" | |||
tplForks base.TplName = "repo/forks" | |||
tplMigrating base.TplName = "repo/migrating" | |||
@@ -855,7 +856,12 @@ func renderCode(ctx *context.Context) { | |||
ctx.Data["TreeLink"] = treeLink | |||
ctx.Data["TreeNames"] = treeNames | |||
ctx.Data["BranchLink"] = branchLink | |||
ctx.HTML(200, tplRepoHome) | |||
if ctx.Repo.Repository.RepoType == models.RepoCourse { | |||
ctx.HTML(200, tplCourseHome) | |||
} else { | |||
ctx.HTML(200, tplRepoHome) | |||
} | |||
} | |||
// RenderUserCards render a page show users according the input templaet | |||
@@ -708,6 +708,13 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}, reqSignIn) | |||
// ***** END: Organization ***** | |||
m.Group("/course", func() { | |||
m.Get("/create", repo.CreateCourse) | |||
m.Post("/create", bindIgnErr(auth.CreateCourseForm{}), repo.CreateCoursePost) | |||
m.Get("/addOrg", repo.AddCourseOrg) | |||
}, reqSignIn) | |||
// ***** START: Repository ***** | |||
m.Group("/repo", func() { | |||
m.Get("/create", repo.Create) | |||
@@ -718,6 +725,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Combo("/:repoid").Get(repo.Fork). | |||
Post(bindIgnErr(auth.CreateRepoForm{}), repo.ForkPost) | |||
}, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) | |||
m.Get("/check_name", repo.CheckName) | |||
}, reqSignIn) | |||
// ***** Release Attachment Download without Signin | |||
@@ -1021,6 +1029,7 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
m.Group("/modelarts", func() { | |||
m.Group("/notebook", func() { | |||
/* v1.0 | |||
m.Group("/:jobid", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) | |||
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug) | |||
@@ -1029,6 +1038,15 @@ func RegisterRoutes(m *macaron.Macaron) { | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.NotebookCreate) | |||
*/ | |||
m.Group("/:jobid", func() { | |||
m.Get("", reqRepoCloudBrainReader, repo.NotebookShow) | |||
m.Get("/debug", cloudbrain.AdminOrJobCreaterRight, repo.NotebookDebug2) | |||
m.Post("/:action", reqRepoCloudBrainWriter, repo.NotebookManage) | |||
m.Post("/del", cloudbrain.AdminOrOwnerOrJobCreaterRight, repo.NotebookDel) | |||
}) | |||
m.Get("/create", reqRepoCloudBrainWriter, repo.NotebookNew) | |||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateModelArtsNotebookForm{}), repo.Notebook2Create) | |||
}) | |||
m.Group("/train-job", func() { | |||
@@ -116,8 +116,16 @@ func checkAutoLogin(ctx *context.Context) bool { | |||
} | |||
if isSucceed { | |||
isCourse := ctx.QueryBool("course") | |||
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true) | |||
ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL)) | |||
if redirectTo == "" && isCourse { | |||
redirectToCourse := setting.AppSubURL + "/" + setting.Course.OrgName | |||
ctx.RedirectToFirst(redirectToCourse) | |||
} else { | |||
ctx.RedirectToFirst(redirectTo, setting.AppSubURL+string(setting.LandingPageURL)) | |||
} | |||
return true | |||
} | |||
@@ -143,6 +151,7 @@ func SignIn(ctx *context.Context) { | |||
ctx.Data["Title"] = ctx.Tr("sign_in") | |||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | |||
ctx.Data["PageIsSignIn"] = true | |||
ctx.Data["IsCourse"] = ctx.QueryBool("course") | |||
ctx.Data["PageIsLogin"] = true | |||
ctx.Data["EnableSSPI"] = models.IsSSPIEnabled() | |||
ctx.Data["EnableCloudBrain"] = true | |||
@@ -182,6 +191,7 @@ func SignInPost(ctx *context.Context, form auth.SignInForm) { | |||
ctx.Data["SignInLink"] = setting.AppSubURL + "/user/login" | |||
ctx.Data["PageIsSignIn"] = true | |||
ctx.Data["PageIsLogin"] = true | |||
ctx.Data["IsCourse"] = ctx.QueryBool("course") | |||
ctx.Data["EnableSSPI"] = models.IsSSPIEnabled() | |||
if ctx.HasError() { | |||
@@ -565,6 +575,12 @@ func handleSignInFull(ctx *context.Context, u *models.User, remember bool, obeyR | |||
return setting.AppSubURL + "/dashboard" | |||
} | |||
isCourse := ctx.QueryBool("course") | |||
if isCourse { | |||
redirectToCourse := setting.AppSubURL + "/" + setting.Course.OrgName | |||
ctx.RedirectToFirst(redirectToCourse) | |||
return redirectToCourse | |||
} | |||
if redirectTo := ctx.GetCookie("redirect_to"); len(redirectTo) > 0 && !util.IsExternalURL(redirectTo) { | |||
ctx.SetCookie("redirect_to", "", -1, setting.AppSubURL, "", setting.SessionConfig.Secure, true) | |||
if obeyRedirect { | |||
@@ -132,7 +132,7 @@ func getNotifications(c *context.Context) { | |||
} | |||
c.Data["Title"] = c.Tr("notifications") | |||
c.Data["Keyword"] = keyword | |||
//c.Data["Keyword"] = keyword | |||
c.Data["Status"] = status | |||
c.Data["Notifications"] = notifications | |||
@@ -5,12 +5,17 @@ | |||
package repository | |||
import ( | |||
"fmt" | |||
"io/ioutil" | |||
"net/http" | |||
"strings" | |||
"code.gitea.io/gitea/models" | |||
"code.gitea.io/gitea/modules/log" | |||
"code.gitea.io/gitea/modules/notification" | |||
repo_module "code.gitea.io/gitea/modules/repository" | |||
"code.gitea.io/gitea/modules/setting" | |||
pull_service "code.gitea.io/gitea/services/pull" | |||
"fmt" | |||
) | |||
// CreateRepository creates a repository for the user/organization. | |||
@@ -31,8 +36,8 @@ func CreateRepository(doer, owner *models.User, opts models.CreateRepoOptions) ( | |||
} | |||
// ForkRepository forks a repository | |||
func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc string) (*models.Repository, error) { | |||
repo, err := repo_module.ForkRepository(doer, u, oldRepo, name, desc) | |||
func ForkRepository(doer, u *models.User, oldRepo *models.Repository, name, desc, alias string) (*models.Repository, error) { | |||
repo, err := repo_module.ForkRepository(doer, u, oldRepo, name, desc, alias) | |||
if err != nil { | |||
if repo != nil { | |||
if errDelete := models.DeleteRepository(doer, u.ID, repo.ID); errDelete != nil { | |||
@@ -86,3 +91,84 @@ func PushCreateRepo(authUser, owner *models.User, repoName string) (*models.Repo | |||
return repo, nil | |||
} | |||
func GetRecommendCourseKeyWords() ([]string, error) { | |||
url := setting.RecommentRepoAddr + "course_keywords" | |||
result, err := RecommendFromPromote(url) | |||
if err != nil { | |||
return []string{}, err | |||
} | |||
return result, err | |||
} | |||
func GetRecommendRepoFromPromote(filename string) ([]map[string]interface{}, error) { | |||
resultRepo := make([]map[string]interface{}, 0) | |||
url := setting.RecommentRepoAddr + filename | |||
result, err := RecommendFromPromote(url) | |||
if err != nil { | |||
return resultRepo, err | |||
} | |||
//resultRepo := make([]*models.Repository, 0) | |||
for _, repoName := range result { | |||
tmpIndex := strings.Index(repoName, "/") | |||
if tmpIndex == -1 { | |||
log.Info("error repo name format.") | |||
} else { | |||
ownerName := strings.Trim(repoName[0:tmpIndex], " ") | |||
repoName := strings.Trim(repoName[tmpIndex+1:], " ") | |||
repo, err := models.GetRepositoryByOwnerAndAlias(ownerName, repoName) | |||
if err == nil { | |||
repoMap := make(map[string]interface{}) | |||
repoMap["ID"] = fmt.Sprint(repo.ID) | |||
repoMap["Name"] = repo.Name | |||
repoMap["Alias"] = repo.Alias | |||
if repo.RepoType == models.RepoCourse { | |||
//Load creator | |||
repo.GetCreator() | |||
repoMap["Creator"] = repo.Creator | |||
} | |||
repoMap["OwnerName"] = repo.OwnerName | |||
repoMap["NumStars"] = repo.NumStars | |||
repoMap["NumForks"] = repo.NumForks | |||
repoMap["Description"] = repo.Description | |||
repoMap["NumWatchs"] = repo.NumWatches | |||
repoMap["Topics"] = repo.Topics | |||
repoMap["Avatar"] = repo.RelAvatarLink() | |||
resultRepo = append(resultRepo, repoMap) | |||
} else { | |||
log.Info("query repo error," + err.Error()) | |||
} | |||
} | |||
} | |||
return resultRepo, nil | |||
} | |||
func RecommendFromPromote(url string) ([]string, error) { | |||
resp, err := http.Get(url) | |||
if err != nil || resp.StatusCode != 200 { | |||
log.Info("Get organizations url error=" + err.Error()) | |||
return nil, err | |||
} | |||
bytes, err := ioutil.ReadAll(resp.Body) | |||
resp.Body.Close() | |||
if err != nil { | |||
log.Info("Get organizations url error=" + err.Error()) | |||
return nil, err | |||
} | |||
allLineStr := string(bytes) | |||
lines := strings.Split(allLineStr, "\n") | |||
result := make([]string, len(lines)) | |||
for i, line := range lines { | |||
log.Info("i=" + fmt.Sprint(i) + " line=" + line) | |||
result[i] = strings.Trim(line, " ") | |||
} | |||
return result, nil | |||
} |
@@ -55,7 +55,7 @@ func TransferOwnership(doer, newOwner *models.User, repo *models.Repository, tea | |||
// ChangeRepositoryName changes all corresponding setting from old repository name to new one. | |||
func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoName string) error { | |||
oldRepoName := repo.Name | |||
//oldRepoName := repo.Name | |||
// Change repository directory name. We must lock the local copy of the | |||
// repo so that we can atomically rename the repo path and updates the | |||
@@ -68,7 +68,7 @@ func ChangeRepositoryName(doer *models.User, repo *models.Repository, newRepoNam | |||
} | |||
repoWorkingPool.CheckOut(com.ToStr(repo.ID)) | |||
notification.NotifyRenameRepository(doer, repo, oldRepoName) | |||
//notification.NotifyRenameRepository(doer, repo, oldRepoName) | |||
return nil | |||
} |
@@ -50,7 +50,7 @@ func (h *ClientsManager) Run() { | |||
} | |||
case message := <-models.ActionChan: | |||
if isInOpTypes(opTypes, message.OpType) { | |||
message.Comment = nil | |||
filterUserPrivateInfo(message) | |||
LastActionsQueue.Push(message) | |||
for _, client := range h.Clients.Keys() { | |||
select { | |||
@@ -95,6 +95,7 @@ func initActionQueue() { | |||
user, err := models.GetUserByID(actions[i].UserID) | |||
if err == nil { | |||
if !user.IsOrganization() { | |||
filterUserPrivateInfo(actions[i]) | |||
LastActionsQueue.Push(actions[i]) | |||
} | |||
@@ -103,3 +104,19 @@ func initActionQueue() { | |||
} | |||
} | |||
} | |||
func filterUserPrivateInfo(action *models.Action) { | |||
action.Comment = nil | |||
action.ActUser.Email = "" | |||
action.ActUser.Passwd = "" | |||
action.ActUser.PasswdHashAlgo = "" | |||
action.ActUser.PrivateKey = "" | |||
action.ActUser.PublicKey = "" | |||
action.ActUser.Salt = "" | |||
action.ActUser.FullName = "" | |||
action.ActUser.AvatarEmail = "" | |||
action.ActUser.IsAdmin = false | |||
action.ActUser.EmailNotificationsPreference = "" | |||
action.ActUser.IsOperator = false | |||
} |
@@ -36,7 +36,7 @@ | |||
<span class="text gold">{{svg "octicon-lock" 16}}</span> | |||
{{end}} | |||
</td> | |||
<td><a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Name}}</a></td> | |||
<td><a href="{{AppSubUrl}}/{{.Owner.Name}}/{{.Name}}">{{.Alias}}</a></td> | |||
<td><i class="fa fa{{if .IsPrivate}}-check{{end}}-square-o"></i></td> | |||
<td>{{.NumWatches}}</td> | |||
<td>{{.NumStars}}</td> | |||
@@ -200,14 +200,7 @@ var _hmt = _hmt || []; | |||
<div class="ui top secondary stackable main menu following bar dark"> | |||
{{template "base/head_navbar" .}} | |||
</div><!-- end bar --> | |||
<div class="notic_content" id ="notic_content" > | |||
<a href={{.notice.Link}} class="a_width"> | |||
<marquee behavior="scroll" direction="left"> | |||
{{.notice.Title}} | |||
</marquee> | |||
</a> | |||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
</div> | |||
{{template "base/head_notice" .}} | |||
{{end}} | |||
{{/* | |||
</div> | |||
@@ -231,7 +224,15 @@ var _hmt = _hmt || []; | |||
}else{ | |||
isNewNotice=false; | |||
} | |||
if (JSON.parse("{{.notice.Visible}}")){ | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
if(isNewNotice){ | |||
document.getElementById("notic_content").style.display='block' | |||
}else{ | |||
@@ -247,5 +248,8 @@ var _hmt = _hmt || []; | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
isShowNotice(); | |||
</script> | |||
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
isShowNotice(); | |||
} | |||
</script> |
@@ -0,0 +1,209 @@ | |||
<!DOCTYPE html> | |||
<html lang="{{.Language}}"> | |||
<head data-suburl="{{AppSubUrl}}"> | |||
<meta charset="utf-8"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1"> | |||
<meta http-equiv="x-ua-compatible" content="ie=edge"> | |||
<title>{{if .Title}}{{.Title}} - {{end}} {{if .Repository.Name}}{{.Repository.Name}} - {{end}}{{AppName}}</title> | |||
<link rel="manifest" href="{{AppSubUrl}}/manifest.json" crossorigin="use-credentials"> | |||
{{if UseServiceWorker}} | |||
<script> | |||
if ('serviceWorker' in navigator) { | |||
navigator.serviceWorker.register('{{AppSubUrl}}/serviceworker.js').then(function(registration) { | |||
// Registration was successful | |||
console.info('ServiceWorker registration successful with scope: ', registration.scope); | |||
}, function(err) { | |||
// registration failed :( | |||
console.info('ServiceWorker registration failed: ', err); | |||
}); | |||
} | |||
</script> | |||
{{else}} | |||
<script> | |||
if ('serviceWorker' in navigator) { | |||
navigator.serviceWorker.getRegistrations().then(function(registrations) { | |||
registrations.forEach(function(registration) { | |||
registration.unregister(); | |||
console.info('ServiceWorker unregistered'); | |||
}); | |||
}); | |||
} | |||
</script> | |||
{{end}} | |||
<meta name="theme-color" content="{{ThemeColorMetaTag}}"> | |||
<meta name="author" content="{{if .Repository}}{{.Owner.Name}}{{else}}{{MetaAuthor}}{{end}}" /> | |||
<meta name="description" content="{{if .Repository}}{{.Repository.Name}}{{if .Repository.Description}} - {{.Repository.Description}}{{end}}{{else}}{{MetaDescription}}{{end}}" /> | |||
<meta name="keywords" content="{{MetaKeywords}}"> | |||
<meta name="referrer" content="no-referrer" /> | |||
<meta name="_csrf" content="{{.CsrfToken}}" /> | |||
{{if .IsSigned}} | |||
<meta name="_uid" content="{{.SignedUser.ID}}" /> | |||
{{end}} | |||
{{if .ContextUser}} | |||
<meta name="_context_uid" content="{{.ContextUser.ID}}" /> | |||
{{end}} | |||
{{if .SearchLimit}} | |||
<meta name="_search_limit" content="{{.SearchLimit}}" /> | |||
{{end}} | |||
{{if .GoGetImport}} | |||
<meta name="go-import" content="{{.GoGetImport}} git {{.CloneLink.HTTPS}}"> | |||
<meta name="go-source" content="{{.GoGetImport}} _ {{.GoDocDirectory}} {{.GoDocFile}}"> | |||
{{end}} | |||
<script> | |||
{{SafeJS `/* | |||
@licstart The following is the entire license notice for the | |||
JavaScript code in this page. | |||
Copyright (c) 2016 The Gitea Authors | |||
Copyright (c) 2015 The Gogs Authors | |||
Permission is hereby granted, free of charge, to any person obtaining a copy | |||
of this software and associated documentation files (the "Software"), to deal | |||
in the Software without restriction, including without limitation the rights | |||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |||
copies of the Software, and to permit persons to whom the Software is | |||
furnished to do so, subject to the following conditions: | |||
The above copyright notice and this permission notice shall be included in | |||
all copies or substantial portions of the Software. | |||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |||
THE SOFTWARE. | |||
--- | |||
Licensing information for additional javascript libraries can be found at: | |||
{{StaticUrlPrefix}}/vendor/librejs.html | |||
@licend The above is the entire license notice | |||
for the JavaScript code in this page. | |||
*/`}} | |||
</script> | |||
<script> | |||
window.config = { | |||
AppSubUrl: '{{AppSubUrl}}', | |||
StaticUrlPrefix: '{{StaticUrlPrefix}}', | |||
csrf: '{{.CsrfToken}}', | |||
HighlightJS: {{if .RequireHighlightJS}}true{{else}}false{{end}}, | |||
Minicolors: {{if .RequireMinicolors}}true{{else}}false{{end}}, | |||
SimpleMDE: {{if .RequireSimpleMDE}}true{{else}}false{{end}}, | |||
Tribute: {{if .RequireTribute}}true{{else}}false{{end}}, | |||
U2F: {{if .RequireU2F}}true{{else}}false{{end}}, | |||
Heatmap: {{if .EnableHeatmap}}true{{else}}false{{end}}, | |||
heatmapUser: {{if .HeatmapUser}}'{{.HeatmapUser}}'{{else}}null{{end}}, | |||
NotificationSettings: { | |||
MinTimeout: {{NotificationSettings.MinTimeout}}, | |||
TimeoutStep: {{NotificationSettings.TimeoutStep}}, | |||
MaxTimeout: {{NotificationSettings.MaxTimeout}}, | |||
EventSourceUpdateTime: {{NotificationSettings.EventSourceUpdateTime}}, | |||
}, | |||
{{if .RequireTribute}} | |||
tributeValues: [ | |||
{{ range .Assignees }} | |||
{key: '{{.Name}} {{.FullName}}', value: '{{.Name}}', | |||
name: '{{.Name}}', fullname: '{{.FullName}}', avatar: '{{.RelAvatarLink}}'}, | |||
{{ end }} | |||
], | |||
{{end}} | |||
}; | |||
</script> | |||
<link rel="shortcut icon" href="{{StaticUrlPrefix}}/img/favicon.png"> | |||
<link rel="mask-icon" href="{{StaticUrlPrefix}}/img/openi-safari.svg" color="#609926"> | |||
<link rel="fluid-icon" href="{{StaticUrlPrefix}}/img/gitea-lg.png" title="{{AppName}}"> | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/assets/font-awesome/css/font-awesome.min.css"> | |||
<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/icons.woff2" type="font/woff2" crossorigin="anonymous"> | |||
<link rel="preload" as="font" href="{{StaticUrlPrefix}}/fomantic/themes/default/assets/fonts/outline-icons.woff2" type="font/woff2" crossorigin="anonymous"> | |||
{{if .RequireSimpleMDE}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/simplemde/simplemde.min.css"> | |||
{{end}} | |||
{{if .RequireTribute}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/tribute/tribute.css"> | |||
{{end}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/fomantic/semantic.min.css?v={{MD5 AppVer}}"> | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/index.css?v={{MD5 AppVer}}"> | |||
<noscript> | |||
<style> | |||
.dropdown:hover > .menu { display: block; } | |||
.ui.secondary.menu .dropdown.item > .menu { margin-top: 0; } | |||
</style> | |||
</noscript> | |||
{{if .RequireMinicolors}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/vendor/plugins/jquery.minicolors/jquery.minicolors.css"> | |||
{{end}} | |||
<style class="list-search-style"></style> | |||
{{if .PageIsUserProfile}} | |||
<meta property="og:title" content="{{.Owner.Name}}" /> | |||
<meta property="og:type" content="profile" /> | |||
<meta property="og:image" content="{{.Owner.AvatarLink}}" /> | |||
<meta property="og:url" content="{{.Owner.HTMLURL}}" /> | |||
{{if .Owner.Description}} | |||
<meta property="og:description" content="{{.Owner.Description}}"> | |||
{{end}} | |||
{{else if .Repository}} | |||
{{if .Issue}} | |||
<meta property="og:title" content="{{.Issue.Title}}" /> | |||
<meta property="og:url" content="{{.Issue.HTMLURL}}" /> | |||
{{if .Issue.Content}} | |||
<meta property="og:description" content="{{.Issue.Content}}" /> | |||
{{end}} | |||
{{else}} | |||
<meta property="og:title" content="{{.Repository.Name}}" /> | |||
<meta property="og:url" content="{{.Repository.HTMLURL}}" /> | |||
{{if .Repository.Description}} | |||
<meta property="og:description" content="{{.Repository.Description}}" /> | |||
{{end}} | |||
{{end}} | |||
<meta property="og:type" content="object" /> | |||
<meta property="og:image" content="{{.Repository.Owner.AvatarLink}}" /> | |||
{{else}} | |||
<meta property="og:title" content="{{AppName}}"> | |||
<meta property="og:type" content="website" /> | |||
<meta property="og:image" content="{{StaticUrlPrefix}}/img/gitea-lg.png" /> | |||
<meta property="og:url" content="{{AppUrl}}" /> | |||
<meta property="og:description" content="{{MetaDescription}}"> | |||
{{end}} | |||
<meta property="og:site_name" content="{{AppName}}" /> | |||
{{if .IsSigned }} | |||
{{ if ne .SignedUser.Theme "gitea" }} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{.SignedUser.Theme}}.css?v={{MD5 AppVer}}"> | |||
{{end}} | |||
{{else if ne DefaultTheme "gitea"}} | |||
<link rel="stylesheet" href="{{StaticUrlPrefix}}/css/theme-{{DefaultTheme}}.css?v={{MD5 AppVer}}"> | |||
{{end}} | |||
<link rel="stylesheet" href="/RemixIcon_Fonts_v2.5.0/fonts/remixicon.css"> | |||
{{template "custom/header" .}} | |||
<script> | |||
var _hmt = _hmt || []; | |||
(function() { | |||
var hm = document.createElement("script"); | |||
hm.src = "https://hm.baidu.com/hm.js?46149a0b61fdeddfe427ff4de63794ba"; | |||
var s = document.getElementsByTagName("script")[0]; | |||
s.parentNode.insertBefore(hm, s); | |||
})(); | |||
</script> | |||
<script src="/self/func.js" type="text/javascript"></script> | |||
</head> | |||
<body> | |||
{{template "custom/body_outer_pre" .}} | |||
<div class="full height"> | |||
<noscript>{{.i18n.Tr "enable_javascript"}}</noscript> | |||
{{template "custom/body_inner_pre" .}} | |||
{{if not .PageIsInstall}} | |||
<div class="ui top secondary stackable main menu following bar dark"> | |||
{{template "base/head_navbar" .}} | |||
</div><!-- end bar --> | |||
{{end}} | |||
{{/* | |||
</div> | |||
</body> | |||
</html> | |||
*/}} | |||
@@ -201,14 +201,7 @@ var _hmt = _hmt || []; | |||
<div class="ui top secondary stackable main menu following bar dark"> | |||
{{template "base/head_navbar_fluid" .}} | |||
</div><!-- end bar --> | |||
<div class="notic_content" id ="notic_content" > | |||
<a href={{.notice.Link}} class="a_width"> | |||
<marquee behavior="scroll" direction="left"> | |||
{{.notice.Title}} | |||
</marquee> | |||
</a> | |||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
</div> | |||
{{template "base/head_notice" .}} | |||
{{end}} | |||
{{/* | |||
</div> | |||
@@ -232,7 +225,15 @@ var _hmt = _hmt || []; | |||
}else{ | |||
isNewNotice=false; | |||
} | |||
if (JSON.parse("{{.notice.Visible}}")){ | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
if(isNewNotice){ | |||
document.getElementById("notic_content").style.display='block' | |||
}else{ | |||
@@ -248,5 +249,8 @@ var _hmt = _hmt || []; | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
isShowNotice(); | |||
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
isShowNotice(); | |||
} | |||
</script> |
@@ -205,14 +205,7 @@ var _hmt = _hmt || []; | |||
<div class="ui top secondary stackable main menu following bar dark"> | |||
{{template "base/head_navbar" .}} | |||
</div><!-- end bar --> | |||
<div class="notic_content" id ="notic_content" > | |||
<a href={{.notice.Link}} class="a_width"> | |||
<marquee behavior="scroll" direction="left"> | |||
{{.notice.Title}} | |||
</marquee> | |||
</a> | |||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
</div> | |||
{{template "base/head_notice" .}} | |||
{{end}} | |||
{{/* | |||
</div> | |||
@@ -236,7 +229,15 @@ var _hmt = _hmt || []; | |||
}else{ | |||
isNewNotice=false; | |||
} | |||
if (JSON.parse("{{.notice.Visible}}")){ | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
if(isNewNotice){ | |||
document.getElementById("notic_content").style.display='block' | |||
}else{ | |||
@@ -252,5 +253,8 @@ var _hmt = _hmt || []; | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
isShowNotice(); | |||
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
isShowNotice(); | |||
} | |||
</script> |
@@ -211,13 +211,25 @@ | |||
</div> | |||
</form> | |||
{{if .ShowRegistrationButton}} | |||
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up"> | |||
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
{{if .IsCourse}} | |||
<a class="item{{if .PageIsSignUp}} active{{end}}" href="https://git.openi.org.cn/user/sign_up" target="_blank"> | |||
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
</a> | |||
{{else}} | |||
<a class="item{{if .PageIsSignUp}} active{{end}}" href="{{AppSubUrl}}/user/sign_up"> | |||
{{svg "octicon-person" 16}} {{.i18n.Tr "register"}} | |||
</a> | |||
{{end}} | |||
{{end}} | |||
{{if .IsCourse}} | |||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="https://git.openi.org.cn/user/login?course=true" target="_blank"> | |||
{{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
</a> | |||
{{else}} | |||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login"> | |||
{{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
</a> | |||
{{end}} | |||
<a class="item{{if .PageIsSignIn}} active{{end}}" rel="nofollow" href="{{AppSubUrl}}/user/login"> | |||
{{svg "octicon-sign-in" 16}} {{.i18n.Tr "sign_in"}} | |||
</a> | |||
</div><!-- end anonymous right menu --> | |||
{{end}} | |||
@@ -0,0 +1,32 @@ | |||
{{if not .IsCourse}} | |||
{{ if .notices}} | |||
<div class="notic_content" id ="notic_content" style="display: block; position: relative"> | |||
<diV class="ui container"> | |||
<marquee behavior="scroll" direction="left"> | |||
{{ $firstTag := true }} | |||
{{range .notices.Notices}} | |||
{{if eq .Visible 1}} | |||
{{if $firstTag}} | |||
<a href={{.Link}} class="a_width" style = 'margin-left: 0px !important;' target="_blank"> | |||
<i class="ri-arrow-right-s-line"></i> | |||
{{.Title}} | |||
</a> | |||
{{else}} | |||
<a href={{.Link}} class="a_width" target="_blank"> | |||
<i class="ri-arrow-right-s-line"></i> | |||
{{.Title}} | |||
</a> | |||
{{end}} | |||
{{ $firstTag = false }} | |||
{{end}} | |||
{{end}} | |||
</marquee> | |||
<div class="item right" style="position:absolute;right: 1px;top:0px;"> | |||
<i class="ri-close-fill x_icon" onclick="closeNoice()"></i> | |||
</div> | |||
</diV> | |||
</div> | |||
{{end}} | |||
{{end}} |
@@ -201,14 +201,7 @@ var _hmt = _hmt || []; | |||
<div class="ui top secondary stackable main menu following bar dark"> | |||
{{template "base/head_navbar_pro" .}} | |||
</div><!-- end bar --> | |||
<div class="notic_content" id ="notic_content" > | |||
<a href={{.notice.Link}} class="a_width"> | |||
<marquee behavior="scroll" direction="left"> | |||
{{.notice.Title}} | |||
</marquee> | |||
</a> | |||
<i class="icon icon-octicon x_icon" onclick="closeNoice()">{{svg "octicon-x" 16}}</i> | |||
</div> | |||
{{template "base/head_notice" .}} | |||
{{end}} | |||
{{/* | |||
</div> | |||
@@ -233,7 +226,15 @@ var _hmt = _hmt || []; | |||
}else{ | |||
isNewNotice=false; | |||
} | |||
if (JSON.parse("{{.notice.Visible}}")){ | |||
let isShowNoticeTag = false; | |||
let notices= {{.notices.Notices}} | |||
for (i =0;i<notices.length;i++){ | |||
if (notices[i].Visible==1){ | |||
isShowNoticeTag =true; | |||
break; | |||
} | |||
} | |||
if (isShowNoticeTag){ | |||
if(isNewNotice){ | |||
document.getElementById("notic_content").style.display='block' | |||
}else{ | |||
@@ -249,5 +250,8 @@ var _hmt = _hmt || []; | |||
document.getElementById("notic_content").style.display='none' | |||
} | |||
} | |||
isShowNotice(); | |||
if(!("{{.IsCourse}}" == true || "{{.IsCourse}}" =='true')) { | |||
isShowNotice(); | |||
} | |||
</script> |
@@ -26,7 +26,7 @@ | |||
<div class="item"> | |||
<div class="ui header"> | |||
<a class="name" href="{{.Repo.Link}}/datasets?type=0"> | |||
{{.Repo.OwnerName}} / {{.Title}} | |||
{{.Repo.OwnerName}} / {{.Repo.Alias}} | |||
</a> | |||
<div class="ui right metas"> | |||
{{if .Task}} | |||
@@ -53,4 +53,4 @@ | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> |
@@ -5,7 +5,7 @@ | |||
border-radius: 0.8rem; | |||
margin-bottom: 1.0rem; | |||
padding: 1.0rem !important; | |||
} | |||
} | |||
.ui.repository.list>.item .header { | |||
font-size: 1.4rem !important; | |||
font-weight: 200; | |||
@@ -24,7 +24,7 @@ | |||
content: ""; | |||
height: 1px; | |||
background-color: #E1E3E6; | |||
bottom: 2.8rem; | |||
bottom: 2.8rem; | |||
} | |||
.repository .ui.mini.menu{ | |||
font-size: .6rem; | |||
@@ -43,13 +43,13 @@ | |||
<a class="{{if eq .SortType "hot"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=hot&tab={{$.TabName}}"> | |||
<svg class="svg octicon-repo" width="16" height="16" aria-hidden="true"> | |||
<use xlink:href="#octicon-repo" /> | |||
</svg> | |||
</svg> | |||
热门{{.i18n.Tr "explore.repos"}} | |||
</a> | |||
<a class="{{if eq .SortType "active"}}active{{end}} item" href="{{$.Link}}?q={{$.Keyword}}&topic={{$.Topic}}&sort=active&tab={{$.TabName}}"> | |||
<svg class="svg octicon-inbox" width="16" height="16" aria-hidden="true"> | |||
<use xlink:href="#octicon-inbox" /> | |||
</svg> | |||
</svg> | |||
活跃{{.i18n.Tr "explore.repos"}} | |||
</a> | |||
{{end}} | |||
@@ -93,7 +93,7 @@ | |||
<div class="ui grid"> | |||
<div class="ui sixteen wide mobile ten wide tablet twelve wide computer column"> | |||
<a class="name" href="{{.Link}}"> | |||
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} <span>/</span> {{end}}{{end}}<strong>{{.Name}}</strong> | |||
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} <span>/</span> {{end}}{{end}}<strong>{{.DisplayName}}</strong> | |||
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} | |||
</a> | |||
{{if .IsPrivate}} | |||
@@ -114,7 +114,7 @@ | |||
<a class="item"> | |||
<svg class="svg octicon-inbox" width="16" height="16" viewBox="0 0 24 24"> | |||
<path fill="currentColor" d="M17.66 11.2C17.43 10.9 17.15 10.64 16.89 10.38C16.22 9.78 15.46 9.35 14.82 8.72C13.33 7.26 13 4.85 13.95 3C13 3.23 12.17 3.75 11.46 4.32C8.87 6.4 7.85 10.07 9.07 13.22C9.11 13.32 9.15 13.42 9.15 13.55C9.15 13.77 9 13.97 8.8 14.05C8.57 14.15 8.33 14.09 8.14 13.93C8.08 13.88 8.04 13.83 8 13.76C6.87 12.33 6.69 10.28 7.45 8.64C5.78 10 4.87 12.3 5 14.47C5.06 14.97 5.12 15.47 5.29 15.97C5.43 16.57 5.7 17.17 6 17.7C7.08 19.43 8.95 20.67 10.96 20.92C13.1 21.19 15.39 20.8 17.03 19.32C18.86 17.66 19.5 15 18.56 12.72L18.43 12.46C18.22 12 17.66 11.2 17.66 11.2M14.5 17.5C14.22 17.74 13.76 18 13.4 18.1C12.28 18.5 11.16 17.94 10.5 17.28C11.69 17 12.4 16.12 12.61 15.23C12.78 14.43 12.46 13.77 12.33 13C12.21 12.26 12.23 11.63 12.5 10.94C12.69 11.32 12.89 11.7 13.13 12C13.9 13 15.11 13.44 15.37 14.8C15.41 14.94 15.43 15.08 15.43 15.23C15.46 16.05 15.1 16.95 14.5 17.5H14.5Z" /> | |||
</svg> | |||
</svg> | |||
{{.Hot}} | |||
</a> | |||
{{else if eq $.SortType "active"}} | |||
@@ -130,7 +130,7 @@ | |||
<a class="item"> | |||
{{svg "octicon-git-branch" 16}} {{.NumForks}} | |||
</a> | |||
{{end}} | |||
{{end}} | |||
<a class="item"> | |||
{{svg "octicon-star" 16}} {{.NumStars}} | |||
</a> | |||
@@ -1,5 +1,6 @@ | |||
<script src="https://cdn.bootcdn.net/ajax/libs/Swiper/6.8.0/swiper-bundle.min.js"></script> | |||
<link href="https://cdn.bootcdn.net/ajax/libs/Swiper/6.8.0/swiper-bundle.min.css" rel="stylesheet"> | |||
<script src="/swiper/swiper-bundle.min.js"></script> | |||
<link href="/swiper/swiper-bundle.min.css" rel="stylesheet"> | |||
<style> | |||
.explore .repos--seach{ | |||
border-bottom:none; | |||
@@ -20,7 +20,7 @@ | |||
<div id="homenews" class="ui container"> | |||
<p>* {{.page_only_dynamic}}</p> | |||
<div class="ui grid"> | |||
<div class="twelve wide tablet ten wide computer column homenews"> | |||
<div class="sixteen wide mobile twelve wide tablet ten wide computer column homenews"> | |||
<div class="newslist"> | |||
<div class="ui mini aligned list swiper-wrapper" id="newmessage"> | |||
@@ -42,7 +42,11 @@ | |||
<a href="{{AppSubUrl}}/explore/organizations" class="circular ui primary basic button">{{.page_recommend_org_more}} <i class="arrow circle right icon"></i></a> | |||
</div> | |||
<div class="sixteen wide tablet twelve wide computer column"> | |||
<div class="ui stackable three column grid homeorg-list" id="recommendorg"> | |||
<div class="homeorg-list"> | |||
<div class="swiper-wrapper" id="recommendorg"> | |||
</div> | |||
<div class="swiper-pagination"></div> | |||
</div> | |||
</div> | |||
@@ -78,7 +82,7 @@ | |||
<h2>{{.page_dev_env}}</h2> | |||
<p><span class="ui text grey">{{.page_dev_env_desc}}</p> | |||
</div> | |||
<div class="ui four stackable cards"> | |||
<div class="ui four doubling cards"> | |||
<div class="card"> | |||
<div class="image"> | |||
<img src="/img/i-pic-01.svg"> | |||
@@ -158,4 +162,4 @@ | |||
<script src="/home/home.js?v={{MD5 AppVer}}" type="text/javascript"></script> | |||
{{template "base/footer" .}} | |||
{{template "base/footer" .}} |
@@ -0,0 +1,137 @@ | |||
<style> | |||
.text-right{ | |||
float:right !important; | |||
} | |||
.header{ | |||
font-weight:bold; | |||
font-size: 18px; | |||
font-family: SourceHanSansSC-medium; | |||
} | |||
.cor{ | |||
color:#0366D6 !important; | |||
} | |||
.header_card{ | |||
/* color:#003A8C !important; */ | |||
color:#101010 !important; | |||
margin: 10px 0 0px 0; | |||
height: 25px; | |||
font-size: 18px; | |||
} | |||
.marg{ | |||
margin: 0 5px !important; | |||
} | |||
.content_list{ | |||
max-height: 130px; | |||
overflow: auto; | |||
} | |||
.Relist{ | |||
color:#0366D6 !important; | |||
} | |||
.descript_height{ | |||
color: #999999 !important; | |||
margin: 10px 0; | |||
height: 40px !important; | |||
word-break:break-all; | |||
line-height: 20px; | |||
overflow: hidden; | |||
/* overflow: hidden!important; | |||
word-wrap:break-word!important; */ | |||
} | |||
.tags_height{ | |||
height: 30px !important; | |||
} | |||
.full_height{ | |||
height: 100%; | |||
} | |||
.omit{ | |||
overflow: hidden; white-space: nowrap; text-overflow: ellipsis; | |||
} | |||
/deep/ ui.checkbox input[type=checkbox]::after{ | |||
border: 1px solid #0366D6 !important; | |||
} | |||
.nowrap-2 { | |||
/* height: 2.837em; */ | |||
/* line-height: 1.4285em; */ | |||
overflow: hidden; | |||
overflow: hidden; | |||
display: -webkit-box; | |||
-webkit-line-clamp: 2; | |||
-webkit-box-orient: vertical; | |||
} | |||
.course_topic{ | |||
color:#0366D6 !important; | |||
background-color: rgba(179, 219, 219, 0.4) !important; | |||
font-weight:normal !important | |||
} | |||
.tag_text{ | |||
/* margin-top: 2px; */ | |||
/* margin-left: 0.5em; */ | |||
font-size: 14px; | |||
} | |||
.card{ | |||
box-shadow: 0px 4px 4px 0px rgba(232, 232, 232, 60) !important; | |||
border-radius: 5px; | |||
border:1px solid #E8E8E8 !important; | |||
} | |||
.tags{ | |||
position: relative; | |||
overflow: hidden; | |||
height: 30px; | |||
line-height: 30px; | |||
-webkit-line-clamp: 1; | |||
-webkit-box-orient: vertical; | |||
} | |||
</style> | |||
<div style="width: 100%;"> | |||
<div class="ui three cards" style="margin-bottom: 10px;"> | |||
{{range .Repos}} | |||
<div class="card " > | |||
<div class="extra full_height cor" > | |||
<div class="content " > | |||
{{if .Topics }} | |||
<div class="omit tags " style="position: relative;"> | |||
{{range .Topics}} | |||
{{if ne . "" }}<a style="max-width:100%;margin: 5px 0;display:inline-flex;cursor:default" ><span class="ui small label topic course_topic" >{{.}}</span></a>{{end}} | |||
{{end}} | |||
</div> | |||
{{end}} | |||
</div> | |||
<div class=" header header_card omit" > | |||
<a class="header_card image poping up " href="{{.Link}}" data-content="{{.Alias}}" data-position="top left" data-variation="tiny inverted"> {{.Alias}}</a> | |||
</div> | |||
<div class='content descript_height nowrap-2'> | |||
{{.Description}} | |||
</div> | |||
</div> | |||
<div class=" extra content" style="color:#888888;border-top: none !important;padding-top: 0px;margin-bottom: 15px;"> | |||
<div class="left aligned author"> | |||
<!-- <span > --> | |||
{{if .Creator }} | |||
<a href="{{.Creator.Name}}" title="{{.Creator.Name}}"> | |||
<img class="ui avatar image" style="width: 22px;height:22px;margin-top:-5px" src="{{.Creator.RelAvatarLink}}"> | |||
</a> | |||
{{else}} | |||
<a href="{{.Owner.Name}}" title="{{.Owner.Name}}"> | |||
<img class="ui avatar image" style="width: 22px;height:22px;margin-top:-5px" src="{{.Owner.RelAvatarLink}}"> | |||
</a> | |||
{{end}} | |||
{{$.i18n.Tr "org.repo_released"}} : {{TimeSinceUnixShort .CreatedUnix}} | |||
<!-- </span> --> | |||
</div> | |||
</div> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> |
@@ -8,20 +8,58 @@ | |||
<img class="ui image" src="{{.SizedRelAvatarLink 100}}"> | |||
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> | |||
{{end}} | |||
{{if .IsOrganizationOwner}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a> | |||
</div> | |||
{{end}} | |||
{{if .CanCreateOrgRepo}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{svg "octicon-plus" 16}} {{.i18n.Tr "new_repo"}}</a> | |||
</div> | |||
{{end}} | |||
{{if .IsCourse}} | |||
{{if .CanCreateOrgRepo}} | |||
<div class="ui right"> | |||
<a class="ui green button" onclick="jion_course_team()">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.teams.join_teams"}}</a> | |||
</div> | |||
{{end}} | |||
{{else}} | |||
{{if .IsOrganizationOwner}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a> | |||
</div> | |||
{{end}} | |||
{{if .CanCreateOrgRepo}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{svg "octicon-plus" 16}} {{.i18n.Tr "new_repo"}}</a> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<script> | |||
function jion_course_team(){ | |||
$.ajax({ | |||
type:"GET", | |||
url:"/course/addOrg", | |||
dataType:"json", | |||
async:false, | |||
success:function(json){ | |||
data = json; | |||
if (data.code==0) { | |||
$('.alert').html('{{.i18n.Tr "repo.computing.success"}}').removeClass('alert-danger').addClass('alert-success').show().delay(2000).fadeOut(); | |||
} else { | |||
$('.alert').html(data.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||
} | |||
setTimeout("location.reload()",2000); | |||
// location.reload() | |||
// if(data.code==0){ | |||
// alert("Join success") | |||
// location.reload() | |||
// }else{ | |||
// alert("Join failure") | |||
// } | |||
}, | |||
}); | |||
} | |||
</script> | |||
@@ -0,0 +1,32 @@ | |||
<div class="organization-header"> | |||
<div class="ui container"> | |||
<div class="ui vertically grid head"> | |||
<div class="column"> | |||
<div class="ui header"> | |||
{{with .Org}} | |||
<img class="ui image" src="{{.SizedRelAvatarLink 100}}"> | |||
<span class="text thin grey"><a href="{{.HomeLink}}">{{.DisplayName}}</a></span> | |||
{{end}} | |||
{{if .IsCourse}} | |||
{{if .IsOrganizationOwner}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a> | |||
</div> | |||
{{end}} | |||
{{else}} | |||
{{if .IsOrganizationOwner}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{.OrgLink}}/teams/new">{{svg "octicon-plus" 16}} {{.i18n.Tr "org.create_new_team"}}</a> | |||
</div> | |||
{{end}} | |||
{{if .CanCreateOrgRepo}} | |||
<div class="ui right"> | |||
<a class="ui green button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{svg "octicon-plus" 16}} {{.i18n.Tr "new_repo"}}</a> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,450 @@ | |||
<style> | |||
.organization-info_1000{ | |||
background: #F5F5F6 !important; | |||
padding-top: 30px; | |||
margin-bottom: 0px !important; | |||
} | |||
.organization-info >.container { | |||
overflow: auto; | |||
background: #f5f5f6 !important; | |||
padding-top: 30px; | |||
padding-bottom: 20px; | |||
background-size: cover; | |||
border-radius: 5px; | |||
border: none !important | |||
} | |||
.organization.profile #org-avatar { | |||
border:none !important | |||
} | |||
.desc { | |||
font-size: 14px; | |||
margin-bottom: 10px !important; | |||
} | |||
.item { | |||
display: inline-block; | |||
margin-right: 10px; | |||
} | |||
.item .icon { | |||
margin-right: 5px; | |||
} | |||
.organization-info >.container { | |||
padding-bottom:0px !important; | |||
} | |||
.tag_bg{ | |||
background-color: #0366D6 !important; | |||
color:#FFFFFF !important; | |||
} | |||
.course{ | |||
padding:10px 0 15px !important; | |||
} | |||
.course_color{ | |||
color: #FA8C16; | |||
} | |||
.tag_lable{ | |||
border: 1px solid rgba(232, 232, 232, 100) ; | |||
border-radius: 4px; | |||
color: rgba(65, 80, 88, 100); | |||
font-family: Microsoft Yahei; | |||
font-size: 14px; | |||
padding: 0.3em 0.5em; | |||
height: 30px; | |||
text-align: center; | |||
margin: 0.2em; | |||
} | |||
.tag_lable_first{ | |||
border: 1px solid rgba(232, 232, 232, 100) ; | |||
border-radius: 4px; | |||
color: rgba(65, 80, 88, 100); | |||
font-family: Microsoft Yahei; | |||
font-size: 14px; | |||
padding: 0.3em 0.5em; | |||
height: 30px; | |||
text-align: center; | |||
margin: 0.2em; | |||
margin-left: none; | |||
} | |||
.tag_key{ | |||
max-width:100%; | |||
margin: 3px 3px; | |||
display:inline-flex; | |||
} | |||
.bpadding{ | |||
padding:10px 40px | |||
} | |||
.omit{ | |||
overflow: hidden; white-space: nowrap; text-overflow: ellipsis; | |||
} | |||
.noborder{ | |||
border: none !important; | |||
} | |||
.div_bt{ | |||
text-align: center; | |||
margin-top: 5px; | |||
margin-top: 10px; | |||
} | |||
</style> | |||
{{template "base/head" .}} | |||
<!-- 提示框 --> | |||
<div class="alert"></div> | |||
<div class="organization profile"> | |||
{{/* overflow: auto is the clearfix - this avoids the image going beyond | |||
the container where it is supposed to stay inside. */}} | |||
<div class="organization-info organization-info_1000"> | |||
<div class="ui center aligned container " style="overflow: auto"> | |||
<img class="ui left image" id="org-avatar" src="{{.Org.SizedRelAvatarLink 140}}"/> | |||
<div class="content" style="text-align: left;margin-left:100px" > | |||
<div class="ui header" > | |||
{{.Org.DisplayName}} | |||
</div> | |||
<div class="description" > | |||
{{if .Org.Description}}<p class="text grey desc">{{.Org.Description}}</p>{{end}} | |||
</div> | |||
<div class="meta" style="display: inline-flex;"> | |||
{{if .Org.Location}}<div class="item">{{svg "octicon-location" 16}} <span>{{.Org.Location}}</span></div>{{end}} | |||
{{if .Org.Website}}<div class="item">{{svg "octicon-link" 16}} <a target="_blank" rel="noopener noreferrer" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "org/navber_course" .}} | |||
<div class="ui container"> | |||
<!-- 新增 --> | |||
<div class="ui stackable grid"> | |||
<div class="ui sixteen wide computer column"> | |||
<div class="ui mobile reversed stackable grid"> | |||
<div class="ui ten wide tablet twelve wide computer column" id='tag'> | |||
<a class="{{if eq $.Keyword "" }} tag_bg {{end}} tag_key ui small tag_lable topic omit" href="{{$.Link}}?" >{{$.i18n.Tr "org.all_keywords"}}</span></a> | |||
{{range .CoursesKeywords}} | |||
{{if ne . ""}} | |||
<a class="{{if eq $.Keyword . }} tag_bg {{end}} tag_key ui small tag_lable topic omit" href="{{$.Link}}?q={{.}}" > | |||
{{.}} | |||
</a> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
<div class="ui sixteen wide mobile six wide tablet four wide computer column"> | |||
<div class=" ui bottom attached segment text center noborder text center" > | |||
<a style="width: 80%;" class="ui green button bpadding" href="https://git.openi.org.cn/course/create" target="_blank"><i class="ri-folder-add-line" style="vertical-align: middle;"></i> {{.i18n.Tr "org.release_course"}} </a> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<!-- 全部 --> | |||
<div class="ui stackable grid"> | |||
<div class="ui sixteen wide computer column"> | |||
<div class="ui mobile reversed stackable grid"> | |||
<div class="ui ten wide tablet twelve wide computer column"> | |||
{{template "org/course_list" .}} | |||
{{template "base/paginate" .}} | |||
</div> | |||
<div class="ui sixteen wide mobile six wide tablet four wide computer column"> | |||
{{if .tags}} | |||
<h4 class="ui top attached header noborder"> | |||
<strong>{{.i18n.Tr "org.selected_couse"}}</strong> | |||
{{if .IsOrganizationOwner}} | |||
<div class="ui right"> | |||
<a class="text grey" id="model" onclick="showcreate()">{{svg "octicon-gear" 16}}</a> | |||
</div> | |||
{{end}} | |||
</h4> | |||
<div class="ui attached table segment course items noborder"> | |||
{{ range .tags}} | |||
{{if eq .TagName "精选项目"}} | |||
{{range $i, $v := .RepoList}} | |||
{{if gt $i 0}} | |||
<div class="ui divider" style="margin-bottom:10px;"></div> | |||
{{end}} | |||
<div class="item"> | |||
<i class="large icon ri-bookmark-3-line course_color"></i> | |||
<div class="content" style="margin-left: 10px;"> | |||
<a href="{{.Link}}"><strong class="team-name">{{.Alias}}</strong></a> | |||
<p class="text grey"> | |||
{{if ne .CreatorID 0}} | |||
{{$.i18n.Tr "home.contributor"}} : {{.Creator.Name}} | |||
{{else}} | |||
{{$.i18n.Tr "home.contributor"}}:{{.Owner.Name}} | |||
{{end}} | |||
</p> | |||
</div> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
{{end}} | |||
</div> | |||
{{end}} | |||
<h4 class="ui top attached header noborder"> | |||
<strong>{{.i18n.Tr "org.people"}}</strong> | |||
<div class="ui right"> | |||
<a class="text grey" href="{{.OrgLink}}/members">{{.MembersTotal}} {{svg "octicon-chevron-right" 16}}</a> | |||
</div> | |||
</h4> | |||
<div class="ui attached segment members course noborder"> | |||
{{$isMember := .IsOrganizationMember}} | |||
{{range .Members}} | |||
{{if or $isMember (.IsPublicMember $.Org.ID)}} | |||
<a href="{{.HomeLink}}" title="{{.Name}}{{if .FullName}} ({{.FullName}}){{end}}"><img class="ui avatar" src="{{.RelAvatarLink}}"></a> | |||
{{end}} | |||
{{end}} | |||
<div class="ui bottom attached segment text center noborder"> | |||
{{if .IsSigned}} | |||
<a class="ui blue basic button" onclick="jion_course_team()" style="width: 80%;"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a> | |||
{{else}} | |||
<a class="ui blue basic button" href="https://git.openi.org.cn/user/login?course=true" style="width: 80%;" target="_blank"> <i class="ri-user-add-line"></i> {{.i18n.Tr "org.teams.join_teams"}}</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{if .IsOrganizationMember}} | |||
<div class="ui top attached header noborder"> | |||
<strong>{{.i18n.Tr "org.teams"}}</strong> | |||
<div class="ui right"> | |||
<a class="text grey" href="{{.OrgLink}}/teams"><span>{{.Org.NumTeams}}</span> {{svg "octicon-chevron-right" 16}}</a> | |||
</div> | |||
</div> | |||
<div class="ui attached table segment teams noborder"> | |||
{{range .Teams}} | |||
<div style="margin-top: 10px;"> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong class="team-name">{{.Name}}</strong></a> | |||
<p class="text grey"> | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.NumMembers}}</strong> {{$.i18n.Tr "org.lower_members"}}</a> · | |||
<a href="{{$.OrgLink}}/teams/{{.LowerName}}/repositories"><strong>{{.NumRepos}}</strong> {{$.i18n.Tr "org.lower_repositories"}}</a> | |||
</p> | |||
</div> | |||
{{end}} | |||
</div> | |||
{{if .IsOrganizationOwner}} | |||
<div class="ui bottom attached segment text center noborder"> | |||
<a class="ui blue basic button" style="width: 80%;" href="{{.OrgLink}}/teams/new">{{.i18n.Tr "org.create_new_team"}}</a> | |||
</div> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui modal"> | |||
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);"> | |||
<h4 id="model_header">{{.i18n.Tr "org.custom_select_courses"}}</h4> | |||
</div> | |||
<div class="content content-padding" style="color: black;"> | |||
<p>{{.i18n.Tr "org.max_selectedPro"}}</p> | |||
<div class="ui search" > | |||
<div class="ui input" style="width: 100%;"> | |||
<input type="text" id = 'search_selectPro' placeholder="Search ..." value = '' oninput="search()"> | |||
</div> | |||
</div> | |||
<div style="margin: 10px ;"> | |||
<div id ='org_list' style="margin-bottom: 20px;"class="content_list" > | |||
</div> | |||
</div> | |||
<p id='recommend'></p> | |||
<div class="inline field" style="margin-left: 37%;"> | |||
<div class="actions"> | |||
<button id="submitId" type="button" class="ui create_train_job green deny button" onclick="saveSeletedPro(1)"> | |||
{{.i18n.Tr "explore.save"}} | |||
</button> | |||
<button class="ui button cancel" >{{.i18n.Tr "explore.cancel"}}</button> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
var data; | |||
var filterData=[]; | |||
var num=0; | |||
function showcreate(obj){ | |||
document.getElementById("search_selectPro").value='' | |||
$('.ui.modal') | |||
.modal({ | |||
centered: false, | |||
onShow:function(){ | |||
$("#org_list").empty() | |||
getPro(1) | |||
}, | |||
onHide:function(){ | |||
} | |||
}) | |||
.modal('show') | |||
} | |||
function getPro(typeTag){ | |||
$.ajax({ | |||
type:"GET", | |||
url:"/org/{{.Org.Name}}/org_tag/repo_list?tagId="+typeTag, | |||
dataType:"json", | |||
async:false, | |||
success:function(json){ | |||
data = json.data; | |||
var n_length = data.length | |||
pro_html = getHTML(data) | |||
$("#org_list").append(pro_html) | |||
// console.log('原始',data) | |||
checkedNum(0) | |||
} | |||
}); | |||
} | |||
function getHTML(data){ | |||
let pro_html='' | |||
for (let i=0;i<data.length;i++){ | |||
if (data[i].Selected==true){ | |||
console.log("data[i]:",data[i]) | |||
pro_html += `<div class="ui checkbox" style="width: 33%;margin-bottom:10px" > <input type="checkbox" id = " ${i}" checked="" onclick="checkedNum(${i})" class="Relist" name ='select_pro_name' data-repoid="${data[i].RepoID}" data-reponame="${data[i].RepoName}" data-selected=${data[i].Selected} > <label class='omit image poping up' data-content=${data[i].RepoName} data-position="top left " data-variation="mini"> ${data[i].RepoName}</label></div>` | |||
pro_html += '</div>' | |||
} | |||
else{ | |||
pro_html += `<div class="ui checkbox" style="width: 33%;margin-bottom:10px" > <input type="checkbox" id = "${i}" onclick="checkedNum(${i})" class="Relist" name ='select_pro_name' data-repoid="${data[i].RepoID}" data-reponame="${data[i].RepoName}" data-selected= ${data[i].Selected}> <label class='omit image poping up' data-content=${data[i].RepoName} data-position="top left " data-variation="mini"> ${data[i].RepoName} </label></div>` | |||
pro_html += '</div>' | |||
} | |||
} | |||
return pro_html | |||
} | |||
function saveSeletedPro(typeTag){ | |||
var saveData=[]; | |||
$('input[name="select_pro_name"]:checked').each(function(){ | |||
// console.log('值',this.dataset.repoid) | |||
saveData.push(parseInt(this.dataset.repoid)); | |||
}) | |||
if(saveData.length>9){ | |||
alert("{{.i18n.Tr "org.save_fail_tips"}}") | |||
return | |||
} | |||
$.ajax({ | |||
type:"POST", | |||
url:"/org/{{.Org.Name}}/org_tag/repo_submit?tagId="+typeTag, | |||
contentType:'application/json', | |||
dataType:"json", | |||
async:false, | |||
data:JSON.stringify({'repoList':saveData | |||
}), | |||
success:function(res){ | |||
// console.log('保存成功'); | |||
location.reload() | |||
} | |||
}); | |||
} | |||
function getSelecteData(){ | |||
var selectedData=[]; | |||
$('input[name="select_pro_name"]:checked').each(function(){ | |||
// console.log(this) | |||
// console.log('值',this.dataset.selected) | |||
selectedData.push({"RepoID":parseInt(this.dataset.repoid),"RepoName":this.dataset.reponame,"Selected":JSON.parse(this.dataset.selected)}); | |||
}) | |||
return selectedData | |||
} | |||
function search(){ | |||
var selectedData = getSelecteData(); | |||
var searchValue = document.getElementById("search_selectPro").value; | |||
filterData=[]; | |||
console.log("searchValue:",searchValue) | |||
for (let i=0;i<data.length;i++){ | |||
var isInclude=false; | |||
if(data[i].RepoName.toLowerCase().includes(searchValue.toLowerCase())){ | |||
filterData.push(data[i]) | |||
} | |||
} | |||
// console.log("选中的值:",selectedData) | |||
// console.log("筛选包括选中的值:",filterData) | |||
var showData=[]; | |||
for(i=0;i<selectedData.length;i++){ | |||
filterData =filterData.filter((item)=>{ | |||
return item.RepoID!=selectedData[i].RepoID | |||
}); | |||
} | |||
// console.log("筛选后不包括选中的值:",filterData) | |||
$("#org_list").empty() | |||
if(searchValue!=""){ | |||
if (filterData.length!=0){ | |||
var pro_html = getHTML(selectedData); | |||
console.log("selectedData_pro_html:",pro_html) | |||
$("#org_list").append(pro_html) | |||
pro_html= getHTML(filterData); | |||
$("#org_list").append(pro_html) | |||
}else{ | |||
var pro_html = getHTML(selectedData); | |||
$("#org_list").append(pro_html) | |||
} | |||
}else{ | |||
var pro_html = getHTML(data); | |||
$("#org_list").append(pro_html) | |||
} | |||
} | |||
function checkedNum(id){ | |||
num=0; | |||
var inputs = document.getElementsByName("select_pro_name") | |||
for (var i=0;i<inputs.length;i++){ | |||
if(inputs[i].checked){ | |||
num++ | |||
if(num>9){ | |||
document.getElementById(id).checked=false | |||
alert( "{{.i18n.Tr "org.select_again"}}") | |||
return | |||
} | |||
} | |||
} | |||
var show_num = 9-num; | |||
let rec = "{{.i18n.Tr "org.recommend_remain_pro"}}" | |||
document.getElementById("recommend").innerHTML=rec +" : "+ show_num | |||
} | |||
function jion_course_team(){ | |||
$.ajax({ | |||
type:"GET", | |||
url:"/course/addOrg", | |||
dataType:"json", | |||
async:false, | |||
success:function(json){ | |||
data = json; | |||
if (data.code==0) { | |||
$('.alert').html('{{.i18n.Tr "repo.computing.success"}}').removeClass('alert-danger').addClass('alert-success').show().delay(2000).fadeOut(); | |||
} else { | |||
$('.alert').html(data.error_msg).removeClass('alert-success').addClass('alert-danger').show().delay(5000).fadeOut(); | |||
} | |||
setTimeout("location.reload()",2000); | |||
}, | |||
}); | |||
} | |||
</script> |
@@ -0,0 +1,130 @@ | |||
<style> | |||
.organization-header{ | |||
margin-bottom: 0px !important; | |||
border-bottom:none !important | |||
} | |||
.course_header{ | |||
color: rgba(3, 102, 214, 100); | |||
font-size: 16px; | |||
text-align: left; | |||
font-family: SourceHanSansSC-medium; | |||
font-weight: bolder; | |||
vertical-align: bottom; | |||
} | |||
.meb_label{ | |||
border-radius: 5px; | |||
background-color: rgba(255, 255, 255, 100) !important; | |||
color: rgba(255, 255, 255, 100); | |||
font-size: 12px; | |||
text-align: center; | |||
font-family: Roboto; | |||
border: 1px solid rgba(212, 212, 213, 100) !important; | |||
margin-left: 1em !important; | |||
} | |||
.ui.small.label.topic { | |||
margin-bottom: 0px !important; | |||
} | |||
.cor{ | |||
color:#888888 !important | |||
} | |||
.card_course{ | |||
padding:1em; | |||
border: 1px solid #F5F5F6; | |||
margin:1em;box-shadow: 0px 4px 4px 0px rgba(232, 232, 232, 60); | |||
border-radius: 5px;border: 1px solid rgba(232, 232, 232, 100); | |||
display: flex; width:calc(33.33333333333333% - 2em) | |||
} | |||
.button_leaveOrg{ | |||
position:absolute;right: -1px;top:0px; | |||
} | |||
.bt_mr{ | |||
margin-right: 0px !important; | |||
font-size: 12px !important; | |||
padding: 5px !important; | |||
} | |||
</style> | |||
{{template "base/head" .}} | |||
<!-- 提示框 --> | |||
<div class="alert"></div> | |||
<div class="organization members"> | |||
{{template "org/header" .}} | |||
{{template "org/navber_course" .}} | |||
<div class="ui container"> | |||
{{template "base/alert" .}} | |||
<div class="ui stackable grid"> | |||
<div class="ui sixteen wide computer column list"> | |||
<div class="ui three cards" > | |||
{{ range .Members}} | |||
<div class="card_course" style="position: relative;" id = "{{.ID}}" onmouseover ="show_bt( {{.ID}} )" onmouseout="hide_bt({{.ID}})"> | |||
<div > | |||
<img class="ui avatar " style="width: 45px;height:45px;margin-top: 2px;" src="{{.SizedRelAvatarLink 48}}"> | |||
<div class="meta" style="text-align: center; margin-top: 0.5em;"> | |||
{{ $isPublic := index $.MembersIsPublicMember .ID}} | |||
{{if $isPublic}} | |||
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}} <a class="link-action" href data-url="{{$.OrgLink}}/members/action/private?uid={{.ID}}">{{$.i18n.Tr "org.members.public_helper"}}</a> {{end}} | |||
{{else}} | |||
{{if or (eq $.SignedUser.ID .ID) $.IsOrganizationOwner}} <a class="link-action" href data-url="{{$.OrgLink}}/members/action/public?uid={{.ID}}">{{$.i18n.Tr "org.members.private_helper"}}</a> {{end}} | |||
{{end}} | |||
</div> | |||
</div> | |||
<div style="padding-left: 0.8em;"> | |||
<div> | |||
<a href="{{.HomeLink}}" class="course_header"> {{.Name}}</a> | |||
<div class="ui small label topic meb_label" > | |||
{{if index $.MembersIsUserOrgOwner .ID}} {{$.i18n.Tr "org.members.owner"}}{{else}}{{$.i18n.Tr "org.members.member"}}{{end}} | |||
</div> | |||
</div> | |||
<div class="meta" style="margin-top: 0.5em;"> | |||
{{.FullName}} | |||
</div> | |||
<div class="meta" style="margin-top: 0.5em;"> | |||
{{svg "octicon-mail" 16}} | |||
<a class="cor" href="mailto:{{.Email}}" rel="nofollow"> {{.Email}}</a> | |||
</div> | |||
</div> | |||
<div class="button_leaveOrg" style="display:none" > | |||
{{if eq $.SignedUser.ID .ID}} | |||
<form method="post" action="{{$.OrgLink}}/members/action/leave"> | |||
{{$.CsrfTokenHtml}} | |||
<button type="submit" class="ui red basic button bt_mr" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.course_members.leave"}}</button> | |||
</form> | |||
{{else if $.IsOrganizationOwner}} | |||
<form method="post" action="{{$.OrgLink}}/members/action/remove"> | |||
{{$.CsrfTokenHtml}} | |||
<button type="submit" class="ui red basic button bt_mr" name="uid" value="{{.ID}}">{{$.i18n.Tr "org.course_members.remove"}}</button> | |||
</form> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
{{template "base/paginate" .}} | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
function show_bt(bt_id){ | |||
console.log(bt_id) | |||
document.getElementById(""+bt_id).getElementsByClassName("button_leaveOrg")[0].style.display="inline-block"; | |||
} | |||
function hide_bt(bt_id){ | |||
document.getElementById(""+bt_id).getElementsByClassName("button_leaveOrg")[0].style.display="none"; | |||
} | |||
</script> |
@@ -0,0 +1,28 @@ | |||
<style> | |||
.navber_course{ | |||
background: #F5F5F6 !important; | |||
padding-top: 30px; | |||
margin-bottom: 20px; | |||
} | |||
</style> | |||
<div class="navber_course"> | |||
<div class="ui tabs container"> | |||
<div class="ui tabular stackable menu navbar"> | |||
{{with .Org}} | |||
<a class="{{if $.PageIsOrgHome}}active{{end}} item " href="{{.HomeLink}}"> | |||
{{svg "octicon-home" 16}} {{$.i18n.Tr "org.home"}} | |||
</a> | |||
{{end}} | |||
<a class="{{if $.PageIsOrgMembers}}active{{end}} item" href="{{$.OrgLink}}/members"> | |||
{{svg "octicon-organization" 16}} {{$.i18n.Tr "org.people"}} | |||
</a> | |||
{{if or ($.IsOrganizationMember) ($.IsOrganizationOwner)}} | |||
<a class="{{if $.PageIsOrgTeams}}active{{end}} item" href="{{$.OrgLink}}/teams"> | |||
{{svg "octicon-jersey" 16}} {{$.i18n.Tr "org.teams"}} | |||
</a> | |||
{{end}} | |||
{{if .IsOrganizationOwner}}<a class="right text grey item" href="{{.OrgLink}}/settings">{{svg "octicon-gear" 16}} {{$.i18n.Tr "org.settings"}}</a>{{end}} | |||
</div> | |||
</div> | |||
</div> |
@@ -33,7 +33,7 @@ | |||
<img class="ui avatar image" src="{{.RelAvatarLink}}"> | |||
{{end}} | |||
<a class="name" href="{{.Link}}"> | |||
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{.Name}} | |||
{{if or $.PageIsExplore $.PageIsProfileStarList }}{{if .Owner}}{{.Owner.Name}} / {{end}}{{end}}{{if .Alias}}{{.Alias}}{{else}}{{.Name}}{{end}} | |||
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} | |||
</a> | |||
{{if .IsPrivate}} | |||
@@ -70,4 +70,4 @@ | |||
{{$.i18n.Tr "explore.repo_no_results"}} | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> |
@@ -70,11 +70,11 @@ | |||
<div style="width: 100%;margin:15px 0;"> | |||
{{if .tags}} | |||
<span class="header"> | |||
精选项目 | |||
{{.i18n.Tr "org.selected_project"}} | |||
</span> | |||
<!-- {{.IsOrganizationOwner}} --> | |||
{{if .IsOrganizationOwner}} | |||
<a class="text-right" id="model" onclick="showcreate()" >{{svg "octicon-gear" 16}}自定义</a> | |||
<a class="text-right" id="model" onclick="showcreate()" >{{svg "octicon-gear" 16}}{{.i18n.Tr "org.customize"}}</a> | |||
{{end}} | |||
{{end}} | |||
@@ -89,7 +89,7 @@ | |||
<div class="extra full_height cor" > | |||
<div class=" header header_card omit" > | |||
<a class="header_card image poping up " href="{{.Link}}" data-content="{{.Name}}" data-position="top left" data-variation="tiny inverted"> {{.Name}}</a> | |||
<a class="header_card image poping up " href="{{.Link}}" data-content="{{if .Alias}}{{.Alias}}{{else}}{{.Name}}{{end}}" data-position="top left" data-variation="tiny inverted">{{if .Alias}}{{.Alias}}{{else}}{{.Name}}{{end}}</a> | |||
</div> | |||
<div class='content descript_height nowrap-2'> | |||
@@ -137,10 +137,10 @@ | |||
<div class="ui modal"> | |||
<div class="header" style="padding: 1rem;background-color: rgba(240, 240, 240, 100);"> | |||
<h4 id="model_header">自定义精选项目</h4> | |||
<h4 id="model_header">{{.i18n.Tr "org.custom_select_projects"}}</h4> | |||
</div> | |||
<div class="content content-padding" style="color: black;"> | |||
<p>最多可选9个公开项目</p> | |||
<p>{{.i18n.Tr "org.max_selectedPro"}}</p> | |||
<div class="ui search" > | |||
<div class="ui input" style="width: 100%;"> | |||
<input type="text" id = 'search_selectPro' placeholder="Search ..." value = '' oninput="search()"> | |||
@@ -167,6 +167,7 @@ | |||
</div> | |||
<script> | |||
console.log({{.tags}}) | |||
var data; | |||
var filterData=[]; | |||
var num=0; | |||
@@ -222,12 +223,12 @@ | |||
function saveSeletedPro(typeTag){ | |||
var saveData=[]; | |||
$('input[name="select_pro_name"]:checked').each(function(){ | |||
console.log('值',this.dataset.repoid) | |||
// console.log('值',this.dataset.repoid) | |||
saveData.push(parseInt(this.dataset.repoid)); | |||
}) | |||
if(saveData.length>9){ | |||
alert("最多可选9个,保存失败") | |||
alert("{{.i18n.Tr "org.save_fail_tips"}}") | |||
return | |||
} | |||
// saveData = getSelecteDataID(); | |||
@@ -241,7 +242,7 @@ | |||
data:JSON.stringify({'repoList':saveData | |||
}), | |||
success:function(res){ | |||
console.log('保存成功'); | |||
// console.log('保存成功'); | |||
location.reload() | |||
} | |||
@@ -269,15 +270,15 @@ | |||
filterData.push(data[i]) | |||
} | |||
} | |||
console.log("选中的值:",selectedData) | |||
console.log("筛选包括选中的值:",filterData) | |||
// console.log("选中的值:",selectedData) | |||
// console.log("筛选包括选中的值:",filterData) | |||
var showData=[]; | |||
for(i=0;i<selectedData.length;i++){ | |||
filterData =filterData.filter((item)=>{ | |||
return item.RepoID!=selectedData[i].RepoID | |||
}); | |||
} | |||
console.log("筛选后不包括选中的值:",filterData) | |||
// console.log("筛选后不包括选中的值:",filterData) | |||
$("#org_list").empty() | |||
if(searchValue!=""){ | |||
if (filterData.length!=0){ | |||
@@ -306,7 +307,7 @@ | |||
num++ | |||
if(num>9){ | |||
document.getElementById(id).checked=false | |||
alert("选择超过9个,请重新选择!") | |||
alert("{{.i18n.Tr "org.select_again"}}") | |||
return | |||
} | |||
} | |||
@@ -314,7 +315,8 @@ | |||
} | |||
var show_num = 9-num; | |||
document.getElementById("recommend").innerHTML="还能推荐"+show_num+"个" | |||
let rec = "{{.i18n.Tr "org.recommend_remain_pro"}}" | |||
document.getElementById("recommend").innerHTML=rec +" : "+ show_num | |||
} | |||
@@ -0,0 +1,56 @@ | |||
<style> | |||
.organization-header{ | |||
margin-bottom: 0px !important; | |||
border-bottom:none !important | |||
} | |||
</style> | |||
{{template "base/head" .}} | |||
<div class="organization teams"> | |||
{{template "org/header_course" .}} | |||
{{template "org/navber_course" .}} | |||
<div class="ui container"> | |||
{{template "base/alert" .}} | |||
<div class="ui stackable grid"> | |||
<div class="ui sixteen wide computer column list"> | |||
<div class="ui two column grid"> | |||
{{range .Teams}} | |||
<div class="column"> | |||
<div class="ui top attached header"> | |||
<a class="text black" href="{{$.OrgLink}}/teams/{{.LowerName}}"><strong>{{.Name}}</strong></a> | |||
<div class="ui right"> | |||
{{if .IsMember $.SignedUser.ID}} | |||
<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName}}/action/leave"> | |||
{{$.CsrfTokenHtml}} | |||
<button type="submit" class="ui red small button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.leave"}}</button> | |||
</form> | |||
{{else if $.IsOrganizationOwner}} | |||
<form method="post" action="{{$.OrgLink}}/teams/{{.LowerName}}/action/join"> | |||
{{$.CsrfTokenHtml}} | |||
<button type="submit" class="ui blue small button" name="uid" value="{{$.SignedUser.ID}}">{{$.i18n.Tr "org.teams.join"}}</button> | |||
</form> | |||
{{end}} | |||
</div> | |||
</div> | |||
<div class="ui attached segment members"> | |||
{{range .Members}} | |||
<a href="{{.HomeLink}}" title="{{.Name}}"> | |||
<img class="ui avatar image" src="{{.RelAvatarLink}}"> | |||
</a> | |||
{{end}} | |||
</div> | |||
<div class="ui bottom attached header"> | |||
<p class="team-meta">{{.NumMembers}} {{$.i18n.Tr "org.lower_members"}} · {{.NumRepos}} {{$.i18n.Tr "org.lower_repositories"}}</p> | |||
</div> | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} |
@@ -50,7 +50,7 @@ | |||
{{else}} | |||
{{svg "octicon-repo" 16}} | |||
{{end}} | |||
<strong>{{$.Org.Name}}/{{.Name}}</strong> | |||
<strong>{{$.Org.Name}}/{{.Alias}}</strong> | |||
</a> | |||
</div> | |||
{{else}} | |||
@@ -150,7 +150,7 @@ | |||
</form> | |||
<a class="ui basic blue button" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/rate" target="_blank"> | |||
<a class="ui basic button {{if $.IsSigned}} blue{{else}} disabled{{end}}" href="{{$.RepoLink}}/cloudbrain/{{.JobID}}/rate" target="_blank"> | |||
评分 | |||
</a> | |||
@@ -116,7 +116,7 @@ | |||
<div class="required unite min_title inline field"> | |||
<label style="font-weight: normal;">{{.i18n.Tr "repo.cloudbrain.benchmark.evaluate_mirror"}}</label> | |||
<span> </span> | |||
<input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image" class="required autofocus" style='width:492px;' maxlength="254"> | |||
<input type="text" list="cloudbrain_image" placeholder="选择镜像" name="image" value="{{.image}}" class="required autofocus" style='width:492px;' maxlength="254"> | |||
<i class="times circle outline icon icons" style="visibility: hidden;" onclick="clearValue()"></i> | |||
<datalist class="ui search" id="cloudbrain_image" style='width:385px;' name="image"> | |||
{{range .images}} | |||
@@ -205,16 +205,6 @@ | |||
on: 'blur', | |||
inline:true, | |||
fields: { | |||
job_name:{ | |||
identifier : 'job_name', | |||
rules: [ | |||
{ | |||
type: 'regExp[/^[a-zA-Z0-9-_]{1,36}$/]', | |||
prompt : '只包含大小写字母、数字、_和-,最长36个字符。' | |||
} | |||
] | |||
}, | |||
image:{ | |||
identifier : 'image', | |||
rules: [ | |||
@@ -277,7 +277,17 @@ td, th { | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
类型 | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" id="{{.VersionName}}-BenchmarkTypeName"> | |||
{{$.BenchmarkTypeName}} | |||
</div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
@@ -346,6 +356,17 @@ td, th { | |||
</div> | |||
</td> | |||
</tr> | |||
<tr class="ti-no-ng-animate"> | |||
<td class="ti-no-ng-animate ti-text-form-label text-width80"> | |||
子类型 | |||
</td> | |||
<td class="ti-text-form-content"> | |||
<div class="text-span text-span-w" id="{{.VersionName}}-BenchmarkChildTypeName"> | |||
{{$.BenchmarkChildTypeName}} | |||
</div> | |||
</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
</div> | |||
@@ -0,0 +1,295 @@ | |||
{{template "base/head" .}} | |||
<style> | |||
.repository.file.list #repo-desc { | |||
font-size: 1.0em; | |||
margin-bottom: 1.0rem; | |||
} | |||
#contributorInfo > a:nth-child(n+26){ | |||
display:none; | |||
} | |||
#contributorInfo > a{ | |||
width: 2.0em; | |||
float: left; | |||
margin: .25em; | |||
} | |||
.edit-link{ | |||
vertical-align: top; | |||
display: inline-block; | |||
overflow: hidden; | |||
word-break: keep-all; | |||
white-space: nowrap; | |||
text-overflow: ellipsis; | |||
width: 16.5em; | |||
} | |||
#contributorInfo > a.circular{ | |||
height: 2.0em; | |||
padding: 0; | |||
overflow: hidden; | |||
letter-spacing:1.0em; | |||
text-indent: 0.6em; | |||
line-height: 2.0em; | |||
text-transform:capitalize; | |||
color: #FFF; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+1){ | |||
background-color: #4ccdec; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+2){ | |||
background-color: #e0b265; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+3){ | |||
background-color: #d884b7; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+4){ | |||
background-color: #8c6bdc; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+5){ | |||
background-color: #3cb99f; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+6){ | |||
background-color: #6995b9; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+7){ | |||
background-color: #ab91a7; | |||
} | |||
#contributorInfo > a.circular:nth-child(9n+8){ | |||
background-color: #bfd0aa; | |||
} | |||
.vue_menu { | |||
cursor: auto; | |||
position: absolute; | |||
outline: none; | |||
margin: 0em; | |||
padding: 0em 0em; | |||
background: #fff; | |||
font-size: 1em; | |||
text-shadow: none; | |||
text-align: left; | |||
/* -webkit-box-shadow: 0px 2px 3px 0px rgb(34 36 38 / 15%); */ | |||
box-shadow: 0px 2px 3px 0px rgba(34, 36, 38, 0.15); | |||
border: 1px solid rgba(34,36,38,0.15); | |||
border-radius: 0.28571429rem; | |||
-webkit-transition: opacity 0.1s ease; | |||
transition: opacity 0.1s ease; | |||
z-index: 11; | |||
will-change: transform, opacity; | |||
-webkit-animation-iteration-count: 1; | |||
animation-iteration-count: 1; | |||
-webkit-animation-duration: 300ms; | |||
animation-duration: 300ms; | |||
-webkit-animation-timing-function: ease; | |||
animation-timing-function: ease; | |||
-webkit-animation-fill-mode: both; | |||
animation-fill-mode: both; | |||
} | |||
.repo-topic{ | |||
background-color: rgba(179, 219, 219, 0.4) !important; | |||
color: #0366D6 !important; | |||
font-weight: 200 !important; | |||
} | |||
</style> | |||
<div class="repository file list"> | |||
{{template "repo/header" .}} | |||
<div class="ui container"> | |||
{{template "base/alert" .}} | |||
<div class="hide" id="validate_prompt"> | |||
<span id="count_prompt">{{.i18n.Tr "repo.topic.count_prompt"}}</span> | |||
<span id="format_prompt">{{.i18n.Tr "repo.topic.format_prompt"}}</span> | |||
</div> | |||
{{if .Repository.IsArchived}} | |||
<div class="ui warning message"> | |||
{{.i18n.Tr "repo.archive.title"}} | |||
</div> | |||
{{end}} | |||
<div> | |||
<span>{{.i18n.Tr "repo.computing.Introduction"}}:</span> | |||
{{if .Repository.DescriptionHTML}} | |||
<span class="description" style="color: #8a8e99;">{{.Repository.DescriptionHTML}}</span> | |||
{{else}} | |||
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||
{{end}} | |||
<!-- <span style="color: #8a8e99;">生课程的教学,在全国范围形成一批开放共享的教学材料。这类材料的风格、设想既不同于教材,也不同于IEEE CSs中对知识体系的描述,而是以一定的知识内容为背景,重在教师个人在教学实践中的心得,包括对某些内容独到的理解和课堂上的处理,等等。</span> --> | |||
</div> | |||
<div class="ui" id="repo-topics"> | |||
<div id="repo-topics1" style="display: inline-block;margin: 0.5rem 0;"> | |||
{{range .Topics}} | |||
<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a> | |||
{{end}} | |||
</div> | |||
<a style="margin-left: 0.5rem;" id="manage_topic"> | |||
{{if and .Permission.IsAdmin (not .Repository.IsArchived)}}<i style="cursor: pointer;" class="plus square outline icon"></i>{{end}} | |||
{{.i18n.Tr "repo.issues.new.add_labels_title"}} | |||
</a> | |||
<div id="topic_edit" class="vue_menu" style="display:none;"> | |||
<div id="topic_edit1"> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui mobile reversed stackable grid" style="margin-top: -1.5rem;"> | |||
{{ $n := len .TreeNames}} | |||
{{ $l := Subtract $n 1}} | |||
<!-- If home page, show new PR. If not, show breadcrumb --> | |||
<div class="ui ten wide tablet twelve wide computer column text right" style="margin-top: 1rem;"> | |||
<div class="right fitted item" id="file-buttons"> | |||
<div class="ui tiny blue buttons"> | |||
{{if .Repository.CanEnableEditor}} | |||
{{if .CanAddFile}} | |||
<a href="{{.RepoLink}}/_new/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button"> | |||
{{.i18n.Tr "repo.editor.new_file"}} | |||
</a> | |||
{{end}} | |||
{{if .CanUploadFile}} | |||
<a href="{{.RepoLink}}/_upload/{{EscapePound .BranchName}}/{{EscapePound .TreePath}}" class="ui button"> | |||
{{.i18n.Tr "repo.editor.upload_file"}} | |||
</a> | |||
{{end}} | |||
{{end}} | |||
{{if and (ne $n 0) (not .IsViewFile) (not .IsBlame) }} | |||
<a href="{{.RepoLink}}/commits/{{EscapePound .BranchNameSubURL}}/{{EscapePound .TreePath}}" class="ui button"> | |||
{{.i18n.Tr "repo.file_history"}} | |||
</a> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui container"> | |||
<div class="ui mobile reversed stackable grid"> | |||
<div class="ui ten wide tablet twelve wide computer column"> | |||
{{if .IsViewFile}} | |||
{{template "repo/view_file" .}} | |||
{{else if .IsBlame}} | |||
{{template "repo/blame" .}} | |||
{{else}} | |||
<table id="repo-files-table" class="ui single line table"> | |||
<thead> | |||
<tr class="commit-list"> | |||
<th colspan="2"> | |||
{{if .LatestCommitUser}} | |||
<img class="ui avatar image img-12" src="{{.LatestCommitUser.RelAvatarLink}}" /> | |||
{{if .LatestCommitUser.FullName}} | |||
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{.LatestCommitUser.FullName}}</strong></a> | |||
{{else}} | |||
<a href="{{AppSubUrl}}/{{.LatestCommitUser.Name}}"><strong>{{if .LatestCommit.Author}}{{.LatestCommit.Author.Name}}{{else}}{{.LatestCommitUser.Name}}{{end}}</strong></a> | |||
{{end}} | |||
{{else}} | |||
{{if .LatestCommit.Author}} | |||
<img class="ui avatar image img-12" src="{{AvatarLink .LatestCommit.Author.Email}}" /> | |||
<strong>{{.LatestCommit.Author.Name}}</strong> | |||
{{end}} | |||
{{end}} | |||
{{if .LatestCommit.Author}} | |||
<span style="margin: 0 0.5rem;color: #767676">{{TimeSince .LatestCommit.Author.When $.Lang}}</span> | |||
{{end}} | |||
{{ $commitLink:= printf "%s/commit/%s" .RepoLink .LatestCommit.ID }} | |||
<span class="grey commit-summary" title="{{.LatestCommit.Summary}}"><span class="message-wrapper">{{RenderCommitMessageLinkSubject .LatestCommit.Message $.RepoLink $commitLink $.Repository.ComposeMetas}}</span> | |||
{{if IsMultilineCommitMessage .LatestCommit.Message}} | |||
<button class="basic compact mini ui icon button commit-button"><i class="ellipsis horizontal icon"></i></button> | |||
<pre class="commit-body" style="display: none;">{{RenderCommitBody .LatestCommit.Message $.RepoLink $.Repository.ComposeMetas}}</pre> | |||
{{end}} | |||
</span> | |||
</th> | |||
</tr> | |||
</thead> | |||
<tbody> | |||
{{if .HasParentPath}} | |||
<tr class="has-parent"> | |||
<td colspan="3">{{svg "octicon-mail-reply" 16}}<a href="{{EscapePound .BranchLink}}{{.ParentPath}}">..</a></td> | |||
</tr> | |||
{{end}} | |||
{{range $item := .Files}} | |||
{{$entry := index $item 0}} | |||
{{$commit := index $item 1}} | |||
<tr> | |||
{{if $entry.IsSubModule}} | |||
<td> | |||
<span class="truncate"> | |||
{{svg "octicon-inbox" 16}} | |||
{{$refURL := $commit.RefURL AppUrl $.Repository.FullName}} | |||
{{if $refURL}} | |||
<a href="{{$refURL}}">{{$entry.Name}}</a> @ <a href="{{$refURL}}/commit/{{$commit.RefID}}">{{ShortSha $commit.RefID}}</a> | |||
{{else}} | |||
{{$entry.Name}} @ {{ShortSha $commit.RefID}} | |||
{{end}} | |||
</span> | |||
</td> | |||
{{else}} | |||
<td class="name thirteen wide"> | |||
<span class="truncate"> | |||
{{if $entry.IsDir}} | |||
{{$subJumpablePathName := $entry.GetSubJumpablePathName}} | |||
{{$subJumpablePath := SubJumpablePath $subJumpablePathName}} | |||
{{svg "octicon-file-directory" 16}} | |||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $subJumpablePathName}}" title="{{$subJumpablePathName}}"> | |||
{{if eq (len $subJumpablePath) 2}} | |||
<span class="jumpable-path">{{index $subJumpablePath 0}}</span>{{index $subJumpablePath 1}} | |||
{{else}} | |||
{{index $subJumpablePath 0}} | |||
{{end}} | |||
</a> | |||
{{else}} | |||
<i class="ri-file-pdf-line" style="font-size: 16px;margin-left: 3px;margin-right: 5px;vertical-align: text-top;color: #FA8C16;"></i> | |||
<a href="{{EscapePound $.TreeLink}}/{{EscapePound $entry.Name}}" title="{{$entry.Name}}">{{$entry.Name}}</a> | |||
{{end}} | |||
</span> | |||
</td> | |||
{{end}} | |||
<td class="text right age one wide" style="text-align: right;">{{TimeSince $commit.Committer.When $.Lang}}</td> | |||
</tr> | |||
{{end}} | |||
</tbody> | |||
</table> | |||
{{if .ReadmeExist}} | |||
{{template "repo/view_file" .}} | |||
{{end}} | |||
{{end}} | |||
</div> | |||
<!-- 贡献者框 --> | |||
<div class="ui six wide tablet four wide computer column"> | |||
<div style="border-radius: 5px;border: 1px solid rgba(225, 227, 230, 100);padding: 1rem;"> | |||
<h4 class="ui header" style="border-bottom: 1px solid rgba(225, 227, 230, 100);padding: 0.5rem 0;"> | |||
{{$lenCon := len .ContributorInfo}} | |||
{{if lt $lenCon 25 }} | |||
<strong>{{.i18n.Tr "home.contributors"}} ({{len .ContributorInfo}})</strong> | |||
{{else}} | |||
<strong>{{.i18n.Tr "home.contributors"}} ({{len .ContributorInfo}}+)</strong> | |||
{{end}} | |||
<div class="ui right"> | |||
<a class="membersmore text grey" href="{{.RepoLink}}/contributors?type={{if .IsViewBranch}}branch{{else}}tag{{end}}&name={{.BranchName}}">{{.i18n.Tr "repo.computing.all"}} {{svg "octicon-chevron-right" 16}}</a> | |||
</div> | |||
</h4> | |||
<div class="ui members" id="contributorInfo"> | |||
{{range .ContributorInfo}} | |||
{{if .UserInfo}} | |||
<a href="{{AppSubUrl}}/{{.UserInfo.Name}}"><img class="ui avatar image" src="{{.UserInfo.RelAvatarLink}}"></a> | |||
{{else if .Email}} | |||
<a href="mailto:{{.Email}}" class="circular ui button">{{.Email}}</a> | |||
{{end}} | |||
{{end}} | |||
</div> | |||
<div style="clear: both;"></div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} |
@@ -2,43 +2,15 @@ | |||
<div class="repository new repo" style="margin-top: 40px;"> | |||
<div class="ui middle very relaxed page grid"> | |||
<div class="column"> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form"> | |||
{{.CsrfTokenHtml}} | |||
<h3 class="ui top attached header"> | |||
{{.i18n.Tr "new_repo"}} | |||
</h3> | |||
<div class="ui attached segment"> | |||
{{template "base/alert" .}} | |||
<div class="inline required field {{if .Err_Owner}}error{{end}}"> | |||
<label>{{.i18n.Tr "repo.owner"}}</label> | |||
<div class="ui selection owner dropdown"> | |||
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> | |||
<span class="text" title="{{.ContextUser.Name}}"> | |||
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> | |||
{{.ContextUser.ShortName 20}} | |||
</span> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> | |||
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> | |||
{{.SignedUser.ShortName 20}} | |||
</div> | |||
{{range .Orgs}} | |||
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> | |||
<img class="ui mini image" src="{{.RelAvatarLink}}"> | |||
{{.ShortName 20}} | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
<span class="help">{{.i18n.Tr "repo.repo_owner_helper"}}</span> | |||
</div> | |||
<div class="inline required field {{if .Err_RepoName}}error{{end}}"> | |||
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> | |||
<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required> | |||
<span class="help"></span> | |||
</div> | |||
{{template "repo/repo_name" .}} | |||
<!-- <div class="js-project-full-path" id="repoAdress" ></div> --> | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.visibility"}}</label> | |||
<div class="ui checkbox"> | |||
@@ -172,7 +144,7 @@ | |||
<br/> | |||
<div class="inline field"> | |||
<label></label> | |||
<button class="ui green button"> | |||
<button class="ui green button" id="submit_reponame"> | |||
{{.i18n.Tr "repo.create_repo"}} | |||
</button> | |||
<a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a> | |||
@@ -0,0 +1,111 @@ | |||
<style> | |||
#course_label::after{ | |||
display: inline-block; | |||
vertical-align: top; | |||
margin: -.2em 0 0 .2em; | |||
content: '*'; | |||
color: #db2828; | |||
} | |||
</style> | |||
{{template "base/head" .}} | |||
<div class="repository new repo" style="margin-top: 40px;"> | |||
<div class="ui middle very relaxed page grid"> | |||
<div class="column"> | |||
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form"> | |||
{{.CsrfTokenHtml}} | |||
<h3 class="ui top attached header"> | |||
{{.i18n.Tr "new_course"}} | |||
</h3> | |||
<div class="ui attached segment"> | |||
{{template "base/alert" .}} | |||
<div class="inline required field {{if .Err_RepoName}}error{{end}}" > | |||
<label for="Alias">{{.i18n.Tr "form.courseAlias"}}</label> | |||
<input id="alias" name="alias" value="{{.alias}}" autofocus required> | |||
<span class="help">{{.i18n.Tr "form.reponame_dash_dot_error"}}</span> | |||
</div> | |||
<div class="inline required fields" style="margin-bottom: 0;"> | |||
<label id="course_label" style="text-align: right;width: 250px!important;word-wrap: break-word;">{{.i18n.Tr "form.courseAdress"}}</label> | |||
<div class="required field {{if .Err_Owner}}error{{end}}" style="padding: 0;"> | |||
<div class="ui selection owner dropdown" id="ownerDropdown"> | |||
<input type="hidden" id="uid" name="uid" value="{{.Owner.Name}}" required> | |||
<div class="text" title="{{.Owner.Name}}"> | |||
<img class="ui mini image" src="{{.Owner.RelAvatarLink}}"> | |||
{{$.Owner.Name}} | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui interval" style="width: 0.6em;font-size: 2rem;line-height: 0px;text-align: center;">/</div> | |||
<div class="required field {{if .Err_RepoName}}error{{end}}"> | |||
<input style="width: 100% !important;" id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required> | |||
</div> | |||
</div> | |||
<span style="display: block;margin-bottom: 1em;" class="help">{{.i18n.Tr "form.repoadd_dash_dot_error"}}</span> | |||
<div class="inline field" id="repoAdress" style="display: none;word-break: break-all;"> | |||
<label for="">{{.i18n.Tr "form.course_Adress"}}:</label> | |||
<span style="flex: 1;"></span> | |||
</div> | |||
<div class="inline field" style="display: flex;align-items:center;"> | |||
<label style="margin: .035714em 1em 0 0;">{{.i18n.Tr "repo.model.manage.label"}}</label> | |||
<div class="ui multiple search selection dropdown" id="dropdown_container"> | |||
<input type="hidden" name="topics" value=""> | |||
<div class="default text" id="default_text">{{.i18n.Tr "repo.repo_label_helpe"}}</div> | |||
<div class="menu" id="course_label_item"> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline field {{if .Err_Description}}error{{end}}"> | |||
<label for="description">{{.i18n.Tr "course_desc"}}</label> | |||
<textarea id="description" name="description" maxlength="254">{{.description}}</textarea> | |||
</div> | |||
<div class="inline field"> | |||
<label></label> | |||
<button class="ui green button" id="submit_reponame"> | |||
{{.i18n.Tr "new_course"}} | |||
</button> | |||
<a class="ui button" href="javascript:history.go(-1)">{{.i18n.Tr "cancel"}}</a> | |||
</div> | |||
</div> | |||
</form> | |||
</div> | |||
</div> | |||
</div> | |||
{{template "base/footer" .}} | |||
<script> | |||
$(document).ready(function(){ | |||
$('.ui.multiple.search.selection.dropdown') | |||
.dropdown({ | |||
allowAdditions: true, | |||
onChange: function(value, text, $selectedItem) { | |||
$('#course_label_item').empty() | |||
} | |||
}) | |||
let defaultText = document.getElementById("default_text").offsetHeight | |||
defaultText = defaultText>40 ? defaultText+12 :defaultText | |||
$("#dropdown_container").css("height",defaultText) | |||
$('input.search').bind('input propertychange', function (event) { | |||
$("#dropdown_container").removeAttr("style"); | |||
const query = $('input.search').val() | |||
if(!query){ | |||
$('#course_label_item').empty() | |||
}else{ | |||
$.get(`/api/v1/topics/search?q=${query}`,(data)=>{ | |||
console.log(data) | |||
if(data.topics.length!==0){ | |||
let html='' | |||
$('#course_label_item').empty() | |||
data.topics.forEach(element => { | |||
html += `<div class="item" data-value="${element.topic_name}">${element.topic_name}</div>` | |||
}); | |||
$('#course_label_item').append(html) | |||
} | |||
}) | |||
} | |||
}); | |||
}) | |||
console.log() | |||
</script> |
@@ -24,7 +24,7 @@ | |||
{{end}} | |||
<a href="{{AppSubUrl}}/{{.Owner.Name}}">{{.Owner.Name}}</a> | |||
<div class="divider"> / </div> | |||
<a href="{{$.RepoLink}}">{{.Name}}</a> | |||
<a href="{{$.RepoLink}}">{{.DisplayName}}</a> | |||
{{if .RelAvatarLink}} | |||
{{if .IsTemplate}} | |||
{{if .IsPrivate}} | |||
@@ -46,7 +46,7 @@ | |||
{{end}} | |||
{{if .IsArchived}}<i class="archive icon archived-icon"></i>{{end}} | |||
{{if .IsMirror}}<div class="fork-flag">{{$.i18n.Tr "repo.mirror_from"}} <a target="_blank" rel="noopener noreferrer" href="{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}">{{if .SanitizedOriginalURL}}{{.SanitizedOriginalURL}}{{else}}{{MirrorAddress $.Mirror}}{{end}}</a></div>{{end}} | |||
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{SubStr .BaseRepo.RelLink 1 -1}}</a></div>{{end}} | |||
{{if .IsFork}}<div class="fork-flag">{{$.i18n.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullDisplayName}}</a></div>{{end}} | |||
{{if .IsGenerated}}<div class="fork-flag">{{$.i18n.Tr "repo.generated_from"}} <a href="{{.TemplateRepo.Link}}">{{SubStr .TemplateRepo.RelLink 1 -1}}</a></div>{{end}} | |||
</div> | |||
{{if not .IsBeingCreated}} | |||
@@ -114,9 +114,9 @@ | |||
{{end}} | |||
</div> | |||
</div> | |||
{{end}} | |||
{{if .Permission.CanRead $.UnitTypeIssues}} | |||
@@ -152,7 +152,7 @@ | |||
{{if .Permission.CanRead $.UnitTypeCloudBrain}} | |||
<a class="{{if .PageIsCloudBrain}}active{{end}} item" href="{{.RepoLink}}/debugjob?debugListType=all"> | |||
<span> | |||
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"/></svg> | |||
<svg class="svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="16" height="16"><path fill="none" d="M0 0h24v24H0z"/><path d="M4 3h16a1 1 0 0 1 1 1v7H3V4a1 1 0 0 1 1-1zM3 13h18v7a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-7zm4 3v2h3v-2H7zM7 6v2h3V6H7z"/></svg> | |||
{{.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> | |||
@@ -256,11 +256,8 @@ | |||
</div> | |||
<div class="ui six wide tablet four wide computer column"> | |||
<div id="repo-desc"> | |||
<h4 id="about-desc" class="ui header">简介 | |||
<!-- <a class="edit-icon" href="javascript:void(0)"> | |||
<i class="gray edit outline icon"></i> | |||
</a> --> | |||
</h4> | |||
<h4 id="about-desc" class="ui header">简介</h4> | |||
<input type="hidden" id="edit-alias" value="{{.Repository.Alias}}"> | |||
<p> | |||
{{if .Repository.DescriptionHTML}} | |||
<span class="description" style="word-break:break-all">{{.Repository.DescriptionHTML}}</span> | |||
@@ -286,14 +283,8 @@ | |||
<i class="grey bookmark icon"></i> | |||
<div id="repo-topics1" style="flex: 1;"> | |||
<!-- {{if not .Topics}} | |||
<span class="no-description text-italic">{{.i18n.Tr "repo.no_desc"}}</span> | |||
{{end}} --> | |||
{{range .Topics}} | |||
<a class="ui repo-topic small label topic" href="{{AppSubUrl}}/explore/repos?q={{.Name}}&topic=">{{.Name}}</a> | |||
{{end}} | |||
</div> | |||
<div> | |||
@@ -2,7 +2,7 @@ | |||
<div class="repository new migrate" style="margin-top: 40px;"> | |||
<div class="ui middle very relaxed page grid"> | |||
<div class="column"> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form"> | |||
{{.CsrfTokenHtml}} | |||
<h3 class="ui top attached header"> | |||
{{.i18n.Tr "new_migrate"}} | |||
@@ -37,35 +37,7 @@ | |||
</div> | |||
<div class="ui divider"></div> | |||
<div class="inline required field {{if .Err_Owner}}error{{end}}"> | |||
<label>{{.i18n.Tr "repo.owner"}}</label> | |||
<div class="ui selection owner dropdown"> | |||
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> | |||
<span class="text" title="{{.ContextUser.Name}}"> | |||
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> | |||
{{.ContextUser.ShortName 20}} | |||
</span> | |||
<i class="dropdown icon"></i> | |||
<div class="menu" title="{{.SignedUser.Name}}"> | |||
<div class="item" data-value="{{.SignedUser.ID}}"> | |||
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> | |||
{{.SignedUser.ShortName 20}} | |||
</div> | |||
{{range .Orgs}} | |||
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> | |||
<img class="ui mini image" src="{{.RelAvatarLink}}"> | |||
{{.ShortName 20}} | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline required field {{if .Err_RepoName}}error{{end}}"> | |||
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> | |||
<input id="repo_name" name="repo_name" value="{{.repo_name}}" required> | |||
</div> | |||
{{template "repo/repo_name" .}} | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.visibility"}}</label> | |||
<div class="ui checkbox"> | |||
@@ -127,7 +99,7 @@ | |||
<div class="inline field"> | |||
<label></label> | |||
<button class="ui green button"> | |||
<button class="ui green button" id="submit_reponame"> | |||
{{.i18n.Tr "repo.migrate_repo"}} | |||
</button> | |||
<a class="ui button" href="{{AppSubUrl}}/">{{.i18n.Tr "cancel"}}</a> | |||
@@ -51,7 +51,7 @@ | |||
<input name="job_name" id="cloudbrain_job_name" placeholder="任务名称" value="{{.job_name}}" tabindex="3" autofocus required maxlength="255" onkeyup="this.value=this.value.replace(/[, ]/g,'')"> | |||
</div> | |||
<div class="inline field"> | |||
<!-- <div class="inline field"> | |||
<label>数据集</label> | |||
<input type="text" list="cloudbrain_dataset" placeholder="选择数据集" name="" id="answerInput" autofocus maxlength="36"> | |||
<datalist id="cloudbrain_dataset" class="ui search" style='width:385px' name="attachment"> | |||
@@ -69,7 +69,7 @@ | |||
<div class="inline required field"> | |||
<label>类型</label> | |||
<input name="job_type" id="cloudbrain_job_type" value="{{.notebook_type}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
</div> | |||
</div> --> | |||
<div class="inline required field"> | |||
<label>规格</label> | |||
<select id="cloudbrain_flavor" class="ui search dropdown" placeholder="选择规格" style='width:385px' name="flavor"> | |||
@@ -79,10 +79,10 @@ | |||
{{end}} | |||
</select> | |||
</div> | |||
<div class="inline required field"> | |||
<!--<div class="inline required field"> | |||
<label>数据集存放路径</label> | |||
<input name="dataset_path" id="cloudbrain_dataset_path" value="{{.dataset_path}}" tabindex="3" disabled autofocus required maxlength="255" readonly="readonly"> | |||
</div> | |||
</div> --> | |||
<div class="inline field"> | |||
<label>描述</label> | |||
<input name="description" id="cloudbrain_description" tabindex="3" autofocus maxlength="255"> | |||
@@ -47,85 +47,7 @@ | |||
</table> | |||
{{end}} | |||
</div> | |||
<div class="ui blue segment"> | |||
{{with .result}} | |||
<table class="ui celled striped table"> | |||
<thead> | |||
<tr> <th colspan="2"> 配置信息 </th> </tr> | |||
</thead> | |||
<tbody> | |||
<tr> | |||
<td class="four wide"> 开发环境类型 </td> | |||
<td>{{.Profile.DeType}}</td> | |||
</tr> | |||
<tr> | |||
<td> 硬件类型 </td> | |||
<td>{{.Profile.FlavorType}}</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<table class="ui celled striped table"> | |||
<thead> | |||
<tr> <th colspan="2"> 机器规格详情 </th> </tr> | |||
</thead> | |||
<tbody> | |||
<tr> | |||
<td class="four wide"> 机器规格 </td> | |||
<td> {{.Flavor}} </td> | |||
</tr> | |||
<tr> | |||
<td> 规格名称 </td> | |||
<td>{{.FlavorDetails.Name}}</td> | |||
</tr> | |||
<tr> | |||
<td> 规格销售状态 </td> | |||
<td>{{.FlavorDetails.Status}}</td> | |||
</tr> | |||
<tr> | |||
<td> 排队个数 </td> | |||
<td>{{.FlavorDetails.QueuingNum}}</td> | |||
</tr> | |||
<tr> | |||
<td> 排到队的剩余时间(秒) </td> | |||
<td>{{.FlavorDetails.QueueLeftTime}}</td> | |||
</tr> | |||
<tr> | |||
<td> 自动停止时间(秒) </td> | |||
<td>{{.FlavorDetails.Duration}}</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
<table class="ui celled striped table" {{if eq .QueuingInfo.RemainTime 0}}hidden{{end}}> | |||
<thead> | |||
<tr> <th colspan="2"> 排队信息 </th> </tr> | |||
</thead> | |||
<tbody> | |||
<tr> | |||
<td> 实例状态 </td> | |||
<td>{{.QueuingInfo.Status}}</td> | |||
</tr> | |||
<tr> | |||
<td> 实例排队的开始时间 </td> | |||
<td>{{.QueuingInfo.BeginTime}}</td> | |||
</tr> | |||
<tr> | |||
<td> 排到队的剩余时间(秒) </td> | |||
<td>{{.QueuingInfo.RemainTime}}</td> | |||
</tr> | |||
<tr> | |||
<td> 实例排队的预计停止时间 </td> | |||
<td>{{.QueuingInfo.EndTime}}</td> | |||
</tr> | |||
<tr> | |||
<td> 实例在队列中的排位 </td> | |||
<td>{{.QueuingInfo.Rank}}</td> | |||
</tr> | |||
</tbody> | |||
</table> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
@@ -2,47 +2,18 @@ | |||
<div class="repository new fork"> | |||
<div class="ui middle very relaxed page grid"> | |||
<div class="column"> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form"> | |||
{{.CsrfTokenHtml}} | |||
<h3 class="ui top attached header"> | |||
{{.i18n.Tr "new_fork"}} | |||
</h3> | |||
<div class="ui attached segment"> | |||
{{template "base/alert" .}} | |||
<div class="inline required field {{if .Err_Owner}}error{{end}}"> | |||
<label>{{.i18n.Tr "repo.owner"}}</label> | |||
<div class="ui selection owner dropdown"> | |||
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> | |||
<span class="text" title="{{.ContextUser.Name}}"> | |||
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> | |||
{{.ContextUser.ShortName 20}} | |||
</span> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
{{if .CanForkToUser}} | |||
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> | |||
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> | |||
{{.SignedUser.ShortName 20}} | |||
</div> | |||
{{end}} | |||
{{range .Orgs}} | |||
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> | |||
<img class="ui mini image" src="{{.RelAvatarLink}}"> | |||
{{.ShortName 20}} | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.fork_from"}}</label> | |||
<a href="{{AppSubUrl}}/{{.ForkFrom}}">{{.ForkFrom}}</a> | |||
</div> | |||
<div class="inline required field {{if .Err_RepoName}}error{{end}}"> | |||
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> | |||
<input id="repo_name" name="repo_name" value="{{.repo_name}}" required> | |||
<a href="{{AppSubUrl}}/{{.ForkFrom}}">{{.ForkDisplayName}}</a> | |||
</div> | |||
{{template "repo/repo_name" .}} | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.visibility"}}</label> | |||
<div class="ui read-only checkbox"> | |||
@@ -0,0 +1,57 @@ | |||
<div class="inline required field {{if .Err_RepoName}}error{{end}}" > | |||
<label for="Alias">{{.i18n.Tr "form.Alias"}}</label> | |||
<input id="alias" name="alias" value="{{.alias}}" autofocus required maxlength="100"> | |||
<span class="help">{{.i18n.Tr "form.reponame_dash_dot_error"}}</span> | |||
</div> | |||
<div class="inline required fields" style="margin-bottom: 0;"> | |||
<label style="text-align: right;width: 250px!important;word-wrap: break-word;">{{.i18n.Tr "form.RepoPath"}}</label> | |||
<div class="required field {{if .Err_Owner}}error{{end}}" style="padding: 0;"> | |||
<!-- <label>{{.i18n.Tr "repo.owner"}}</label> --> | |||
<div class="ui selection owner dropdown" id="ownerDropdown"> | |||
<input type="hidden" id="uid" name="uid" value="{{.ContextUser.ID}}" required> | |||
<div class="text" title="{{.ContextUser.Name}}"> | |||
<img class="ui mini image" src="{{.ContextUser.RelAvatarLink}}"> | |||
{{.ContextUser.ShortName 20}} | |||
</div> | |||
<i class="dropdown icon"></i> | |||
<div class="menu"> | |||
<div class="item" data-value="{{.SignedUser.ID}}" title="{{.SignedUser.Name}}"> | |||
<img class="ui mini image" src="{{.SignedUser.RelAvatarLink}}"> | |||
{{.SignedUser.ShortName 20}} | |||
</div> | |||
{{range .Orgs}} | |||
<div class="item" data-value="{{.ID}}" title="{{.Name}}"> | |||
<img class="ui mini image" src="{{.RelAvatarLink}}"> | |||
{{.ShortName 20}} | |||
</div> | |||
{{end}} | |||
</div> | |||
</div> | |||
<!-- <span class="help">{{.i18n.Tr "repo.repo_owner_helper"}}</span> --> | |||
</div> | |||
<!-- <div class="required field {{if .Err_RepoName}}error{{end}}"> | |||
<input id="repo_name" name="repo_name" value="{{.repo_name}}" autofocus required> | |||
</div> --> | |||
<div class="ui interval" style="width: 0.6em;font-size: 2rem;line-height: 0px;text-align: center;">/</div> | |||
<div class="required field {{if .Err_RepoName}}error{{end}}"> | |||
<!-- <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> --> | |||
<input style="width: 100% !important;" id="repo_name" name="repo_name" value="{{.repo_name}}" maxlength="100" autofocus required> | |||
</div> | |||
</div> | |||
<span style="display: block;margin-bottom: 1em;" class="help">{{.i18n.Tr "form.repoadd_dash_dot_error"}}</span> | |||
<div class="inline field" id="repoAdress" style="display: none;word-break: break-all;"> | |||
<label for="">{{.i18n.Tr "form.RepoAdress"}}:</label> | |||
<span style="flex: 1;"></span> | |||
</div> | |||
<script> | |||
console.log({{$.Err_Alias}}) | |||
</script> |
@@ -8,12 +8,46 @@ | |||
{{.i18n.Tr "repo.settings.basic_settings"}} | |||
</h4> | |||
<div class="ui attached segment"> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
<form class="ui form" action="{{.Link}}" method="post" id="create_repo_form"> | |||
{{.CsrfTokenHtml}} | |||
<input type="hidden" name="action" value="update"> | |||
<div class="required field {{if .Err_RepoName}}error{{end}}"> | |||
<!-- <div class="required field {{if .Err_RepoName}}error{{end}}"> | |||
<label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> | |||
<input id="repo_name" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required> | |||
<input id="repo_name" name="alias" value="{{.Repository.Alias}}" data-repo-name="{{.Repository.Alias}}" autofocus required> | |||
<input type="hidden" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required> | |||
</div> --> | |||
<div class="required field {{if .Err_Alias}}error{{end}}"> | |||
<label for="Alias">{{.i18n.Tr "form.Alias"}}</label> | |||
<input name="alias" value="{{.Repository.Alias}}" data-repo-name="{{.Repository.Alias}}" autofocus required> | |||
<span class="help">{{.i18n.Tr "form.reponame_dash_dot_error"}}</span> | |||
</div> | |||
<div class="required field"> | |||
<label>{{.i18n.Tr "form.RepoPath"}}</label> | |||
<div class="fields"> | |||
<div class="eight wide required field {{if .Err_Owner}}error{{end}}"> | |||
<div class="ui selection owner dropdown" id="ownerDropdown"> | |||
<input type="hidden" id="uid" name="uid" value="{{.Owner.Name}}" required> | |||
<div class="text" title="{{.Owner.Name}}"> | |||
<img class="ui mini image" src="{{.Owner.RelAvatarLink}}"> | |||
{{.Owner.ShortName 40}} | |||
</div> | |||
</div> | |||
</div> | |||
<div class="ui interval" style="width: 0.6em;font-size: 2rem;line-height: 38px;text-align: center;">/</div> | |||
<div class="eight wide required field {{if .Err_Alias}}error{{end}}"> | |||
<!-- <label for="repo_name">{{.i18n.Tr "repo.repo_name"}}</label> --> | |||
<input style="width: 100% !important;" id="repo_name" name="repo_name" value="{{.Repository.Name}}" data-repo-name="{{.Repository.Name}}" autofocus required> | |||
<span class="help">{{.i18n.Tr "form.repoadd_dash_dot_error"}}</span> | |||
</div> | |||
</div> | |||
</div> | |||
<div class="inline field" id="repoAdress" style="display: none;"> | |||
<label for="">{{.i18n.Tr "form.RepoAdress"}}:</label> | |||
<span></span> | |||
</div> | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.repo_size"}}</label> | |||
@@ -41,7 +75,7 @@ | |||
{{end}} | |||
<div class="field {{if .Err_Description}}error{{end}}"> | |||
<label for="description">{{$.i18n.Tr "repo.repo_desc"}}</label> | |||
<textarea id="description" name="description" rows="2" maxlength="255">{{.Repository.Description}}</textarea> | |||
<textarea id="description" name="description" rows="2" maxlength="254">{{.Repository.Description}}</textarea> | |||
</div> | |||
<div class="field {{if .Err_Website}}error{{end}}"> | |||
<label for="website">{{.i18n.Tr "repo.settings.site"}}</label> | |||
@@ -152,7 +186,7 @@ | |||
{{$isModelMangeEnabled := .Repository.UnitEnabled $.UnitTypeModelManage }} | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.model_manager"}}</label> | |||
<div class="ui checkbox {{if ne $.MODEL_COUNT 0}}disabled{{end}}"> | |||
<div class="ui checkbox"> | |||
<input class="enable-system" name="enable_model_manager" type="checkbox" {{if $isModelMangeEnabled}}checked{{end}}> | |||
<label>{{.i18n.Tr "repo.settings.model_desc"}}</label> | |||
</div> | |||
@@ -160,7 +194,7 @@ | |||
{{$isCloudBrainEnabled := .Repository.UnitEnabled $.UnitTypeCloudBrain }} | |||
<div class="inline field"> | |||
<label>{{.i18n.Tr "repo.cloudbrain"}}</label> | |||
<div class="ui checkbox {{if ne $.jobCount 0}}disabled{{end}}"> | |||
<div class="ui checkbox"> | |||
<input class="enable-system" name="enable_cloud_brain" type="checkbox" {{if $isCloudBrainEnabled}}checked{{end}}> | |||
<label>{{.i18n.Tr "repo.settings.cloudbrain_desc"}}</label> | |||
</div> | |||
@@ -475,7 +509,7 @@ | |||
<div class="field"> | |||
<label> | |||
{{.i18n.Tr "repo.settings.transfer_form_title"}} | |||
<span class="text red">{{.Repository.Name}}</span> | |||
<span class="text red">{{.Repository.Alias}}</span> | |||
</label> | |||
</div> | |||
<div class="required field"> | |||
@@ -507,7 +541,7 @@ | |||
<div class="field"> | |||
<label> | |||
{{.i18n.Tr "repo.settings.transfer_form_title"}} | |||
<span class="text red">{{.Repository.Name}}</span> | |||
<span class="text red">{{.Repository.Alias}}</span> | |||
</label> | |||
</div> | |||
<div class="required field"> | |||
@@ -534,7 +568,7 @@ | |||
<div class="content"> | |||
<div class="ui warning message text left"> | |||
{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br> | |||
{{.i18n.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}} | |||
{{.i18n.Tr "repo.settings.delete_notices_2" .Repository.Alias | Safe}} | |||
{{if .Repository.NumForks}}<br> | |||
{{.i18n.Tr "repo.settings.delete_notices_fork_1"}} | |||
{{end}} | |||
@@ -545,7 +579,7 @@ | |||
<div class="field"> | |||
<label> | |||
{{.i18n.Tr "repo.settings.transfer_form_title"}} | |||
<span class="text red">{{.Repository.Name}}</span> | |||
<span class="text red">{{.Repository.Alias}}</span> | |||
</label> | |||
</div> | |||
<div class="required field"> | |||
@@ -569,7 +603,7 @@ | |||
<div class="content"> | |||
<div class="ui warning message text left"> | |||
{{.i18n.Tr "repo.settings.delete_notices_1" | Safe}}<br> | |||
{{.i18n.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}} | |||
{{.i18n.Tr "repo.settings.wiki_delete_notices_1" .Repository.Alias | Safe}} | |||
</div> | |||
<form class="ui form" action="{{.Link}}" method="post"> | |||
{{.CsrfTokenHtml}} | |||
@@ -577,7 +611,7 @@ | |||
<div class="field"> | |||
<label> | |||
{{.i18n.Tr "repo.settings.transfer_form_title"}} | |||
<span class="text red">{{.Repository.Name}}</span> | |||
<span class="text red">{{.Repository.Alias}}</span> | |||
</label> | |||
</div> | |||
<div class="required field"> | |||
@@ -625,4 +659,4 @@ | |||
{{end}} | |||
{{end}} | |||
{{template "base/footer" .}} | |||
{{template "base/footer" .}} |
@@ -93,6 +93,7 @@ | |||
</span> | |||
</td> | |||
{{end}} | |||
<td class="message nine wide"> | |||
<span class="truncate"> | |||
<a href="{{$.RepoLink}}/commit/{{$commit.ID}}" title="{{$commit.Summary}}">{{$commit.Summary | RenderEmoji}}</a> | |||
@@ -29,7 +29,11 @@ | |||
<div class="ui grid"> | |||
<div class="column"> | |||
<form class="ui form" action="{{.SignInLink}}" method="post"> | |||
{{if .IsCourse}} | |||
<form class="ui form" action="{{.SignInLink}}?course=true" method="post"> | |||
{{else}} | |||
<form class="ui form" action="{{.SignInLink}}" method="post"> | |||
{{end}} | |||
{{.CsrfTokenHtml}} | |||
<div class="field"> | |||
<div class="ui left icon input {{if and (.Err_UserName) (or (not .LinkAccountMode) (and .LinkAccountMode .LinkAccountModeSignIn))}}error{{end}}"> | |||
@@ -13,60 +13,60 @@ | |||
{{.ShortActUserName}} | |||
{{end}} | |||
{{if eq .GetOpType 1}} | |||
{{$.i18n.Tr "action.create_repo" .GetRepoLink .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.create_repo" .GetRepoLink .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 2}} | |||
{{$.i18n.Tr "action.rename_repo" .GetContent .GetRepoLink .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.rename_repo" .GetContent .GetRepoLink .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 5}} | |||
{{ $branchLink := .GetBranch | EscapePound | Escape}} | |||
{{$.i18n.Tr "action.commit_repo" .GetRepoLink $branchLink (Escape .GetBranch) .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.commit_repo" .GetRepoLink $branchLink (Escape .GetBranch) .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 6}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.create_issue" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.create_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 7}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.create_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.create_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 8}} | |||
{{$.i18n.Tr "action.transfer_repo" .GetContent .GetRepoLink .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.transfer_repo" .GetContent .GetRepoLink .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 9}} | |||
{{ $branchLink := .GetBranch | EscapePound | Escape}} | |||
{{$.i18n.Tr "action.push_tag" .GetRepoLink $branchLink .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.push_tag" .GetRepoLink $branchLink .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 10}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.comment_issue" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.comment_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 11}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.merge_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.merge_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 12}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.close_issue" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.close_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 13}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.reopen_issue" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.reopen_issue" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 14}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.close_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.close_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 15}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.reopen_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.reopen_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 16}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.delete_tag" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 17}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.delete_branch" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 18}} | |||
{{ $branchLink := .GetBranch | EscapePound}} | |||
{{$.i18n.Tr "action.mirror_sync_push" .GetRepoLink $branchLink .GetBranch .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.mirror_sync_push" .GetRepoLink $branchLink .GetBranch .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 19}} | |||
{{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.mirror_sync_create" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 20}} | |||
{{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.mirror_sync_delete" .GetRepoLink .GetBranch .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 21}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.approve_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 22}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.reject_pull_request" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||
{{$.i18n.Tr "action.reject_pull_request" .GetRepoLink $index .ShortRepoFullDisplayName | Str2html}} | |||
{{else if eq .GetOpType 23}} | |||
{{ $index := index .GetIssueInfos 0}} | |||
{{$.i18n.Tr "action.comment_pull" .GetRepoLink $index .ShortRepoPath | Str2html}} | |||