Reviewed-by: 林嘉怡 <2441898885@qq.com> Reviewed-by: yuyuanshifu <747342561@qq.com>master
@@ -0,0 +1,101 @@ | |||||
package models | |||||
import ( | |||||
"code.gitea.io/gitea/modules/timeutil" | |||||
"fmt" | |||||
"time" | |||||
) | |||||
type BlockChainIssueStatus int | |||||
const ( | |||||
BlockChainIssueInit BlockChainIssueStatus = iota | |||||
BlockChainIssueSuccess | |||||
BlockChainIssueFailed | |||||
) | |||||
type BlockChainIssue struct { | |||||
ID int64 `xorm:"pk autoincr"` | |||||
IssueID int64 `xorm:"INDEX NOT NULL unique"` | |||||
Contributor string `xorm:"INDEX NOT NULL"` | |||||
ContractAddress string `xorm:"INDEX NOT NULL"` | |||||
Status BlockChainIssueStatus `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
Amount int64 `xorm:"INDEX"` | |||||
UserID int64 `xorm:"INDEX"` | |||||
RepoID int64 `xorm:"INDEX"` | |||||
TransactionHash string `xorm:"INDEX"` | |||||
CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"` | |||||
DeletedAt time.Time `xorm:"deleted"` | |||||
User *User `xorm:"-"` | |||||
Repo *Repository `xorm:"-"` | |||||
} | |||||
func getBlockChainIssueByID(e Engine, id int64) (*BlockChainIssue, error) { | |||||
blockChainIssue := new(BlockChainIssue) | |||||
has, err := e.ID(id).Get(blockChainIssue) | |||||
if err != nil { | |||||
return nil, err | |||||
} else if !has { | |||||
return nil, fmt.Errorf("get block_chain by id failed(%d)", id) | |||||
} | |||||
return blockChainIssue, nil | |||||
} | |||||
func GetBlockChainIssueByID(id int64) (*BlockChainIssue, error) { | |||||
return getBlockChainIssueByID(x, id) | |||||
} | |||||
func getBlockChainIssueByPrID(e Engine, prId int64) (*BlockChainIssue, error) { | |||||
blockChainIssue := new(BlockChainIssue) | |||||
has, err := e.Where("pr_id = ?", prId).Get(blockChainIssue) | |||||
if err != nil { | |||||
return nil, err | |||||
} else if !has { | |||||
return nil, fmt.Errorf("get block_chain by pr_id failed(%d)", prId) | |||||
} | |||||
return blockChainIssue, nil | |||||
} | |||||
func GetBlockChainIssueByPrID(prId int64) (*BlockChainIssue, error) { | |||||
return getBlockChainIssueByPrID(x, prId) | |||||
} | |||||
func getBlockChainIssueByCommitID(e Engine, commitID string) (*BlockChainIssue, error) { | |||||
blockChainIssue := new(BlockChainIssue) | |||||
has, err := e.Where("commit_id = ?", commitID).Get(blockChainIssue) | |||||
if err != nil { | |||||
return nil, err | |||||
} else if !has { | |||||
return nil, fmt.Errorf("get block_chain_issue by commitID failed(%s)", commitID) | |||||
} | |||||
return blockChainIssue, nil | |||||
} | |||||
func GetBlockChainIssueByCommitID(commitID string) (*BlockChainIssue, error) { | |||||
return getBlockChainIssueByCommitID(x, commitID) | |||||
} | |||||
func updateBlockChainIssueCols(e Engine, blockChainIssue *BlockChainIssue, cols ...string) error { | |||||
_, err := e.ID(blockChainIssue.ID).Cols(cols...).Update(blockChainIssue) | |||||
return err | |||||
} | |||||
func UpdateBlockChainIssueCols(blockChainIssue *BlockChainIssue, cols ...string) error { | |||||
return updateBlockChainIssueCols(x, blockChainIssue, cols...) | |||||
} | |||||
func GetBlockChainUnSuccessIssues() ([]*BlockChainIssue, error) { | |||||
blockChainIssues := make([]*BlockChainIssue, 0, 10) | |||||
return blockChainIssues, x. | |||||
Where("status != ?", BlockChainIssueSuccess). | |||||
Find(&blockChainIssues) | |||||
} | |||||
func InsertBlockChainIssue(blockChainIssue *BlockChainIssue) (_ *BlockChainIssue, err error) { | |||||
if _, err := x.Insert(blockChainIssue); err != nil { | |||||
return nil, err | |||||
} | |||||
return blockChainIssue, nil | |||||
} |
@@ -15,18 +15,19 @@ const ( | |||||
) | ) | ||||
type BlockChain struct { | type BlockChain struct { | ||||
ID int64 `xorm:"pk autoincr"` | |||||
CommitID string `xorm:"INDEX NOT NULL"` | |||||
Contributor string `xorm:"INDEX NOT NULL"` | |||||
ContractAddress string `xorm:"INDEX NOT NULL"` | |||||
Status BlockChainCommitStatus `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
Amount int64 `xorm:"INDEX"` | |||||
UserID int64 `xorm:"INDEX"` | |||||
RepoID int64 `xorm:"INDEX"` | |||||
TransactionHash string `xorm:"INDEX"` | |||||
CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"` | |||||
DeletedAt time.Time `xorm:"deleted"` | |||||
ID int64 `xorm:"pk autoincr"` | |||||
PrID int64 `xorm:"INDEX NOT NULL unique"` | |||||
CommitID string `xorm:"INDEX NOT NULL unique"` | |||||
Contributor string `xorm:"INDEX NOT NULL"` | |||||
ContractAddress string `xorm:"INDEX NOT NULL"` | |||||
Status BlockChainCommitStatus `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
Amount int64 `xorm:"INDEX"` | |||||
UserID int64 `xorm:"INDEX"` | |||||
RepoID int64 `xorm:"INDEX"` | |||||
TransactionHash string `xorm:"INDEX"` | |||||
CreatedUnix timeutil.TimeStamp `xorm:"created"` | |||||
UpdatedUnix timeutil.TimeStamp `xorm:"updated"` | |||||
DeletedAt time.Time `xorm:"deleted"` | |||||
User *User `xorm:"-"` | User *User `xorm:"-"` | ||||
Repo *Repository `xorm:"-"` | Repo *Repository `xorm:"-"` | ||||
@@ -47,6 +48,21 @@ func GetBlockChainByID(id int64) (*BlockChain, error) { | |||||
return getBlockChainByID(x, id) | return getBlockChainByID(x, id) | ||||
} | } | ||||
func getBlockChainByPrID(e Engine, prId int64) (*BlockChain, error) { | |||||
blockChain := new(BlockChain) | |||||
has, err := e.Where("pr_id = ?", prId).Get(blockChain) | |||||
if err != nil { | |||||
return nil, err | |||||
} else if !has { | |||||
return nil, fmt.Errorf("get block_chain by pr_id failed(%d)", prId) | |||||
} | |||||
return blockChain, nil | |||||
} | |||||
func GetBlockChainByPrID(prId int64) (*BlockChain, error) { | |||||
return getBlockChainByPrID(x, prId) | |||||
} | |||||
func getBlockChainByCommitID(e Engine, commitID string) (*BlockChain, error) { | func getBlockChainByCommitID(e Engine, commitID string) (*BlockChain, error) { | ||||
blockChain := new(BlockChain) | blockChain := new(BlockChain) | ||||
has, err := e.Where("commit_id = ?", commitID).Get(blockChain) | has, err := e.Where("commit_id = ?", commitID).Get(blockChain) | ||||
@@ -66,6 +66,10 @@ type Issue struct { | |||||
// IsLocked limits commenting abilities to users on an issue | // IsLocked limits commenting abilities to users on an issue | ||||
// with write access | // with write access | ||||
IsLocked bool `xorm:"NOT NULL DEFAULT false"` | IsLocked bool `xorm:"NOT NULL DEFAULT false"` | ||||
//block_chain | |||||
Amount int64 | |||||
IsTransformed bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
} | } | ||||
var ( | var ( | ||||
@@ -36,6 +36,13 @@ const ( | |||||
PullRequestStatusError | PullRequestStatusError | ||||
) | ) | ||||
const ( | |||||
PullRequestAmountZero int = 0 | |||||
PullRequestAmountOne int = 100 | |||||
PullRequestAmountTwo int = 200 | |||||
PullRequestAmountMax int = 300 | |||||
) | |||||
// PullRequest represents relation between pull request and repositories. | // PullRequest represents relation between pull request and repositories. | ||||
type PullRequest struct { | type PullRequest struct { | ||||
ID int64 `xorm:"pk autoincr"` | ID int64 `xorm:"pk autoincr"` | ||||
@@ -65,6 +72,10 @@ type PullRequest struct { | |||||
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` | MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"` | ||||
isHeadRepoLoaded bool `xorm:"-"` | isHeadRepoLoaded bool `xorm:"-"` | ||||
//block_chain | |||||
IsTransformed bool `xorm:"INDEX NOT NULL DEFAULT false"` | |||||
Amount int `xorm:"INDEX NOT NULL DEFAULT 0"` | |||||
} | } | ||||
// MustHeadUserName returns the HeadRepo's username if failed return blank | // MustHeadUserName returns the HeadRepo's username if failed return blank | ||||
@@ -396,7 +407,7 @@ func (pr *PullRequest) SetMerged() (bool, error) { | |||||
return false, fmt.Errorf("Issue.changeStatus: %v", err) | return false, fmt.Errorf("Issue.changeStatus: %v", err) | ||||
} | } | ||||
if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix").Update(pr); err != nil { | |||||
if _, err := sess.Where("id = ?", pr.ID).Cols("has_merged, status, merged_commit_id, merger_id, merged_unix, amount").Update(pr); err != nil { | |||||
return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err) | return false, fmt.Errorf("Failed to update pr[%d]: %v", pr.ID, err) | ||||
} | } | ||||
@@ -5,6 +5,7 @@ | |||||
package models | package models | ||||
import ( | import ( | ||||
"code.gitea.io/gitea/modules/setting" | |||||
"fmt" | "fmt" | ||||
"code.gitea.io/gitea/modules/base" | "code.gitea.io/gitea/modules/base" | ||||
@@ -170,3 +171,11 @@ func (prs PullRequestList) invalidateCodeComments(e Engine, doer *User, repo *gi | |||||
func (prs PullRequestList) InvalidateCodeComments(doer *User, repo *git.Repository, branch string) error { | func (prs PullRequestList) InvalidateCodeComments(doer *User, repo *git.Repository, branch string) error { | ||||
return prs.invalidateCodeComments(x, doer, repo, branch) | return prs.invalidateCodeComments(x, doer, repo, branch) | ||||
} | } | ||||
func GetUnTransformedMergedPullRequests() ([]*PullRequest, error) { | |||||
prs := make([]*PullRequest, 0, 10) | |||||
return prs, x. | |||||
Where("has_merged = ? AND pull_request.is_transformed = ? AND to_timestamp(merged_unix) >= ?",true, false, setting.CommitValidDate). | |||||
Join("INNER", "issue", "issue.id = pull_request.issue_id"). | |||||
Find(&prs) | |||||
} |
@@ -206,9 +206,9 @@ type Repository struct { | |||||
Avatar string `xorm:"VARCHAR(64)"` | Avatar string `xorm:"VARCHAR(64)"` | ||||
//blockchain | //blockchain | ||||
ContractAddress string `xorm:"INDEX"` | |||||
Balance int64 `xorm:"NOT NULL DEFAULT 0"` | |||||
BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"` | |||||
ContractAddress string `xorm:"INDEX"` | |||||
Balance string `xorm:"NOT NULL DEFAULT '0'"` | |||||
BlockChainStatus RepoBlockChainStatus `xorm:"NOT NULL DEFAULT 0"` | |||||
// git clone total count | // git clone total count | ||||
CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` | CloneCnt int64 `xorm:"NOT NULL DEFAULT 0"` | ||||
@@ -1105,6 +1105,12 @@ func CreateRepository(ctx DBContext, doer, u *User, repo *Repository) (err error | |||||
Type: tp, | Type: tp, | ||||
Config: &CloudBrainConfig{EnableCloudBrain: true}, | Config: &CloudBrainConfig{EnableCloudBrain: true}, | ||||
}) | }) | ||||
} else if tp == UnitTypeBlockChain { | |||||
units = append(units, RepoUnit{ | |||||
RepoID: repo.ID, | |||||
Type: tp, | |||||
Config: &BlockChainConfig{EnableBlockChain: true}, | |||||
}) | |||||
} else { | } else { | ||||
units = append(units, RepoUnit{ | units = append(units, RepoUnit{ | ||||
RepoID: repo.ID, | RepoID: repo.ID, | ||||
@@ -141,6 +141,20 @@ func (cfg *CloudBrainConfig) ToDB() ([]byte, error) { | |||||
return json.Marshal(cfg) | return json.Marshal(cfg) | ||||
} | } | ||||
type BlockChainConfig struct { | |||||
EnableBlockChain bool | |||||
} | |||||
// FromDB fills up a CloudBrainConfig from serialized format. | |||||
func (cfg *BlockChainConfig) FromDB(bs []byte) error { | |||||
return json.Unmarshal(bs, &cfg) | |||||
} | |||||
// ToDB exports a CloudBrainConfig to a serialized format. | |||||
func (cfg *BlockChainConfig) ToDB() ([]byte, error) { | |||||
return json.Marshal(cfg) | |||||
} | |||||
// BeforeSet is invoked from XORM before setting the value of a field of this object. | // BeforeSet is invoked from XORM before setting the value of a field of this object. | ||||
func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | ||||
switch colName { | switch colName { | ||||
@@ -160,6 +174,8 @@ func (r *RepoUnit) BeforeSet(colName string, val xorm.Cell) { | |||||
r.Config = new(DatasetConfig) | r.Config = new(DatasetConfig) | ||||
case UnitTypeCloudBrain: | case UnitTypeCloudBrain: | ||||
r.Config = new(CloudBrainConfig) | r.Config = new(CloudBrainConfig) | ||||
case UnitTypeBlockChain: | |||||
r.Config = new(BlockChainConfig) | |||||
default: | default: | ||||
panic("unrecognized repo unit type: " + com.ToStr(*val)) | panic("unrecognized repo unit type: " + com.ToStr(*val)) | ||||
} | } | ||||
@@ -26,6 +26,7 @@ const ( | |||||
UnitTypeExternalTracker // 7 ExternalTracker | UnitTypeExternalTracker // 7 ExternalTracker | ||||
UnitTypeDatasets UnitType = 10 // 10 Dataset | UnitTypeDatasets UnitType = 10 // 10 Dataset | ||||
UnitTypeCloudBrain UnitType = 11 // 11 CloudBrain | UnitTypeCloudBrain UnitType = 11 // 11 CloudBrain | ||||
UnitTypeBlockChain UnitType = 12 // 12 BlockChain | |||||
) | ) | ||||
// Value returns integer value for unit type | // Value returns integer value for unit type | ||||
@@ -53,6 +54,8 @@ func (u UnitType) String() string { | |||||
return "UnitTypeDataset" | return "UnitTypeDataset" | ||||
case UnitTypeCloudBrain: | case UnitTypeCloudBrain: | ||||
return "UnitTypeCloudBrain" | return "UnitTypeCloudBrain" | ||||
case UnitTypeBlockChain: | |||||
return "UnitTypeBlockChain" | |||||
} | } | ||||
return fmt.Sprintf("Unknown UnitType %d", u) | return fmt.Sprintf("Unknown UnitType %d", u) | ||||
} | } | ||||
@@ -76,6 +79,7 @@ var ( | |||||
UnitTypeExternalTracker, | UnitTypeExternalTracker, | ||||
UnitTypeDatasets, | UnitTypeDatasets, | ||||
UnitTypeCloudBrain, | UnitTypeCloudBrain, | ||||
UnitTypeBlockChain, | |||||
} | } | ||||
// DefaultRepoUnits contains the default unit types | // DefaultRepoUnits contains the default unit types | ||||
@@ -87,6 +91,7 @@ var ( | |||||
UnitTypeWiki, | UnitTypeWiki, | ||||
UnitTypeDatasets, | UnitTypeDatasets, | ||||
UnitTypeCloudBrain, | UnitTypeCloudBrain, | ||||
UnitTypeBlockChain, | |||||
} | } | ||||
// NotAllowedDefaultRepoUnits contains units that can't be default | // NotAllowedDefaultRepoUnits contains units that can't be default | ||||
@@ -268,6 +273,14 @@ var ( | |||||
6, | 6, | ||||
} | } | ||||
UnitBlockChain = Unit{ | |||||
UnitTypeBlockChain, | |||||
"repo.blockchains", | |||||
"/blockchains", | |||||
"repo.blockchains.desc", | |||||
7, | |||||
} | |||||
// Units contains all the units | // Units contains all the units | ||||
Units = map[UnitType]Unit{ | Units = map[UnitType]Unit{ | ||||
UnitTypeCode: UnitCode, | UnitTypeCode: UnitCode, | ||||
@@ -279,6 +292,7 @@ var ( | |||||
UnitTypeExternalWiki: UnitExternalWiki, | UnitTypeExternalWiki: UnitExternalWiki, | ||||
UnitTypeDatasets: UnitDataset, | UnitTypeDatasets: UnitDataset, | ||||
UnitTypeCloudBrain: UnitCloudBrain, | UnitTypeCloudBrain: UnitCloudBrain, | ||||
UnitTypeBlockChain: UnitBlockChain, | |||||
} | } | ||||
) | ) | ||||
@@ -174,8 +174,8 @@ type User struct { | |||||
Token string `xorm:"VARCHAR(1024)"` | Token string `xorm:"VARCHAR(1024)"` | ||||
//BlockChain | //BlockChain | ||||
PublicKey string `xorm` | |||||
PrivateKey string `xorm` | |||||
PublicKey string `xorm:"INDEX"` | |||||
PrivateKey string `xorm:"INDEX"` | |||||
} | } | ||||
// SearchOrganizationsOptions options to filter organizations | // SearchOrganizationsOptions options to filter organizations | ||||
@@ -369,6 +369,7 @@ type CreateIssueForm struct { | |||||
AssigneeID int64 | AssigneeID int64 | ||||
Content string | Content string | ||||
Files []string | Files []string | ||||
Rewards int64 | |||||
} | } | ||||
// Validate validates the fields | // Validate validates the fields | ||||
@@ -489,6 +490,7 @@ type MergePullRequestForm struct { | |||||
MergeTitleField string | MergeTitleField string | ||||
MergeMessageField string | MergeMessageField string | ||||
ForceMerge *bool `json:"force_merge,omitempty"` | ForceMerge *bool `json:"force_merge,omitempty"` | ||||
BlockChainAmount int | |||||
} | } | ||||
// Validate validates the fields | // Validate validates the fields | ||||
@@ -2,6 +2,7 @@ package blockchain | |||||
import ( | import ( | ||||
"fmt" | "fmt" | ||||
"strconv" | |||||
"code.gitea.io/gitea/modules/setting" | "code.gitea.io/gitea/modules/setting" | ||||
"github.com/go-resty/resty/v2" | "github.com/go-resty/resty/v2" | ||||
@@ -13,11 +14,10 @@ var ( | |||||
const ( | const ( | ||||
UrlCreateAccount = "createAccount" | UrlCreateAccount = "createAccount" | ||||
UrlGetBalance = "getBalance" | |||||
UrlNewRepo = "newRepo" | |||||
UrlContribute = "contribute" | |||||
ActionCommit = "commit" | |||||
UrlGetBalance = "getBalance" | |||||
UrlNewRepo = "newRepo" | |||||
UrlContribute = "contribute" | |||||
UrlSetIssue = "setIssue" | |||||
Success = 0 | Success = 0 | ||||
) | ) | ||||
@@ -31,7 +31,7 @@ type CreateAccountResult struct { | |||||
type GetBalanceResult struct { | type GetBalanceResult struct { | ||||
Code int `json:"code"` | Code int `json:"code"` | ||||
Msg string `json:"message"` | Msg string `json:"message"` | ||||
Payload map[string]interface{} `json:"data"` | |||||
Data string `json:"data"` | |||||
} | } | ||||
type NewRepoResult struct { | type NewRepoResult struct { | ||||
@@ -41,9 +41,15 @@ type NewRepoResult struct { | |||||
} | } | ||||
type ContributeResult struct { | type ContributeResult struct { | ||||
Code int `json:"code"` | |||||
Msg string `json:"message"` | |||||
Payload map[string]interface{} `json:"data"` | |||||
Code int `json:"code"` | |||||
Msg string `json:"message"` | |||||
//Payload map[string]interface{} `json:"data"` | |||||
} | |||||
type SetIssueResult struct { | |||||
Code int `json:"code"` | |||||
Msg string `json:"message"` | |||||
//Data string `json:"data"` | |||||
} | } | ||||
func getRestyClient() *resty.Client { | func getRestyClient() *resty.Client { | ||||
@@ -122,18 +128,18 @@ func GetBalance(contractAddress, contributor string) (*GetBalanceResult, error) | |||||
return &result, nil | return &result, nil | ||||
} | } | ||||
func Contribute(contractAddress, contributor, action, commitId string, codeLine int) (*ContributeResult, error) { | |||||
func Contribute(contractAddress, contributor, commitId string, amount int64) (*ContributeResult, error) { | |||||
client := getRestyClient() | client := getRestyClient() | ||||
var result ContributeResult | var result ContributeResult | ||||
strAmount := strconv.FormatInt(amount, 10) | |||||
res, err := client.R(). | res, err := client.R(). | ||||
SetHeader("Accept", "application/json"). | SetHeader("Accept", "application/json"). | ||||
SetQueryParams(map[string]string{ | SetQueryParams(map[string]string{ | ||||
"contractAddress": contractAddress, | |||||
"contributor": contributor, | |||||
"action": action, | |||||
"commitId": commitId, | |||||
"amount": string(codeLine), | |||||
"contractAddress" : contractAddress, | |||||
"contributor" : contributor, | |||||
"commitId": commitId, | |||||
"amount": strAmount, | |||||
}). | }). | ||||
SetResult(&result). | SetResult(&result). | ||||
Get(setting.BlockChainHost + UrlContribute) | Get(setting.BlockChainHost + UrlContribute) | ||||
@@ -148,3 +154,31 @@ func Contribute(contractAddress, contributor, action, commitId string, codeLine | |||||
return &result, nil | return &result, nil | ||||
} | } | ||||
func SetIssue(contractAddress, contributor string, issueId int64, amount int64) (*SetIssueResult, error) { | |||||
client := getRestyClient() | |||||
var result SetIssueResult | |||||
strAmount := strconv.FormatInt(amount, 10) | |||||
strIssue := strconv.FormatInt(issueId, 10) | |||||
res, err := client.R(). | |||||
SetHeader("Accept", "application/json"). | |||||
SetQueryParams(map[string]string{ | |||||
"contractAddress" : contractAddress, | |||||
"contributor" : contributor, | |||||
"issueId": strIssue, | |||||
"amount": strAmount, | |||||
}). | |||||
SetResult(&result). | |||||
Get(setting.BlockChainHost + UrlSetIssue) | |||||
if err != nil { | |||||
return nil, fmt.Errorf("resty SetIssue: %v", err) | |||||
} | |||||
if result.Code != Success { | |||||
return &result, fmt.Errorf("SetIssue err: %s", res.String()) | |||||
} | |||||
return &result, nil | |||||
} |
@@ -820,5 +820,6 @@ func UnitTypes() macaron.Handler { | |||||
ctx.Data["UnitTypeWiki"] = models.UnitTypeWiki | ctx.Data["UnitTypeWiki"] = models.UnitTypeWiki | ||||
ctx.Data["UnitTypeExternalWiki"] = models.UnitTypeExternalWiki | ctx.Data["UnitTypeExternalWiki"] = models.UnitTypeExternalWiki | ||||
ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker | ctx.Data["UnitTypeExternalTracker"] = models.UnitTypeExternalTracker | ||||
ctx.Data["UnitTypeBlockChain"] = models.UnitTypeBlockChain | |||||
} | } | ||||
} | } |
@@ -12,16 +12,16 @@ func init() { | |||||
spec := "*/10 * * * *" | spec := "*/10 * * * *" | ||||
c.AddFunc(spec, repo.HandleUnDecompressAttachment) | c.AddFunc(spec, repo.HandleUnDecompressAttachment) | ||||
//specCheckBlockChainUserSuccess := "*/10 * * * *" | |||||
//c.AddFunc(specCheckBlockChainUserSuccess, repo.HandleBlockChainUnSuccessUsers) | |||||
specCheckBlockChainUserSuccess := "*/10 * * * *" | |||||
c.AddFunc(specCheckBlockChainUserSuccess, repo.HandleBlockChainUnSuccessUsers) | |||||
//specCheckRepoBlockChainSuccess := "*/5 * * * *" | |||||
//c.AddFunc(specCheckRepoBlockChainSuccess, repo.HandleBlockChainUnSuccessRepos) | |||||
specCheckRepoBlockChainSuccess := "*/1 * * * *" | |||||
c.AddFunc(specCheckRepoBlockChainSuccess, repo.HandleBlockChainUnSuccessRepos) | |||||
//specCheckUnTransformedActions := "*/1 * * * *" | |||||
//c.AddFunc(specCheckUnTransformedActions, repo.HandleUnTransformedActions) | |||||
specCheckUnTransformedPRs := "*/1 * * * *" | |||||
c.AddFunc(specCheckUnTransformedPRs, repo.HandleBlockChainMergedPulls) | |||||
//specCheckBlockChainCommitSuccess := "*/3 * * * *" | |||||
//c.AddFunc(specCheckBlockChainCommitSuccess, repo.HandleBlockChainUnSuccessCommits) | |||||
specCheckBlockChainCommitSuccess := "*/3 * * * *" | |||||
c.AddFunc(specCheckBlockChainCommitSuccess, repo.HandleBlockChainUnSuccessCommits) | |||||
c.Start() | c.Start() | ||||
} | } |
@@ -268,7 +268,7 @@ twofa_passcode_incorrect=你的验证码不正确。如果你丢失了你的设 | |||||
twofa_scratch_token_incorrect=你的验证口令不正确。 | twofa_scratch_token_incorrect=你的验证口令不正确。 | ||||
login_userpass=登录 | login_userpass=登录 | ||||
login_openid=OpenID | login_openid=OpenID | ||||
login_cloudbrain=云脑用户登录 | |||||
login_cloudbrain=用户登录 | |||||
oauth_signup_tab=注册帐号 | oauth_signup_tab=注册帐号 | ||||
oauth_signup_title=添加电子邮件和密码 (用于帐号恢复) | oauth_signup_title=添加电子邮件和密码 (用于帐号恢复) | ||||
oauth_signup_submit=完成账号 | oauth_signup_submit=完成账号 | ||||
@@ -753,7 +753,10 @@ cloudbrain.new=新建任务 | |||||
cloudbrain.desc=云脑功能 | cloudbrain.desc=云脑功能 | ||||
cloudbrain.cancel=取消 | cloudbrain.cancel=取消 | ||||
cloudbrain.commit_image=提交 | cloudbrain.commit_image=提交 | ||||
clone_cnt=次下载 | |||||
balance=余额 | |||||
balance.total_view=余额总览 | |||||
balance.available=可用余额: | |||||
balance.disable=不可用余额: | |||||
template.items=模板选项 | template.items=模板选项 | ||||
template.git_content=Git数据(默认分支) | template.git_content=Git数据(默认分支) | ||||
@@ -936,6 +939,7 @@ issues.filter_labels=筛选标签 | |||||
issues.filter_reviewers=筛选审核者 | issues.filter_reviewers=筛选审核者 | ||||
issues.new=创建任务 | issues.new=创建任务 | ||||
issues.new.title_empty=标题不能为空 | issues.new.title_empty=标题不能为空 | ||||
issues.new.rewards_error=奖励金额错误:不能小于0,且不能大于自身余额 | |||||
issues.new.labels=标签 | issues.new.labels=标签 | ||||
issues.new.add_labels_title=添加标签 | issues.new.add_labels_title=添加标签 | ||||
issues.new.no_label=未选择标签 | issues.new.no_label=未选择标签 | ||||
@@ -1,14 +1,14 @@ | |||||
package repo | package repo | ||||
import ( | import ( | ||||
"code.gitea.io/gitea/modules/repository" | |||||
"encoding/json" | |||||
"strconv" | |||||
"code.gitea.io/gitea/models" | "code.gitea.io/gitea/models" | ||||
"code.gitea.io/gitea/modules/base" | |||||
"code.gitea.io/gitea/modules/blockchain" | "code.gitea.io/gitea/modules/blockchain" | ||||
"code.gitea.io/gitea/modules/context" | "code.gitea.io/gitea/modules/context" | ||||
"code.gitea.io/gitea/modules/log" | "code.gitea.io/gitea/modules/log" | ||||
"encoding/json" | |||||
"net/http" | |||||
"strconv" | |||||
) | ) | ||||
type BlockChainInitNotify struct { | type BlockChainInitNotify struct { | ||||
@@ -20,6 +20,29 @@ type BlockChainCommitNotify struct { | |||||
CommitID string `json:"commitId"` | CommitID string `json:"commitId"` | ||||
TransactionHash string `json:"txHash"` | TransactionHash string `json:"txHash"` | ||||
} | } | ||||
const ( | |||||
tplBlockChainIndex base.TplName = "repo/blockchain/index" | |||||
) | |||||
func BlockChainIndex(ctx *context.Context) { | |||||
repo := ctx.Repo.Repository | |||||
if repo.ContractAddress == "" || ctx.User.PublicKey == ""{ | |||||
log.Error("the repo(%d) or the user(%d) has not been initialized in block_chain", repo.RepoID, ctx.User.ID) | |||||
ctx.HTML(http.StatusInternalServerError, tplBlockChainIndex) | |||||
return | |||||
} | |||||
res, err := blockchain.GetBalance(repo.ContractAddress, ctx.User.PublicKey) | |||||
if err != nil { | |||||
log.Error("GetBalance(%s) failed:%v", ctx.User.PublicKey, err) | |||||
ctx.HTML(http.StatusInternalServerError, tplBlockChainIndex) | |||||
return | |||||
} | |||||
ctx.Data["balance"] = res.Data | |||||
ctx.Data["PageIsBlockChain"] = true | |||||
ctx.HTML(200, tplBlockChainIndex) | |||||
} | |||||
func HandleBlockChainInitNotify(ctx *context.Context) { | func HandleBlockChainInitNotify(ctx *context.Context) { | ||||
var req BlockChainInitNotify | var req BlockChainInitNotify | ||||
@@ -130,7 +153,6 @@ func HandleBlockChainUnSuccessRepos() { | |||||
continue | continue | ||||
} | } | ||||
strRepoID := strconv.FormatInt(repo.ID, 10) | strRepoID := strconv.FormatInt(repo.ID, 10) | ||||
log.Info(strRepoID) | |||||
_, err = blockchain.NewRepo(strRepoID, repo.Owner.PublicKey, repo.Name) | _, err = blockchain.NewRepo(strRepoID, repo.Owner.PublicKey, repo.Name) | ||||
if err != nil { | if err != nil { | ||||
log.Error("blockchain.NewRepo(%s) failed:%v", strRepoID, err) | log.Error("blockchain.NewRepo(%s) failed:%v", strRepoID, err) | ||||
@@ -148,7 +170,7 @@ func HandleBlockChainUnSuccessCommits() { | |||||
} | } | ||||
for _, block_chain := range blockChains { | for _, block_chain := range blockChains { | ||||
_, err = blockchain.Contribute(block_chain.ContractAddress, block_chain.Contributor, blockchain.ActionCommit, block_chain.CommitID, int(block_chain.Amount)) | |||||
_, err = blockchain.Contribute(block_chain.ContractAddress, block_chain.Contributor, block_chain.CommitID, block_chain.Amount) | |||||
if err != nil { | if err != nil { | ||||
log.Error("blockchain.Contribute(%s) failed:%v", block_chain.CommitID, err) | log.Error("blockchain.Contribute(%s) failed:%v", block_chain.CommitID, err) | ||||
} | } | ||||
@@ -180,71 +202,89 @@ func HandleBlockChainUnSuccessUsers() { | |||||
return | return | ||||
} | } | ||||
func HandleUnTransformedActions() { | |||||
actions, err := models.GetUnTransformedActions() | |||||
func HandleBlockChainMergedPulls() { | |||||
prs, err := models.GetUnTransformedMergedPullRequests() | |||||
if err != nil { | if err != nil { | ||||
log.Error("GetUnTransformedActions failed:", err.Error()) | |||||
log.Error("GetUnTransformedMergedPullRequests failed:", err.Error()) | |||||
return | return | ||||
} | } | ||||
isTransformed := true | |||||
for _, pr := range prs { | |||||
_, err = models.GetBlockChainByPrID(pr.ID) | |||||
if err == nil { | |||||
log.Info("the pr(%s) has been transformed", pr.MergedCommitID) | |||||
continue | |||||
} | |||||
for _, action := range actions { | |||||
var content repository.PushCommits | |||||
err = json.Unmarshal([]byte(action.Content), &content) | |||||
err = pr.LoadIssue() | |||||
if err != nil { | if err != nil { | ||||
isTransformed = false | |||||
log.Error("json.Unmarshal action.Content(%s) failed:%v", action.Content, err) | |||||
break | |||||
log.Error("LoadIssue(%s) failed:%v", pr.MergedCommitID, err) | |||||
continue | |||||
} | } | ||||
repo, err := models.GetRepositoryByID(action.RepoID) | |||||
poster, err := models.GetUserByID(pr.Issue.PosterID) | |||||
if err != nil { | if err != nil { | ||||
isTransformed = false | |||||
log.Error("GetRepositoryByID(%d) failed:%v", action.RepoID, err) | |||||
break | |||||
log.Error("GetUserByID(%s) failed:%v", pr.MergedCommitID, err) | |||||
continue | |||||
} | |||||
if len(poster.PrivateKey) == 0 || len(poster.PublicKey) == 0 { | |||||
log.Error("the user has not been init in block_chain:", poster.Name) | |||||
continue | |||||
} | } | ||||
if repo.ContractAddress == "" { | |||||
isTransformed = false | |||||
repo, err := models.GetRepositoryByID(pr.HeadRepoID) | |||||
if err != nil { | |||||
log.Error("GetUserByID(%s) failed:%v", pr.MergedCommitID, err) | |||||
continue | |||||
} | |||||
if len(repo.ContractAddress) == 0 { | |||||
log.Error("the repo(%s) has not been initialized in block_chain", repo.Name) | log.Error("the repo(%s) has not been initialized in block_chain", repo.Name) | ||||
break | |||||
continue | |||||
} | } | ||||
for _, commit := range content.Commits { | |||||
_, err = models.GetBlockChainByCommitID(commit.Sha1) | |||||
if err == nil { | |||||
log.Info("the commit(%s) has been transformed", commit.Sha1) | |||||
continue | |||||
} | |||||
user, err := models.GetUserByName(commit.CommitterName) | |||||
if err != nil { | |||||
isTransformed = false | |||||
log.Error("GetUserByName(%s) failed:%v", commit.CommitterName, err) | |||||
break | |||||
} | |||||
blockChain := models.BlockChain{ | |||||
CommitID: commit.Sha1, | |||||
Contributor: user.PublicKey, | |||||
ContractAddress: repo.ContractAddress, | |||||
Status: models.BlockChainCommitInit, | |||||
Amount: 1, | |||||
UserID: action.UserID, | |||||
RepoID: action.RepoID, | |||||
} | |||||
_, err = models.InsertBlockChain(&blockChain) | |||||
if err != nil { | |||||
isTransformed = false | |||||
log.Error("InsertBlockChain(%s) failed:%v", commit.Sha1, err) | |||||
break | |||||
} | |||||
blockChain := models.BlockChain{ | |||||
Contributor : poster.PublicKey, | |||||
PrID : pr.ID, | |||||
CommitID : pr.MergedCommitID, | |||||
ContractAddress : repo.ContractAddress, | |||||
Status : models.BlockChainCommitInit, | |||||
Amount : int64(pr.Amount), | |||||
UserID : poster.ID, | |||||
RepoID : pr.HeadRepoID, | |||||
} | } | ||||
_, err = models.InsertBlockChain(&blockChain) | |||||
if err != nil { | |||||
log.Error("InsertBlockChain(%s) failed:%v", pr.MergedCommitID, err) | |||||
continue | |||||
} | |||||
pr.IsTransformed = true | |||||
pr.UpdateCols("is_transformed") | |||||
_, err = blockchain.Contribute(repo.ContractAddress, poster.PublicKey, pr.MergedCommitID, int64(pr.Amount)) | |||||
if err != nil { | |||||
log.Error("Contribute(%s) failed:%v", pr.MergedCommitID, err) | |||||
} | |||||
} | } | ||||
log.Info("", isTransformed) | |||||
return | |||||
} | |||||
func HandleBlockChainUnSuccessIssues() { | |||||
issues, err := models.GetBlockChainUnSuccessCommits() | |||||
if err != nil { | |||||
log.Error("GetBlockChainUnSuccessIssues failed:", err.Error()) | |||||
return | |||||
} | |||||
for _, issue := range issues { | |||||
_, err = blockchain.SetIssue(issue.ContractAddress, issue.Contributor, issue.ID, issue.Amount) | |||||
if err != nil { | |||||
log.Error("SetIssue(%s) failed:%v", issue.CommitID, err) | |||||
} | |||||
} | |||||
return | return | ||||
} | } |
@@ -509,6 +509,21 @@ func NewIssue(ctx *context.Context) { | |||||
ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(models.UnitTypeIssues) | ctx.Data["HasIssuesOrPullsWritePermission"] = ctx.Repo.CanWrite(models.UnitTypeIssues) | ||||
/*if ctx.Repo.Repository.ContractAddress == "" || ctx.User.PublicKey == ""{ | |||||
log.Error("the repo(%d) or the user(%d) has not been initialized in block_chain", ctx.Repo.Repository.ID, ctx.User.ID) | |||||
ctx.HTML(http.StatusInternalServerError, tplIssueNew) | |||||
return | |||||
} | |||||
res, err := blockchain.GetBalance(ctx.Repo.Repository.ContractAddress, ctx.User.PublicKey) | |||||
if err != nil { | |||||
log.Error("GetBalance(%s) failed:%v", ctx.User.PublicKey, err) | |||||
ctx.HTML(http.StatusInternalServerError, tplIssueNew) | |||||
return | |||||
} | |||||
ctx.Data["balance"] = res.Data*/ | |||||
ctx.HTML(200, tplIssueNew) | ctx.HTML(200, tplIssueNew) | ||||
} | } | ||||
@@ -637,6 +652,33 @@ func NewIssuePost(ctx *context.Context, form auth.CreateIssueForm) { | |||||
Ref: form.Ref, | Ref: form.Ref, | ||||
} | } | ||||
/*if repo.ContractAddress == "" || ctx.User.PublicKey == ""{ | |||||
log.Error("the repo(%d) or the user(%d) has not been initialized in block_chain", issue.RepoID, ctx.User.ID) | |||||
ctx.HTML(http.StatusInternalServerError, tplIssueNew) | |||||
return | |||||
} | |||||
res, err := blockchain.GetBalance(repo.ContractAddress, ctx.User.PublicKey) | |||||
if err != nil { | |||||
log.Error("GetBalance(%s) failed:%v", ctx.User.PublicKey, err) | |||||
ctx.HTML(http.StatusInternalServerError, tplIssueNew) | |||||
return | |||||
} | |||||
balance, err := com.StrTo(res.Data).Int64() | |||||
if err != nil { | |||||
log.Error("balance(%s) convert failed:%v", res.Data, err) | |||||
ctx.HTML(http.StatusInternalServerError, tplIssueNew) | |||||
return | |||||
} | |||||
if form.Rewards < 0 || form.Rewards > balance { | |||||
ctx.RenderWithErr(ctx.Tr("repo.issues.new.rewards_error"), tplIssueNew, form) | |||||
return | |||||
} | |||||
issue.Amount = form.Rewards*/ | |||||
if err := issue_service.NewIssue(repo, issue, labelIDs, attachments, assigneeIDs); err != nil { | if err := issue_service.NewIssue(repo, issue, labelIDs, attachments, assigneeIDs); err != nil { | ||||
if models.IsErrUserDoesNotHaveAccessToRepo(err) { | if models.IsErrUserDoesNotHaveAccessToRepo(err) { | ||||
ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error()) | ctx.Error(400, "UserDoesNotHaveAccessToRepo", err.Error()) | ||||
@@ -810,6 +810,14 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) { | |||||
return | return | ||||
} | } | ||||
if form.BlockChainAmount < int(models.PullRequestAmountZero) || form.BlockChainAmount > int(models.PullRequestAmountMax) { | |||||
log.Error("amount set error(0-300)") | |||||
ctx.RenderWithErr("amount set error(0-300)", tplIssueView, form) | |||||
return | |||||
} | |||||
pr.Amount = form.BlockChainAmount | |||||
if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil { | if err = pull_service.Merge(pr, ctx.User, ctx.Repo.GitRepo, models.MergeStyle(form.Do), message); err != nil { | ||||
if models.IsErrInvalidMergeStyle(err) { | if models.IsErrInvalidMergeStyle(err) { | ||||
ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option")) | ctx.Flash.Error(ctx.Tr("repo.pulls.invalid_merge_option")) | ||||
@@ -568,6 +568,8 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
reqRepoDatasetWriter := context.RequireRepoWriter(models.UnitTypeDatasets) | reqRepoDatasetWriter := context.RequireRepoWriter(models.UnitTypeDatasets) | ||||
reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain) | reqRepoCloudBrainReader := context.RequireRepoReader(models.UnitTypeCloudBrain) | ||||
reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain) | reqRepoCloudBrainWriter := context.RequireRepoWriter(models.UnitTypeCloudBrain) | ||||
//reqRepoBlockChainReader := context.RequireRepoReader(models.UnitTypeBlockChain) | |||||
//reqRepoBlockChainWriter := context.RequireRepoWriter(models.UnitTypeBlockChain) | |||||
// ***** START: Organization ***** | // ***** START: Organization ***** | ||||
m.Group("/org", func() { | m.Group("/org", func() { | ||||
@@ -911,6 +913,10 @@ func RegisterRoutes(m *macaron.Macaron) { | |||||
m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | m.Post("/create", reqRepoCloudBrainWriter, bindIgnErr(auth.CreateCloudBrainForm{}), repo.CloudBrainCreate) | ||||
}, context.RepoRef()) | }, context.RepoRef()) | ||||
m.Group("/blockchain", func() { | |||||
m.Get("", repo.BlockChainIndex) | |||||
}, context.RepoRef()) | |||||
m.Group("/wiki", func() { | m.Group("/wiki", func() { | ||||
m.Get("/?:page", repo.Wiki) | m.Get("/?:page", repo.Wiki) | ||||
m.Get("/_pages", repo.WikiPages) | m.Get("/_pages", repo.WikiPages) | ||||
@@ -0,0 +1,18 @@ | |||||
{{template "base/head" .}} | |||||
<div class="repository balance view"> | |||||
{{template "repo/header" .}} | |||||
<div class="ui container"> | |||||
<h3 class="ui top attached header"> | |||||
{{.i18n.Tr "repo.balance.total_view"}} | |||||
</h3> | |||||
<div class="ui attached segment"> | |||||
<div class="inline field"> | |||||
<span class="fitted">{{.i18n.Tr "repo.balance.available"}}</span> | |||||
<span class="fitted">{{.balance}}</span> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
{{template "base/footer" .}} |
@@ -145,6 +145,11 @@ | |||||
</a> | </a> | ||||
{{end}} | {{end}} | ||||
<a class="{{if .PageIsBlockChain}}active{{end}} item " href="{{.RepoLink}}/blockchain"> | |||||
{{svg "octicon-law" 16}} | |||||
{{.i18n.Tr "repo.balance"}} | |||||
</a> | |||||
{{template "custom/extra_tabs" .}} | {{template "custom/extra_tabs" .}} | ||||
{{if .Permission.IsAdmin}} | {{if .Permission.IsAdmin}} | ||||
@@ -158,4 +163,4 @@ | |||||
{{end}} | {{end}} | ||||
</div> | </div> | ||||
<div class="ui tabs divider"></div> | <div class="ui tabs divider"></div> | ||||
</div> | |||||
</div> |
@@ -188,7 +188,6 @@ | |||||
{{end}} | {{end}} | ||||
<div class="ui {{if .IsClosed}}{{if .IsPull}}{{if .PullRequest.HasMerged}}purple{{else}}red{{end}}{{else}}red{{end}}{{else}}{{if .IsRead}}white{{else}}green{{end}}{{end}} label">#{{.Index}}</div> | <div class="ui {{if .IsClosed}}{{if .IsPull}}{{if .PullRequest.HasMerged}}purple{{else}}red{{end}}{{else}}red{{end}}{{else}}{{if .IsRead}}white{{else}}green{{end}}{{end}} label">#{{.Index}}</div> | ||||
<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji}}</a> | <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{.Title | RenderEmoji}}</a> | ||||
{{if .IsPull }} | {{if .IsPull }} | ||||
{{if (index $.CommitStatus .PullRequest.ID)}} | {{if (index $.CommitStatus .PullRequest.ID)}} | ||||
{{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}} | {{template "repo/commit_status" (index $.CommitStatus .PullRequest.ID)}} | ||||
@@ -1,182 +1,224 @@ | |||||
<form class="ui comment form stackable grid" action="{{.Link}}" method="post"> | |||||
{{.CsrfTokenHtml}} | |||||
{{if .Flash}} | |||||
<div class="sixteen wide column"> | |||||
{{template "base/alert" .}} | |||||
</div> | |||||
{{end}} | |||||
<div class="twelve wide column"> | |||||
<div class="ui comments"> | |||||
<div class="comment"> | |||||
<a class="avatar" href="{{.SignedUser.HomeLink}}"> | |||||
<img src="{{.SignedUser.RelAvatarLink}}"> | |||||
</a> | |||||
<div class="ui segment content"> | |||||
<div class="field"> | |||||
<input name="title" id="issue_title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" tabindex="3" autofocus required maxlength="255"> | |||||
{{if .PageIsComparePull}} | |||||
<div class="title_wip_desc">{{.i18n.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> | |||||
{{end}} | |||||
</div> | |||||
{{template "repo/issue/comment_tab" .}} | |||||
<div class="text right"> | |||||
<button class="ui green button" tabindex="6"> | |||||
{{if .PageIsComparePull}} | |||||
{{.i18n.Tr "repo.pulls.create"}} | |||||
{{else}} | |||||
{{.i18n.Tr "repo.issues.create"}} | |||||
{{end}} | |||||
</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<!-- --> | |||||
<form class="ui comment form stackable grid" action="{{.Link}}" method="post"> | |||||
{{.CsrfTokenHtml}} | |||||
{{if .Flash}} | |||||
<div class="sixteen wide column"> | |||||
{{template "base/alert" .}} | |||||
</div> | |||||
{{end}} | |||||
<div class="twelve wide column"> | |||||
<div class="ui comments"> | |||||
<div class="comment"> | |||||
<a class="avatar" href="{{.SignedUser.HomeLink}}"> | |||||
<img src="{{.SignedUser.RelAvatarLink}}"> | |||||
</a> | |||||
<div class="ui segment content"> | |||||
<div class="field"> | |||||
<!-- --> | |||||
<input name="title" id="issue_title" placeholder="{{.i18n.Tr "repo.milestones.title"}}" value="{{.title}}" tabindex="3" autofocus required maxlength="255"> | |||||
{{if .PageIsComparePull}} | |||||
<div class="title_wip_desc">{{.i18n.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}</div> | |||||
{{end}} | |||||
</div> | |||||
<div class="four wide column"> | |||||
<div class="ui segment metas"> | |||||
{{template "repo/issue/branch_selector_field" .}} | |||||
<!-- 项目奖励输入框 --> | |||||
<!-- <div class="field"> | |||||
<!-- value="{{.dog}}" -> | |||||
<input name="dog" id="issue_reward" placeholder="项目奖励" value="asdfas" tabindex="3" autofocus> | |||||
</div> --> | |||||
<input id="label_ids" name="label_ids" type="hidden" value="{{.label_ids}}"> | |||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-label dropdown"> | |||||
<span class="text"> | |||||
<strong>{{.i18n.Tr "repo.issues.new.labels"}}</strong> | |||||
{{if .HasIssuesOrPullsWritePermission}} | |||||
{{svg "octicon-gear" 16}} | |||||
{{end}} | |||||
</span> | |||||
<div class="filter menu" data-id="#label_ids"> | |||||
<div class="header" style="text-transform: none;font-size:16px;">{{.i18n.Tr "repo.issues.new.add_labels_title"}}</div> | |||||
{{if or .Labels .OrgLabels}} | |||||
<div class="ui icon search input"> | |||||
<i class="search icon"></i> | |||||
<input type="text" placeholder="{{.i18n.Tr "repo.issues.filter_labels"}}"> | |||||
</div> | |||||
{{end}} | |||||
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_labels"}}</div> | |||||
{{if or .Labels .OrgLabels}} | |||||
{{range .Labels}} | |||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check" 16}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} | |||||
{{if .Description }}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> | |||||
{{end}} | |||||
<div class="ui divider"></div> | |||||
{{range .OrgLabels}} | |||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check" 16}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} | |||||
{{if .Description }}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> | |||||
{{end}} | |||||
{{else}} | |||||
<div class="header" style="text-transform: none;font-size:14px;">{{.i18n.Tr "repo.issues.new.no_items"}}</div> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui labels list"> | |||||
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span> | |||||
{{range .Labels}} | |||||
<a class="{{if not .IsChecked}}hide{{end}} item" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}"><span class="label color" style="background-color: {{.Color}}"></span> <span class="text">{{.Name | RenderEmoji}}</span></a> | |||||
{{end}} | |||||
{{range .OrgLabels}} | |||||
<a class="{{if not .IsChecked}}hide{{end}} item" id="label_{{.ID}}" href="/issues?labels={{.ID}}"><span class="label color" style="background-color: {{.Color}}"></span> <span class="text">{{.Name | RenderEmoji}}</span></a> | |||||
{{end}} | |||||
</div> | |||||
{{template "repo/issue/comment_tab" .}} | |||||
<div class="text right"> | |||||
<button class="ui green button" tabindex="6"> | |||||
{{if .PageIsComparePull}} | |||||
{{.i18n.Tr "repo.pulls.create"}} | |||||
{{else}} | |||||
{{.i18n.Tr "repo.issues.create"}} | |||||
{{end}} | |||||
</button> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="ui divider"></div> | |||||
<div class="four wide column"> | |||||
<div class="ui segment metas"> | |||||
{{template "repo/issue/branch_selector_field" .}} | |||||
<input id="milestone_id" name="milestone_id" type="hidden" value="{{.milestone_id}}"> | |||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-milestone dropdown"> | |||||
<span class="text"> | |||||
<strong>{{.i18n.Tr "repo.issues.new.milestone"}}</strong> | |||||
{{if .HasIssuesOrPullsWritePermission}} | |||||
{{svg "octicon-gear" 16}} | |||||
{{end}} | |||||
</span> | |||||
<div class="menu"> | |||||
<div class="header" style="text-transform: none;font-size:16px;">{{.i18n.Tr "repo.issues.new.add_milestone_title"}}</div> | |||||
{{if or .OpenMilestones .ClosedMilestones}} | |||||
<div class="ui icon search input"> | |||||
<i class="search icon"></i> | |||||
<input type="text" placeholder="{{.i18n.Tr "repo.issues.filter_milestones"}}"> | |||||
</div> | |||||
{{end}} | |||||
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_milestone"}}</div> | |||||
{{if and (not .OpenMilestones) (not .ClosedMilestones)}} | |||||
<div class="header" style="text-transform: none;font-size:14px;"> | |||||
{{.i18n.Tr "repo.issues.new.no_items"}} | |||||
</div> | |||||
{{else}} | |||||
{{if .OpenMilestones}} | |||||
<div class="divider"></div> | |||||
<div class="header"> | |||||
{{svg "octicon-milestone" 16}} | |||||
{{.i18n.Tr "repo.issues.new.open_milestone"}} | |||||
</div> | |||||
{{range .OpenMilestones}} | |||||
<div class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{.Name}}</div> | |||||
{{end}} | |||||
{{end}} | |||||
{{if .ClosedMilestones}} | |||||
<div class="divider"></div> | |||||
<div class="header"> | |||||
{{svg "octicon-milestone" 16}} | |||||
{{.i18n.Tr "repo.issues.new.closed_milestone"}} | |||||
</div> | |||||
{{range .ClosedMilestones}} | |||||
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{.Name}}</a> | |||||
{{end}} | |||||
{{end}} | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui select-milestone list"> | |||||
<span class="no-select item {{if .Milestone}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_milestone"}}</span> | |||||
<div class="selected"> | |||||
{{if .Milestone}} | |||||
<a class="item" href="{{.RepoLink}}/issues?milestone={{.Milestone.ID}}"> {{.Milestone.Name}}</a> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<input id="label_ids" name="label_ids" type="hidden" value="{{.label_ids}}"> | |||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-label dropdown"> | |||||
<span class="text"> | |||||
<strong>{{.i18n.Tr "repo.issues.new.labels"}}</strong> | |||||
{{if .HasIssuesOrPullsWritePermission}} | |||||
{{svg "octicon-gear" 16}} | |||||
{{end}} | |||||
</span> | |||||
<div class="filter menu" data-id="#label_ids"> | |||||
<div class="header" style="text-transform: none;font-size:16px;">{{.i18n.Tr "repo.issues.new.add_labels_title"}}</div> | |||||
{{if or .Labels .OrgLabels}} | |||||
<div class="ui icon search input"> | |||||
<i class="search icon"></i> | |||||
<input type="text" placeholder="{{.i18n.Tr "repo.issues.filter_labels"}}"> | |||||
</div> | |||||
{{end}} | |||||
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_labels"}}</div> | |||||
{{if or .Labels .OrgLabels}} | |||||
{{range .Labels}} | |||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check" 16}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} | |||||
{{if .Description }}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> | |||||
{{end}} | |||||
<div class="ui divider"></div> | |||||
{{range .OrgLabels}} | |||||
<a class="{{if .IsChecked}}checked{{end}} item" href="#" data-id="{{.ID}}" data-id-selector="#label_{{.ID}}"><span class="octicon-check {{if not .IsChecked}}invisible{{end}}">{{svg "octicon-check" 16}}</span><span class="label color" style="background-color: {{.Color}}"></span> {{.Name | RenderEmoji}} | |||||
{{if .Description }}<br><small class="desc">{{.Description | RenderEmoji}}</small>{{end}}</a> | |||||
{{end}} | |||||
{{else}} | |||||
<div class="header" style="text-transform: none;font-size:14px;">{{.i18n.Tr "repo.issues.new.no_items"}}</div> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui labels list"> | |||||
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_label"}}</span> | |||||
{{range .Labels}} | |||||
<a class="{{if not .IsChecked}}hide{{end}} item" id="label_{{.ID}}" href="{{$.RepoLink}}/issues?labels={{.ID}}"><span class="label color" style="background-color: {{.Color}}"></span> <span class="text">{{.Name | RenderEmoji}}</span></a> | |||||
{{end}} | |||||
{{range .OrgLabels}} | |||||
<a class="{{if not .IsChecked}}hide{{end}} item" id="label_{{.ID}}" href="/issues?labels={{.ID}}"><span class="label color" style="background-color: {{.Color}}"></span> <span class="text">{{.Name | RenderEmoji}}</span></a> | |||||
{{end}} | |||||
</div> | |||||
<div class="ui divider"></div> | |||||
<div class="ui divider"></div> | |||||
<input id="assignee_ids" name="assignee_ids" type="hidden" value="{{.assignee_ids}}"> | |||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-assignees dropdown"> | |||||
<span class="text"> | |||||
<strong>{{.i18n.Tr "repo.issues.new.assignees"}}</strong> | |||||
{{if .HasIssuesOrPullsWritePermission}} | |||||
{{svg "octicon-gear" 16}} | |||||
{{end}} | |||||
</span> | |||||
<div class="filter menu" data-id="#assignee_ids"> | |||||
<div class="header" style="text-transform: none;font-size:16px;">{{.i18n.Tr "repo.issues.new.add_assignees_title"}}</div> | |||||
<div class="ui icon search input"> | |||||
<i class="search icon"></i> | |||||
<input type="text" placeholder="{{.i18n.Tr "repo.issues.filter_assignees"}}"> | |||||
</div> | |||||
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignees"}}</div> | |||||
{{range .Assignees}} | |||||
<a class="item" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}"> | |||||
<span class="octicon-check invisible">{{svg "octicon-check" 16}}</span> | |||||
<span class="text"> | |||||
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{.GetDisplayName}} | |||||
</span> | |||||
</a> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui assignees list"> | |||||
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}"> | |||||
{{.i18n.Tr "repo.issues.new.no_assignees"}} | |||||
</span> | |||||
{{range .Assignees}} | |||||
<a style="padding: 5px;color:rgba(0, 0, 0, 0.87);" class="hide item" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}"> | |||||
<img class="ui avatar image" src="{{.RelAvatarLink}}" style="vertical-align: middle;"> {{.GetDisplayName}} | |||||
</a> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<input id="milestone_id" name="milestone_id" type="hidden" value="{{.milestone_id}}"> | |||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-milestone dropdown"> | |||||
<span class="text"> | |||||
<strong>{{.i18n.Tr "repo.issues.new.milestone"}}</strong> | |||||
{{if .HasIssuesOrPullsWritePermission}} | |||||
{{svg "octicon-gear" 16}} | |||||
{{end}} | |||||
</span> | |||||
<div class="menu"> | |||||
<div class="header" style="text-transform: none;font-size:16px;">{{.i18n.Tr "repo.issues.new.add_milestone_title"}}</div> | |||||
{{if or .OpenMilestones .ClosedMilestones}} | |||||
<div class="ui icon search input"> | |||||
<i class="search icon"></i> | |||||
<input type="text" placeholder="{{.i18n.Tr "repo.issues.filter_milestones"}}"> | |||||
</div> | |||||
{{end}} | |||||
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_milestone"}}</div> | |||||
{{if and (not .OpenMilestones) (not .ClosedMilestones)}} | |||||
<div class="header" style="text-transform: none;font-size:14px;"> | |||||
{{.i18n.Tr "repo.issues.new.no_items"}} | |||||
</div> | |||||
{{else}} | |||||
{{if .OpenMilestones}} | |||||
<div class="divider"></div> | |||||
<div class="header"> | |||||
{{svg "octicon-milestone" 16}} | |||||
{{.i18n.Tr "repo.issues.new.open_milestone"}} | |||||
</div> | |||||
{{range .OpenMilestones}} | |||||
<div class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{.Name}}</div> | |||||
{{end}} | |||||
{{end}} | |||||
{{if .ClosedMilestones}} | |||||
<div class="divider"></div> | |||||
<div class="header"> | |||||
{{svg "octicon-milestone" 16}} | |||||
{{.i18n.Tr "repo.issues.new.closed_milestone"}} | |||||
</div> | |||||
{{range .ClosedMilestones}} | |||||
<a class="item" data-id="{{.ID}}" data-href="{{$.RepoLink}}/issues?milestone={{.ID}}"> {{.Name}}</a> | |||||
{{end}} | |||||
{{end}} | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui select-milestone list"> | |||||
<span class="no-select item {{if .Milestone}}hide{{end}}">{{.i18n.Tr "repo.issues.new.no_milestone"}}</span> | |||||
<div class="selected"> | |||||
{{if .Milestone}} | |||||
<a class="item" href="{{.RepoLink}}/issues?milestone={{.Milestone.ID}}"> {{.Milestone.Name}}</a> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui divider"></div> | |||||
<input id="assignee_ids" name="assignee_ids" type="hidden" value="{{.assignee_ids}}"> | |||||
<div class="ui {{if not .HasIssuesOrPullsWritePermission}}disabled{{end}} floating jump select-assignees dropdown"> | |||||
<span class="text"> | |||||
<strong>{{.i18n.Tr "repo.issues.new.assignees"}}</strong> | |||||
{{if .HasIssuesOrPullsWritePermission}} | |||||
{{svg "octicon-gear" 16}} | |||||
{{end}} | |||||
</span> | |||||
<div class="filter menu" data-id="#assignee_ids"> | |||||
<div class="header" style="text-transform: none;font-size:16px;">{{.i18n.Tr "repo.issues.new.add_assignees_title"}}</div> | |||||
<div class="ui icon search input"> | |||||
<i class="search icon"></i> | |||||
<input type="text" placeholder="{{.i18n.Tr "repo.issues.filter_assignees"}}"> | |||||
</div> | |||||
<div class="no-select item">{{.i18n.Tr "repo.issues.new.clear_assignees"}}</div> | |||||
{{range .Assignees}} | |||||
<a class="item" href="#" data-id="{{.ID}}" data-id-selector="#assignee_{{.ID}}"> | |||||
<span class="octicon-check invisible">{{svg "octicon-check" 16}}</span> | |||||
<span class="text"> | |||||
<img class="ui avatar image" src="{{.RelAvatarLink}}"> {{.GetDisplayName}} | |||||
</span> | |||||
</a> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
<div class="ui assignees list"> | |||||
<span class="no-select item {{if .HasSelectedLabel}}hide{{end}}"> | |||||
{{.i18n.Tr "repo.issues.new.no_assignees"}} | |||||
</span> | |||||
{{range .Assignees}} | |||||
<a style="padding: 5px;color:rgba(0, 0, 0, 0.87);" class="hide item" id="assignee_{{.ID}}" href="{{$.RepoLink}}/issues?assignee={{.ID}}"> | |||||
<img class="ui avatar image" src="{{.RelAvatarLink}}" style="vertical-align: middle;"> {{.GetDisplayName}} | |||||
</a> | |||||
{{end}} | |||||
</div> | |||||
</div> | |||||
</div> | |||||
</form> | </form> | ||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> | |||||
<script> | |||||
// $(document).ready(function(){ | |||||
// var reward_value = $('.ui.form').form('get value', 'dog') | |||||
// var reward_value = $('form').form('get value', 'dog') | |||||
// console.log(reward_value) | |||||
// alert(reward_value) | |||||
// $('.ui.green.button').click(function(){ | |||||
// $('.ui.form') | |||||
// .form({ | |||||
// // on: 'blur', | |||||
// inline: true, | |||||
// fields: { | |||||
// dog: { | |||||
// identifier: 'dog', | |||||
// rules: [ | |||||
// { | |||||
// type: 'empty', | |||||
// prompt: '请您输入项目奖励' | |||||
// }, | |||||
// { | |||||
// type : 'integer[0..100]', | |||||
// prompt : '项目奖励必须为整数,请您输入有效奖励金额' | |||||
// } | |||||
// ] | |||||
// } | |||||
// }, | |||||
// onFailure: function(e){ | |||||
// return false; | |||||
// } | |||||
// }); | |||||
// }); | |||||
// </script> | |||||
{{if .PageIsComparePull}} | {{if .PageIsComparePull}} | ||||
<script>window.wipPrefixes = {{.PullRequestWorkInProgressPrefixes}}</script> | |||||
<script>window.wipPrefixes = {{.PullRequestWorkInProgressPrefixes}}</script> | |||||
{{end}} | {{end}} | ||||
@@ -1,3 +1,9 @@ | |||||
<style> | |||||
.ui.selection.dropdown { | |||||
padding-top: 0em; | |||||
padding-bottom: 0em; | |||||
} | |||||
</style> | |||||
{{if gt (len .PullReviewers) 0}} | {{if gt (len .PullReviewers) 0}} | ||||
<div class="comment box"> | <div class="comment box"> | ||||
<div class="content"> | <div class="content"> | ||||
@@ -58,6 +64,8 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
{{end}} | {{end}} | ||||
<!-- 合并请求 --> | |||||
<div class="timeline-item comment merge box"> | <div class="timeline-item comment merge box"> | ||||
<a class="timeline-avatar text {{if .Issue.PullRequest.HasMerged}}purple | <a class="timeline-avatar text {{if .Issue.PullRequest.HasMerged}}purple | ||||
{{- else if .Issue.IsClosed}}grey | {{- else if .Issue.IsClosed}}grey | ||||
@@ -188,6 +196,8 @@ | |||||
{{$.i18n.Tr (printf "repo.signing.wont_sign.%s" .WontSignReason) }} | {{$.i18n.Tr (printf "repo.signing.wont_sign.%s" .WontSignReason) }} | ||||
</div> | </div> | ||||
{{end}} | {{end}} | ||||
<!-- 合并请求 --> | |||||
{{if .AllowMerge}} | {{if .AllowMerge}} | ||||
{{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} | {{$prUnit := .Repository.MustGetUnit $.UnitTypePullRequests}} | ||||
{{$approvers := .Issue.PullRequest.GetApprovers}} | {{$approvers := .Issue.PullRequest.GetApprovers}} | ||||
@@ -197,6 +207,21 @@ | |||||
<div class="ui form merge-fields" style="display: none"> | <div class="ui form merge-fields" style="display: none"> | ||||
<form action="{{.Link}}/merge" method="post"> | <form action="{{.Link}}/merge" method="post"> | ||||
{{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
<!-- 下拉框 --> | |||||
<div class="field"> | |||||
<div class="ui dropdown selection"> | |||||
<input type="hidden" name="block_chain_amount"> | |||||
<div class="default text">项目奖励</div> | |||||
<i class="dropdown icon"></i> | |||||
<div class="menu" > | |||||
<div class="item" data-value=0>0</div> | |||||
<div class="item" data-value=100>100</div> | |||||
<div class="item" data-value=200>200</div> | |||||
<div class="item" data-value=300>300</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="field"> | <div class="field"> | ||||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> | <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> | ||||
</div> | </div> | ||||
@@ -206,7 +231,7 @@ | |||||
<button class="ui green button" type="submit" name="do" value="merge"> | <button class="ui green button" type="submit" name="do" value="merge"> | ||||
{{$.i18n.Tr "repo.pulls.merge_pull_request"}} | {{$.i18n.Tr "repo.pulls.merge_pull_request"}} | ||||
</button> | </button> | ||||
<button class="ui button merge-cancel"> | |||||
<button class="ui button merge-cancel" onclick="$('.ui.form.merge-fields')[0].reset();"> | |||||
{{$.i18n.Tr "cancel"}} | {{$.i18n.Tr "cancel"}} | ||||
</button> | </button> | ||||
</form> | </form> | ||||
@@ -216,6 +241,21 @@ | |||||
<div class="ui form rebase-fields" style="display: none"> | <div class="ui form rebase-fields" style="display: none"> | ||||
<form action="{{.Link}}/merge" method="post"> | <form action="{{.Link}}/merge" method="post"> | ||||
{{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
<!-- 下拉框 --> | |||||
<div class="field"> | |||||
<div class="ui dropdown selection"> | |||||
<input type="hidden" name="block_chain_amount"> | |||||
<div class="default text">项目奖励</div> | |||||
<i class="dropdown icon"></i> | |||||
<div class="menu" > | |||||
<div class="item" data-value=0>0</div> | |||||
<div class="item" data-value=100>100</div> | |||||
<div class="item" data-value=200>200</div> | |||||
<div class="item" data-value=300>300</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<button class="ui green button" type="submit" name="do" value="rebase"> | <button class="ui green button" type="submit" name="do" value="rebase"> | ||||
{{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}} | {{$.i18n.Tr "repo.pulls.rebase_merge_pull_request"}} | ||||
</button> | </button> | ||||
@@ -229,6 +269,21 @@ | |||||
<div class="ui form rebase-merge-fields" style="display: none"> | <div class="ui form rebase-merge-fields" style="display: none"> | ||||
<form action="{{.Link}}/merge" method="post"> | <form action="{{.Link}}/merge" method="post"> | ||||
{{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
<!-- 下拉框 --> | |||||
<div class="field"> | |||||
<div class="ui dropdown selection"> | |||||
<input type="hidden" name="block_chain_amount"> | |||||
<div class="default text">项目奖励</div> | |||||
<i class="dropdown icon"></i> | |||||
<div class="menu" > | |||||
<div class="item" data-value=0>0</div> | |||||
<div class="item" data-value=100>100</div> | |||||
<div class="item" data-value=200>200</div> | |||||
<div class="item" data-value=300>300</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="field"> | <div class="field"> | ||||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> | <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultMergeMessage}}"> | ||||
</div> | </div> | ||||
@@ -248,6 +303,21 @@ | |||||
<div class="ui form squash-fields" style="display: none"> | <div class="ui form squash-fields" style="display: none"> | ||||
<form action="{{.Link}}/merge" method="post"> | <form action="{{.Link}}/merge" method="post"> | ||||
{{.CsrfTokenHtml}} | {{.CsrfTokenHtml}} | ||||
<!-- 下拉框 --> | |||||
<div class="field"> | |||||
<div class="ui dropdown selection"> | |||||
<input type="hidden" name="block_chain_amount"> | |||||
<div class="default text">项目奖励</div> | |||||
<i class="dropdown icon"></i> | |||||
<div class="menu" > | |||||
<div class="item" data-value=0>0</div> | |||||
<div class="item" data-value=100>100</div> | |||||
<div class="item" data-value=200>200</div> | |||||
<div class="item" data-value=300>300</div> | |||||
</div> | |||||
</div> | |||||
</div> | |||||
<div class="field"> | <div class="field"> | ||||
<input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}"> | <input type="text" name="merge_title_field" value="{{.Issue.PullRequest.GetDefaultSquashMessage}}"> | ||||
</div> | </div> | ||||
@@ -377,3 +447,34 @@ | |||||
</div> | </div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
<script src="https://cdn.jsdelivr.net/npm/jquery@3.2.1/dist/jquery.min.js"></script> | |||||
<script> | |||||
$(document) | |||||
.ready(function() { | |||||
$('.ui.dropdown.selection').dropdown(); | |||||
$('.ui.green.button').click(function(){ | |||||
$('.ui.form') | |||||
.form({ | |||||
on: 'blur', | |||||
inline: true, | |||||
fields: { | |||||
block_chain_amount: { | |||||
identifier: 'block_chain_amount', | |||||
rules: [ | |||||
{ | |||||
type : 'empty', | |||||
prompt : '项目奖励不能为空' | |||||
} | |||||
] | |||||
} | |||||
}, | |||||
onFailure: function(e){ | |||||
return false; | |||||
} | |||||
}); | |||||
}); | |||||
}) | |||||
</script> |