forked from Gitlink/gitea-1156
commit
ac7cec05a1
|
@ -0,0 +1,4 @@
|
||||||
|
set go111module=on
|
||||||
|
set GOOS=linux
|
||||||
|
set GOARCH=amd64
|
||||||
|
go build -tags='bindata' -o release\linux\gitea
|
|
@ -0,0 +1,5 @@
|
||||||
|
set go111module=on
|
||||||
|
SET GOOS=darwin
|
||||||
|
SET GOARCH=amd64
|
||||||
|
go build -tags='bindata' -o release\mac\gitea
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "strings"
|
||||||
|
|
||||||
|
// GetFeedsOptions options for retrieving feeds
|
||||||
|
type GetContributorsOptionsExt struct {
|
||||||
|
RepoId int64
|
||||||
|
UserId int64
|
||||||
|
}
|
||||||
|
|
||||||
|
type ContributorsDto struct {
|
||||||
|
Contributions int64 `json:"contributions"`
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
Login string `json:"login"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
//Type string `json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContributors(opt GetContributorsOptionsExt) (interface{}, error) {
|
||||||
|
sql :=
|
||||||
|
`select a.act_user_id as id ,
|
||||||
|
u.name as login,
|
||||||
|
u.email as email,
|
||||||
|
count(act_user_id) as contributions
|
||||||
|
from action a
|
||||||
|
left join user u on a.act_user_id=u.id
|
||||||
|
where repo_id=? and user_id=?
|
||||||
|
group by repo_id,act_user_id `
|
||||||
|
|
||||||
|
result := make([]ContributorsDto, 0, 0)
|
||||||
|
err := x.SQL(sql, opt.RepoId, opt.UserId).Find(&result)
|
||||||
|
return result, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetGetActivityOptions struct {
|
||||||
|
FromDateUnix int64 `json:"-"`
|
||||||
|
ToDateUnix int64 `json:"-"`
|
||||||
|
FromDate string `json:"from_date"`
|
||||||
|
ToDate string `json:"to_date"`
|
||||||
|
Top int64 `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PlatformDTO struct {
|
||||||
|
Id int64 `json:"-"`
|
||||||
|
Name string `json:"-"`
|
||||||
|
TotalCount int64 `json:"total_count"`
|
||||||
|
ActiveCount int64 `json:"active_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//平台所需数据;
|
||||||
|
func GetActivity(opt *GetGetActivityOptions) (interface{}, error) {
|
||||||
|
sql := `select a.id,a.name,ifNull(b.active_count,0) as active_count,b.total_count
|
||||||
|
from ( select 11 as id ,'PullRequest' name
|
||||||
|
union
|
||||||
|
select 5 as id, 'Commit' name
|
||||||
|
) a
|
||||||
|
left join (
|
||||||
|
select op_type,count(op_type) as total_count,
|
||||||
|
sum(case when a.created_unix>=? and a.created_unix<=?
|
||||||
|
then 1 else 0 end
|
||||||
|
) as active_count
|
||||||
|
from action a
|
||||||
|
where (a.op_type=11 or a.op_type=5)
|
||||||
|
group by a.op_type
|
||||||
|
) b on a.id=b.op_type`
|
||||||
|
|
||||||
|
datalist := make([]PlatformDTO, 0, 0)
|
||||||
|
err := x.SQL(sql, opt.FromDateUnix, opt.ToDateUnix).Find(&datalist)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
convertMap := make(map[string]interface{})
|
||||||
|
for i := 0; i <= len(datalist)-1; i++ {
|
||||||
|
convertMap[strings.ToLower(datalist[i].Name)] = datalist[i]
|
||||||
|
}
|
||||||
|
return convertMap, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectDTO struct {
|
||||||
|
Id int64 `json:"-"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
TotalCount int64 `json:"total_count"`
|
||||||
|
ActiveCount int64 `json:"active_count"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//项目所需数据-按项目统计 top 5
|
||||||
|
func GetActivityProject(opt *GetGetActivityOptions) (interface{}, error) {
|
||||||
|
sql :=
|
||||||
|
`select repo_id as id,r.name,
|
||||||
|
count(op_type) as total_count,
|
||||||
|
sum(case when a.created_unix>=? and a.created_unix<=?
|
||||||
|
then 1 else 0 end
|
||||||
|
) as active_count
|
||||||
|
from action a
|
||||||
|
left join repository r on a.repo_id=r.id
|
||||||
|
where (a.op_type=5)
|
||||||
|
group by a.repo_id
|
||||||
|
order by total_count desc
|
||||||
|
limit ?
|
||||||
|
`
|
||||||
|
|
||||||
|
datalist := make([]ProjectDTO, 0, 0)
|
||||||
|
err := x.SQL(sql, opt.FromDateUnix, opt.ToDateUnix, opt.Top).Find(&datalist)
|
||||||
|
return datalist, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//项目所需数据-按开发者统计 top 5
|
||||||
|
func GetActivityDevelop(opt *GetGetActivityOptions) (interface{}, error) {
|
||||||
|
sql :=
|
||||||
|
`select u.name as develop_name,
|
||||||
|
count(op_type) as total_count,
|
||||||
|
sum(case when (a.created_unix>=? and a.created_unix<=?) then 1 else 0 end ) as active_count
|
||||||
|
from action a
|
||||||
|
left join user u on a.act_user_id=u.id
|
||||||
|
where (a.op_type=5)
|
||||||
|
group by a.act_user_id
|
||||||
|
order by total_count desc
|
||||||
|
limit ? `
|
||||||
|
|
||||||
|
datalist := make([]DevelopDTO, 0, 0)
|
||||||
|
err := x.SQL(sql, opt.FromDateUnix, opt.ToDateUnix, opt.Top).Find(&datalist)
|
||||||
|
return datalist, err
|
||||||
|
}
|
||||||
|
|
||||||
|
type DevelopDTO struct {
|
||||||
|
DevelopName string `json:"develop_name"`
|
||||||
|
TotalCount int64 `json:"total_count"`
|
||||||
|
ActiveCount int64 `json:"active_count"`
|
||||||
|
}
|
|
@ -69,6 +69,10 @@ type PullRequest struct {
|
||||||
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"`
|
MergedUnix timeutil.TimeStamp `xorm:"updated INDEX"`
|
||||||
|
|
||||||
isHeadRepoLoaded bool `xorm:"-"`
|
isHeadRepoLoaded bool `xorm:"-"`
|
||||||
|
|
||||||
|
// add configure
|
||||||
|
CommitNum int
|
||||||
|
ChangedFiles int
|
||||||
}
|
}
|
||||||
|
|
||||||
// MustHeadUserName returns the HeadRepo's username if failed return blank
|
// MustHeadUserName returns the HeadRepo's username if failed return blank
|
||||||
|
|
|
@ -242,6 +242,27 @@ func GetLatestReleaseByRepoID(repoID int64) (*Release, error) {
|
||||||
return rel, nil
|
return rel, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLatestReleaseByRepoID returns the latest release for a repository
|
||||||
|
func GetLatestReleaseByRepoIDExt(repoID int64) (*Release, error) {
|
||||||
|
cond := builder.NewCond().
|
||||||
|
And(builder.Eq{"repo_id": repoID}).
|
||||||
|
And(builder.Eq{"is_draft": false}).
|
||||||
|
And(builder.Eq{"is_prerelease": false})
|
||||||
|
|
||||||
|
rel := new(Release)
|
||||||
|
has, err := x.
|
||||||
|
Desc("created_unix", "id").
|
||||||
|
Where(cond).
|
||||||
|
Get(rel)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
} else if !has {
|
||||||
|
return nil, ErrReleaseNotExist{0, "latest"}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rel, nil
|
||||||
|
}
|
||||||
|
|
||||||
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
|
// GetReleasesByRepoIDAndNames returns a list of releases of repository according repoID and tagNames.
|
||||||
func GetReleasesByRepoIDAndNames(ctx DBContext, repoID int64, tagNames []string) (rels []*Release, err error) {
|
func GetReleasesByRepoIDAndNames(ctx DBContext, repoID int64, tagNames []string) (rels []*Release, err error) {
|
||||||
err = ctx.e.
|
err = ctx.e.
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "code.gitea.io/gitea/modules/timeutil"
|
||||||
|
|
||||||
|
type TimestampOptions struct {
|
||||||
|
Start timeutil.TimeStamp
|
||||||
|
End timeutil.TimeStamp
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opts *TimestampOptions) setDefaultValues() {
|
||||||
|
if opts.Start <= 0 {
|
||||||
|
opts.Start = timeutil.TimeStampNow() - 365*24*60*60
|
||||||
|
}
|
||||||
|
if opts.End <= 0 {
|
||||||
|
opts.End = timeutil.TimeStampNow()
|
||||||
|
}
|
||||||
|
}
|
|
@ -67,3 +67,43 @@ func getUserHeatmapData(user *User, team *Team, doer *User) ([]*UserHeatmapData,
|
||||||
OrderBy("timestamp").
|
OrderBy("timestamp").
|
||||||
Find(&hdata)
|
Find(&hdata)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserHeatMapDataByUserWithTimeStamp(user *User, opts TimestampOptions) ([]*UserHeatmapData, error) {
|
||||||
|
opts.setDefaultValues() // Setting the defaule value.
|
||||||
|
|
||||||
|
hdata := make([]*UserHeatmapData, 0) // initial value
|
||||||
|
if user.KeepActivityPrivate {
|
||||||
|
return hdata, nil
|
||||||
|
}
|
||||||
|
var groupBy string
|
||||||
|
var groupByName = "timestamp"
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case setting.Database.UseSQLite3:
|
||||||
|
groupBy = "strftime('%s', strftime('%Y-%m-%d', created_unix, 'unixepoch'))"
|
||||||
|
|
||||||
|
case setting.Database.UseMySQL:
|
||||||
|
groupBy = "UNIX_TIMESTAMP(DATE(FROM_UNIXTIME(created_unix)))"
|
||||||
|
|
||||||
|
case setting.Database.UsePostgreSQL:
|
||||||
|
groupBy = "extract(epoch from data_trunc('day', to_timestamp(created_unix)))"
|
||||||
|
|
||||||
|
case setting.Database.UseMSSQL:
|
||||||
|
// groupBy = "datediff(SECOND, '19700101', dateadd(DAY, 0, datediff(day, 0, dateadd(s, created_unix, '19700101'))))"
|
||||||
|
groupBy = "datediff(SECOND, '19700101', dateadd(DAY, 0, datediff(day, 0, dateadd(s, created_unix, '19700101'))))"
|
||||||
|
|
||||||
|
groupByName = groupBy
|
||||||
|
}
|
||||||
|
sess := x.Select(groupBy+"AS timestamp, count(user_id) as contributions").
|
||||||
|
Table("action").
|
||||||
|
Where("user_id = ?", user.ID).
|
||||||
|
And(groupBy+">= ? ", opts.Start).
|
||||||
|
And(groupBy+"<= ?", opts.End)
|
||||||
|
if user.Type == UserTypeIndividual {
|
||||||
|
sess = sess.And("act_user_id = ?", user.ID)
|
||||||
|
}
|
||||||
|
err := sess.GroupBy(groupByName).
|
||||||
|
OrderBy("timestamp").
|
||||||
|
Find(&hdata)
|
||||||
|
return hdata, err
|
||||||
|
}
|
||||||
|
|
|
@ -77,6 +77,13 @@ func IsValidHookContentType(name string) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsValidHookHttpMethod(name string) bool {
|
||||||
|
if name == "POST" || name == "GET" {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// HookEvents is a set of web hook events
|
// HookEvents is a set of web hook events
|
||||||
type HookEvents struct {
|
type HookEvents struct {
|
||||||
Create bool `json:"create"`
|
Create bool `json:"create"`
|
||||||
|
@ -151,6 +158,7 @@ type Webhook struct {
|
||||||
Type HookType `xorm:"VARCHAR(16) 'type'"`
|
Type HookType `xorm:"VARCHAR(16) 'type'"`
|
||||||
Meta string `xorm:"TEXT"` // store hook-specific attributes
|
Meta string `xorm:"TEXT"` // store hook-specific attributes
|
||||||
LastStatus HookStatus // Last delivery status
|
LastStatus HookStatus // Last delivery status
|
||||||
|
BranchFilter string `xorm:"TEXT"`
|
||||||
|
|
||||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||||
|
@ -647,16 +655,23 @@ type HookResponse struct {
|
||||||
|
|
||||||
// HookTask represents a hook task.
|
// HookTask represents a hook task.
|
||||||
type HookTask struct {
|
type HookTask struct {
|
||||||
ID int64 `xorm:"pk autoincr"`
|
ID int64 `xorm:"pk autoincr"`
|
||||||
RepoID int64 `xorm:"INDEX"`
|
RepoID int64 `xorm:"INDEX"`
|
||||||
HookID int64
|
HookID int64
|
||||||
UUID string
|
UUID string
|
||||||
|
|
||||||
|
Type HookType
|
||||||
|
URL string `xorm:"TEXT"`
|
||||||
|
Signature string `xorm:"TEXT"`
|
||||||
|
HTTPMethod string `xorm:"http_method"`
|
||||||
|
ContentType HookContentType
|
||||||
api.Payloader `xorm:"-"`
|
api.Payloader `xorm:"-"`
|
||||||
PayloadContent string `xorm:"TEXT"`
|
PayloadContent string `xorm:"TEXT"`
|
||||||
EventType HookEventType
|
EventType HookEventType
|
||||||
IsDelivered bool
|
IsDelivered bool
|
||||||
Delivered int64
|
Delivered int64
|
||||||
DeliveredString string `xorm:"-"`
|
DeliveredString string `xorm:"-"`
|
||||||
|
IsSSL bool
|
||||||
|
|
||||||
// History info.
|
// History info.
|
||||||
IsSucceed bool
|
IsSucceed bool
|
||||||
|
@ -708,6 +723,18 @@ func (t *HookTask) simpleMarshalJSON(v interface{}) string {
|
||||||
return string(p)
|
return string(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetHookTasksByRepoIDAndHookID(repoID int64, hookID int64, listOptions ListOptions) ([]*HookTask, error) {
|
||||||
|
if listOptions.Page == 0 {
|
||||||
|
hookTasks := make([]*HookTask, 0, 5)
|
||||||
|
return hookTasks, x.Find(&hookTasks, &HookTask{RepoID: repoID, HookID: hookID})
|
||||||
|
}
|
||||||
|
|
||||||
|
sess := listOptions.getPaginatedSession()
|
||||||
|
hookTasks := make([]*HookTask, 0, listOptions.PageSize)
|
||||||
|
|
||||||
|
return hookTasks, sess.Find(&hookTasks, &HookTask{RepoID: repoID, HookID: hookID})
|
||||||
|
}
|
||||||
|
|
||||||
// HookTasks returns a list of hook tasks by given conditions.
|
// HookTasks returns a list of hook tasks by given conditions.
|
||||||
func HookTasks(hookID int64, page int) ([]*HookTask, error) {
|
func HookTasks(hookID int64, page int) ([]*HookTask, error) {
|
||||||
tasks := make([]*HookTask, 0, setting.Webhook.PagingNum)
|
tasks := make([]*HookTask, 0, setting.Webhook.PagingNum)
|
||||||
|
|
|
@ -105,6 +105,51 @@ func (ctx *APIContext) Error(status int, title string, obj interface{}) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *APIContext) FileNameError(objs ...interface{}) {
|
||||||
|
var message = "FileName too long"
|
||||||
|
var errors []string
|
||||||
|
|
||||||
|
for _, obj := range objs {
|
||||||
|
// Ignore nil
|
||||||
|
if obj == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := obj.(error); ok {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
} else {
|
||||||
|
message = obj.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.JSON(500, map[string]interface{}{
|
||||||
|
"message": message,
|
||||||
|
"documentation_url": setting.API.SwaggerURL,
|
||||||
|
"errors": errors,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
func (ctx *APIContext) FileExistError(objs ...interface{}) {
|
||||||
|
var message = "file does not exist"
|
||||||
|
var errors []string
|
||||||
|
|
||||||
|
for _, obj := range objs {
|
||||||
|
// Ignore nil
|
||||||
|
if obj == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if err, ok := obj.(error); ok {
|
||||||
|
errors = append(errors, err.Error())
|
||||||
|
} else {
|
||||||
|
message = obj.(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.JSON(500, map[string]interface{}{
|
||||||
|
"message": message,
|
||||||
|
"documentation_url": setting.API.SwaggerURL,
|
||||||
|
"errors": errors,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// InternalServerError responds with an error message to the client with the error as a message
|
// InternalServerError responds with an error message to the client with the error as a message
|
||||||
// and the file and line of the caller.
|
// and the file and line of the caller.
|
||||||
func (ctx *APIContext) InternalServerError(err error) {
|
func (ctx *APIContext) InternalServerError(err error) {
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
package convert
|
package convert
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -29,8 +30,18 @@ func ToEmail(email *models.EmailAddress) *api.Email {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
None BranchKind = iota
|
||||||
|
DefaultBranch
|
||||||
|
ProtectedBranch
|
||||||
|
OtherBranch
|
||||||
|
)
|
||||||
|
|
||||||
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
// ToBranch convert a git.Commit and git.Branch to an api.Branch
|
||||||
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User, isRepoAdmin bool) (*api.Branch, error) {
|
func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.ProtectedBranch, user *models.User, isRepoAdmin bool) (*api.Branch, error) {
|
||||||
|
var branchKind BranchKind
|
||||||
if bp == nil {
|
if bp == nil {
|
||||||
var hasPerm bool
|
var hasPerm bool
|
||||||
var err error
|
var err error
|
||||||
|
@ -41,6 +52,12 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if b.Name == repo.DefaultBranch {
|
||||||
|
branchKind = DefaultBranch
|
||||||
|
} else {
|
||||||
|
branchKind = OtherBranch
|
||||||
|
}
|
||||||
|
|
||||||
return &api.Branch{
|
return &api.Branch{
|
||||||
Name: b.Name,
|
Name: b.Name,
|
||||||
Commit: ToPayloadCommit(repo, c),
|
Commit: ToPayloadCommit(repo, c),
|
||||||
|
@ -50,16 +67,30 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
|
||||||
StatusCheckContexts: []string{},
|
StatusCheckContexts: []string{},
|
||||||
UserCanPush: hasPerm,
|
UserCanPush: hasPerm,
|
||||||
UserCanMerge: hasPerm,
|
UserCanMerge: hasPerm,
|
||||||
|
|
||||||
|
CommitID: c.ID.String(), //add configure
|
||||||
|
CommitTime: c.Author.When.Format(time.RFC3339),
|
||||||
|
DefaultBranch: repo.DefaultBranch,
|
||||||
|
BranchKind: int(branchKind),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
if b.Name == repo.DefaultBranch {
|
||||||
|
branchKind = DefaultBranch
|
||||||
|
} else {
|
||||||
|
branchKind = ProtectedBranch
|
||||||
|
}
|
||||||
branch := &api.Branch{
|
branch := &api.Branch{
|
||||||
Name: b.Name,
|
Name: b.Name,
|
||||||
|
CommitID: c.ID.String(), //add configure
|
||||||
Commit: ToPayloadCommit(repo, c),
|
Commit: ToPayloadCommit(repo, c),
|
||||||
Protected: true,
|
Protected: true,
|
||||||
RequiredApprovals: bp.RequiredApprovals,
|
RequiredApprovals: bp.RequiredApprovals,
|
||||||
EnableStatusCheck: bp.EnableStatusCheck,
|
EnableStatusCheck: bp.EnableStatusCheck,
|
||||||
StatusCheckContexts: bp.StatusCheckContexts,
|
StatusCheckContexts: bp.StatusCheckContexts,
|
||||||
|
|
||||||
|
CommitTime: c.Author.When.Format(time.RFC3339),
|
||||||
|
DefaultBranch: repo.DefaultBranch,
|
||||||
|
BranchKind: int(branchKind),
|
||||||
}
|
}
|
||||||
|
|
||||||
if isRepoAdmin {
|
if isRepoAdmin {
|
||||||
|
@ -138,12 +169,27 @@ func ToTag(repo *models.Repository, t *git.Tag) *api.Tag {
|
||||||
Name: t.Name,
|
Name: t.Name,
|
||||||
Message: strings.TrimSpace(t.Message),
|
Message: strings.TrimSpace(t.Message),
|
||||||
ID: t.ID.String(),
|
ID: t.ID.String(),
|
||||||
Commit: ToCommitMeta(repo, t),
|
Commit: ToTagCommit(repo, t),
|
||||||
|
Tagger: ToCommitUser(t.Tagger),
|
||||||
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
|
ZipballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".zip"),
|
||||||
TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
|
TarballURL: util.URLJoin(repo.HTMLURL(), "archive", t.Name+".tar.gz"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToTagCommit(repo *models.Repository, t *git.Tag) *api.TagCommit {
|
||||||
|
commit, err := t.Commit()
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Commit", err)
|
||||||
|
return &api.TagCommit{}
|
||||||
|
}
|
||||||
|
return &api.TagCommit{
|
||||||
|
CommitMeta: ToCommitMeta(repo, t),
|
||||||
|
Commiter: ToCommitUser(commit.Committer),
|
||||||
|
Author: ToCommitUser(commit.Author),
|
||||||
|
Message: commit.CommitMessage,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
|
// ToVerification convert a git.Commit.Signature to an api.PayloadCommitVerification
|
||||||
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
|
func ToVerification(c *git.Commit) *api.PayloadCommitVerification {
|
||||||
verif := models.ParseCommitWithSignature(c)
|
verif := models.ParseCommitWithSignature(c)
|
||||||
|
@ -228,6 +274,7 @@ func ToHook(repoLink string, w *models.Webhook) *api.Hook {
|
||||||
config := map[string]string{
|
config := map[string]string{
|
||||||
"url": w.URL,
|
"url": w.URL,
|
||||||
"content_type": w.ContentType.Name(),
|
"content_type": w.ContentType.Name(),
|
||||||
|
"http_method": w.HTTPMethod,
|
||||||
}
|
}
|
||||||
if w.Type == models.SLACK {
|
if w.Type == models.SLACK {
|
||||||
s := webhook.GetSlackHook(w)
|
s := webhook.GetSlackHook(w)
|
||||||
|
@ -238,14 +285,45 @@ func ToHook(repoLink string, w *models.Webhook) *api.Hook {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.Hook{
|
return &api.Hook{
|
||||||
ID: w.ID,
|
ID: w.ID,
|
||||||
Type: string(w.Type),
|
Type: string(w.Type),
|
||||||
URL: fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
|
URL: fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
|
||||||
Active: w.IsActive,
|
Active: w.IsActive,
|
||||||
Config: config,
|
Config: config,
|
||||||
Events: w.EventsArray(),
|
Events: w.EventsArray(),
|
||||||
Updated: w.UpdatedUnix.AsTime(),
|
Updated: w.UpdatedUnix.AsTime(),
|
||||||
Created: w.CreatedUnix.AsTime(),
|
Created: w.CreatedUnix.AsTime(),
|
||||||
|
BranchFilter: w.HookEvent.BranchFilter,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToHookTask(t *models.HookTask) *api.HookTask {
|
||||||
|
config := map[string]string{
|
||||||
|
"url": t.URL,
|
||||||
|
"content_type": t.ContentType.Name(),
|
||||||
|
"http_method": t.HTTPMethod,
|
||||||
|
}
|
||||||
|
|
||||||
|
payloadContent := make(map[string]interface{})
|
||||||
|
requestContent := make(map[string]interface{})
|
||||||
|
responseContent := make(map[string]interface{})
|
||||||
|
_ = json.Unmarshal([]byte(t.PayloadContent), &payloadContent)
|
||||||
|
_ = json.Unmarshal([]byte(t.RequestContent), &requestContent)
|
||||||
|
_ = json.Unmarshal([]byte(t.ResponseContent), &responseContent)
|
||||||
|
|
||||||
|
return &api.HookTask{
|
||||||
|
ID: t.ID,
|
||||||
|
UUID: t.UUID,
|
||||||
|
Type: string(t.Type),
|
||||||
|
Config: config,
|
||||||
|
PayloadContent: payloadContent,
|
||||||
|
EventType: string(t.EventType),
|
||||||
|
IsSSL: t.IsSSL,
|
||||||
|
IsDelivered: t.IsDelivered,
|
||||||
|
Delivered: t.Delivered,
|
||||||
|
IsSucceed: t.IsSucceed,
|
||||||
|
RequestContent: requestContent,
|
||||||
|
ResponseContent: responseContent,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,3 +438,18 @@ func ToLFSLock(l *models.LFSLock) *api.LFSLock {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ToOrganization convert models.User to api.Organization
|
||||||
|
func ToOrganizationExt(org *models.User) *api.OrganizationExt {
|
||||||
|
return &api.OrganizationExt{
|
||||||
|
ID: org.ID,
|
||||||
|
AvatarURL: org.AvatarLink(),
|
||||||
|
UserName: org.Name,
|
||||||
|
FullName: org.FullName,
|
||||||
|
Description: org.Description,
|
||||||
|
Website: org.Website,
|
||||||
|
Location: org.Location,
|
||||||
|
Visibility: org.Visibility.String(),
|
||||||
|
RepoAdminChangeTeamAccess: org.RepoAdminChangeTeamAccess,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -146,6 +146,7 @@ func ToCommit(repo *models.Repository, commit *git.Commit, userCache map[string]
|
||||||
}
|
}
|
||||||
|
|
||||||
return &api.Commit{
|
return &api.Commit{
|
||||||
|
CommitDate: commit.Committer.When.Format("2006-01-02"), // new time format, year-moon-day
|
||||||
CommitMeta: &api.CommitMeta{
|
CommitMeta: &api.CommitMeta{
|
||||||
URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
|
URL: repo.APIURL() + "/git/commits/" + commit.ID.String(),
|
||||||
SHA: commit.ID.String(),
|
SHA: commit.ID.String(),
|
||||||
|
|
|
@ -61,14 +61,17 @@ func ToAPIPullRequest(pr *models.PullRequest, doer *models.User) *api.PullReques
|
||||||
State: apiIssue.State,
|
State: apiIssue.State,
|
||||||
IsLocked: apiIssue.IsLocked,
|
IsLocked: apiIssue.IsLocked,
|
||||||
Comments: apiIssue.Comments,
|
Comments: apiIssue.Comments,
|
||||||
HTMLURL: pr.Issue.HTMLURL(),
|
|
||||||
DiffURL: pr.Issue.DiffURL(),
|
CommitNum: pr.CommitNum,
|
||||||
PatchURL: pr.Issue.PatchURL(),
|
ChangedFiles: pr.ChangedFiles,
|
||||||
HasMerged: pr.HasMerged,
|
HTMLURL: pr.Issue.HTMLURL(),
|
||||||
MergeBase: pr.MergeBase,
|
DiffURL: pr.Issue.DiffURL(),
|
||||||
Deadline: apiIssue.Deadline,
|
PatchURL: pr.Issue.PatchURL(),
|
||||||
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
HasMerged: pr.HasMerged,
|
||||||
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
MergeBase: pr.MergeBase,
|
||||||
|
Deadline: apiIssue.Deadline,
|
||||||
|
Created: pr.Issue.CreatedUnix.AsTimePtr(),
|
||||||
|
Updated: pr.Issue.UpdatedUnix.AsTimePtr(),
|
||||||
|
|
||||||
Base: &api.PRBranchInfo{
|
Base: &api.PRBranchInfo{
|
||||||
Name: pr.BaseBranch,
|
Name: pr.BaseBranch,
|
||||||
|
|
|
@ -37,3 +37,16 @@ func ToGitServiceType(value string) structs.GitServiceType {
|
||||||
return structs.PlainGitService
|
return structs.PlainGitService
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ToBranchType(index int) structs.BranchKind {
|
||||||
|
switch index {
|
||||||
|
case 1:
|
||||||
|
return structs.DefaultBranch
|
||||||
|
case 2:
|
||||||
|
return structs.ProtectedBranch
|
||||||
|
case 3:
|
||||||
|
return structs.OtherBranch
|
||||||
|
default:
|
||||||
|
return structs.None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -103,6 +103,31 @@ func GetBranchesByPath(path string, skip, limit int) ([]*Branch, int, error) {
|
||||||
return branches, countAll, nil
|
return branches, countAll, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetBranchesByPath returns a branch by it's path
|
||||||
|
func GetBranchesByPathNoLimit(path string) ([]*Branch, int, error) {
|
||||||
|
gitRepo, err := OpenRepository(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
brs, countAll, err := gitRepo.GetBranches(0, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
branches := make([]*Branch, len(brs))
|
||||||
|
for i := range brs {
|
||||||
|
branches[i] = &Branch{
|
||||||
|
Path: path,
|
||||||
|
Name: brs[i],
|
||||||
|
gitRepo: gitRepo,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return branches, countAll, nil
|
||||||
|
}
|
||||||
|
|
||||||
// DeleteBranchOptions Option(s) for delete branch
|
// DeleteBranchOptions Option(s) for delete branch
|
||||||
type DeleteBranchOptions struct {
|
type DeleteBranchOptions struct {
|
||||||
Force bool
|
Force bool
|
||||||
|
|
|
@ -200,6 +200,19 @@ func (repo *Repository) FileCommitsCount(revision, file string) (int64, error) {
|
||||||
return CommitsCountFiles(repo.Path, []string{revision}, []string{file})
|
return CommitsCountFiles(repo.Path, []string{revision}, []string{file})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFirstAndLastCommitByPath returns the first commit and the last commit of relative path.
|
||||||
|
func (repo *Repository) GetFirstAndLastCommitByPath(revision, relpath string) (*Commit, *Commit, error) {
|
||||||
|
stdout, err := NewCommand("log", revision, prettyLogFormat, "--", relpath).RunInDirBytes(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
commits, err := repo.parsePrettyFormatLogToList(stdout)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return commits.Front().Value.(*Commit), commits.Back().Value.(*Commit), nil
|
||||||
|
}
|
||||||
|
|
||||||
// CommitsByFileAndRange return the commits according revision file and the page
|
// CommitsByFileAndRange return the commits according revision file and the page
|
||||||
func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
|
func (repo *Repository) CommitsByFileAndRange(revision, file string, page int) (*list.List, error) {
|
||||||
skip := (page - 1) * setting.Git.CommitsRangeSize
|
skip := (page - 1) * setting.Git.CommitsRangeSize
|
||||||
|
|
|
@ -20,6 +20,16 @@ func IsTagExist(repoPath, name string) bool {
|
||||||
return IsReferenceExist(repoPath, TagPrefix+name)
|
return IsReferenceExist(repoPath, TagPrefix+name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (repo *Repository) GetTagCount() (int64, error) {
|
||||||
|
stdout, err := NewCommand("tag").RunInDir(repo.Path)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
tagNames := strings.Split(strings.TrimRight(stdout, "\n"), "\n")
|
||||||
|
return int64(len(tagNames)), nil
|
||||||
|
}
|
||||||
|
|
||||||
// CreateTag create one tag in the repository
|
// CreateTag create one tag in the repository
|
||||||
func (repo *Repository) CreateTag(name, revision string) error {
|
func (repo *Repository) CreateTag(name, revision string) error {
|
||||||
_, err := NewCommand("tag", "--", name, revision).RunInDir(repo.Path)
|
_, err := NewCommand("tag", "--", name, revision).RunInDir(repo.Path)
|
||||||
|
|
|
@ -64,15 +64,15 @@ func setupComplexity(values []string) {
|
||||||
}
|
}
|
||||||
if len(requiredList) == 0 {
|
if len(requiredList) == 0 {
|
||||||
// No valid character classes found; use all classes as default
|
// No valid character classes found; use all classes as default
|
||||||
for _, complex := range charComplexities {
|
// for _, complex := range charComplexities {
|
||||||
validChars += complex.ValidChars
|
// validChars += complex.ValidChars
|
||||||
requiredList = append(requiredList, complex)
|
// requiredList = append(requiredList, complex)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if validChars == "" {
|
if validChars == "" {
|
||||||
// No complexities to check; provide a sensible default for password generation
|
// No complexities to check; provide a sensible default for password generation
|
||||||
validChars = charComplexities["lower"].ValidChars + charComplexities["upper"].ValidChars + charComplexities["digit"].ValidChars
|
// validChars = charComplexities["lower"].ValidChars + charComplexities["upper"].ValidChars + charComplexities["digit"].ValidChars
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,10 @@ func GetBranches(repo *models.Repository, skip, limit int) ([]*git.Branch, int,
|
||||||
return git.GetBranchesByPath(repo.RepoPath(), skip, limit)
|
return git.GetBranchesByPath(repo.RepoPath(), skip, limit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetBranchesNoLimit(repo *models.Repository) ([]*git.Branch, int, error) {
|
||||||
|
return git.GetBranchesByPathNoLimit(repo.RepoPath())
|
||||||
|
}
|
||||||
|
|
||||||
// checkBranchName validates branch name with existing repository branches
|
// checkBranchName validates branch name with existing repository branches
|
||||||
func checkBranchName(repo *models.Repository, name string) error {
|
func checkBranchName(repo *models.Repository, name string) error {
|
||||||
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
gitRepo, err := git.OpenRepository(repo.RepoPath())
|
||||||
|
|
|
@ -20,18 +20,34 @@ var (
|
||||||
|
|
||||||
// Hook a hook is a web hook when one repository changed
|
// Hook a hook is a web hook when one repository changed
|
||||||
type Hook struct {
|
type Hook struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
URL string `json:"-"`
|
URL string `json:"-"`
|
||||||
Config map[string]string `json:"config"`
|
Config map[string]string `json:"config"`
|
||||||
Events []string `json:"events"`
|
Events []string `json:"events"`
|
||||||
Active bool `json:"active"`
|
Active bool `json:"active"`
|
||||||
|
BranchFilter string `json:"branch_filter"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
Updated time.Time `json:"updated_at"`
|
Updated time.Time `json:"updated_at"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
Created time.Time `json:"created_at"`
|
Created time.Time `json:"created_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type HookTask struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UUID string `json:"uuid"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Config map[string]string `json:"config"`
|
||||||
|
PayloadContent map[string]interface{} `json:"payload_content"`
|
||||||
|
EventType string `json:"event_type"`
|
||||||
|
IsSSL bool `json:"is_ssl"`
|
||||||
|
IsDelivered bool `json:"is_delivered"`
|
||||||
|
Delivered int64 `json:"delivered"`
|
||||||
|
IsSucceed bool `json:"is_succeed"`
|
||||||
|
RequestContent map[string]interface{} `json:"request_info"`
|
||||||
|
ResponseContent map[string]interface{} `json:"response_content"`
|
||||||
|
}
|
||||||
|
|
||||||
// HookList represents a list of API hook.
|
// HookList represents a list of API hook.
|
||||||
type HookList []*Hook
|
type HookList []*Hook
|
||||||
|
|
||||||
|
|
|
@ -44,3 +44,28 @@ type EditTeamOption struct {
|
||||||
Units []string `json:"units"`
|
Units []string `json:"units"`
|
||||||
CanCreateOrgRepo *bool `json:"can_create_org_repo"`
|
CanCreateOrgRepo *bool `json:"can_create_org_repo"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type OrganizationExt struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UserName string `json:"username"`
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
AvatarURL string `json:"avatar_url"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Visibility string `json:"visibility"`
|
||||||
|
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
|
||||||
|
OwnerTeam interface{} `json:"owner_team"` //团队关系;
|
||||||
|
}
|
||||||
|
|
||||||
|
type EditOrgOptionExt struct {
|
||||||
|
Name string `json:"name"` // 添加对name的修改,lower_name 其值跟随name变化;
|
||||||
|
FullName string `json:"full_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Website string `json:"website"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
// possible values are `public`, `limited` or `private`
|
||||||
|
// enum: public,limited,private
|
||||||
|
Visibility string `json:"visibility" binding:"In(,public,limited,private)"`
|
||||||
|
RepoAdminChangeTeamAccess bool `json:"repo_admin_change_team_access"`
|
||||||
|
}
|
||||||
|
|
|
@ -48,6 +48,9 @@ type PullRequest struct {
|
||||||
Updated *time.Time `json:"updated_at"`
|
Updated *time.Time `json:"updated_at"`
|
||||||
// swagger:strfmt date-time
|
// swagger:strfmt date-time
|
||||||
Closed *time.Time `json:"closed_at"`
|
Closed *time.Time `json:"closed_at"`
|
||||||
|
|
||||||
|
CommitNum int `json:"commit_num"`
|
||||||
|
ChangedFiles int `json:"changed_files"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// PRBranchInfo information about a branch
|
// PRBranchInfo information about a branch
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package structs
|
package structs
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -19,8 +20,57 @@ type Branch struct {
|
||||||
UserCanPush bool `json:"user_can_push"`
|
UserCanPush bool `json:"user_can_push"`
|
||||||
UserCanMerge bool `json:"user_can_merge"`
|
UserCanMerge bool `json:"user_can_merge"`
|
||||||
EffectiveBranchProtectionName string `json:"effective_branch_protection_name"`
|
EffectiveBranchProtectionName string `json:"effective_branch_protection_name"`
|
||||||
|
|
||||||
|
CommitID string `json:"commit_id"` // add configure
|
||||||
|
CommitTime string `json:"commit_time"` // add configure
|
||||||
|
DefaultBranch string `json:"default_branch"`
|
||||||
|
BranchKind int `json:"branch_kind"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchKind int
|
||||||
|
|
||||||
|
const (
|
||||||
|
None BranchKind = iota
|
||||||
|
DefaultBranch
|
||||||
|
ProtectedBranch
|
||||||
|
OtherBranch
|
||||||
|
)
|
||||||
|
|
||||||
|
func (bk BranchKind) Name() string {
|
||||||
|
return strings.ToLower(bk.Title())
|
||||||
|
}
|
||||||
|
func (bk BranchKind) Title() string {
|
||||||
|
switch bk {
|
||||||
|
case DefaultBranch:
|
||||||
|
return "default"
|
||||||
|
case ProtectedBranch:
|
||||||
|
return "protected"
|
||||||
|
case OtherBranch:
|
||||||
|
return "other"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
type BranchesSlice struct {
|
||||||
|
BranchName string `json:"branch_name"`
|
||||||
|
// BranchKind int `json:"branch_kind"`
|
||||||
|
Branches []Branch `json:"branches"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort by branchkind
|
||||||
|
type SortBranch []Branch
|
||||||
|
|
||||||
|
func (s SortBranch) Len() int { return len(s) }
|
||||||
|
func (s SortBranch) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s SortBranch) Less(i, j int) bool { return s[i].BranchKind < s[j].BranchKind }
|
||||||
|
|
||||||
|
// sort by CommiTime of the branch
|
||||||
|
type SortBranchTime []Branch
|
||||||
|
|
||||||
|
func (s SortBranchTime) Len() int { return len(s) }
|
||||||
|
func (s SortBranchTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s SortBranchTime) Less(i, j int) bool { return s[i].CommitTime > s[j].CommitTime }
|
||||||
|
|
||||||
// BranchProtection represents a branch protection for a repository
|
// BranchProtection represents a branch protection for a repository
|
||||||
type BranchProtection struct {
|
type BranchProtection struct {
|
||||||
BranchName string `json:"branch_name"`
|
BranchName string `json:"branch_name"`
|
||||||
|
|
|
@ -48,6 +48,7 @@ type Commit struct {
|
||||||
Committer *User `json:"committer"`
|
Committer *User `json:"committer"`
|
||||||
Parents []*CommitMeta `json:"parents"`
|
Parents []*CommitMeta `json:"parents"`
|
||||||
Files []*CommitAffectedFiles `json:"files"`
|
Files []*CommitAffectedFiles `json:"files"`
|
||||||
|
CommitDate string `json:"commit_date"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
|
// CommitDateOptions store dates for GIT_AUTHOR_DATE and GIT_COMMITTER_DATE
|
||||||
|
@ -62,3 +63,14 @@ type CommitDateOptions struct {
|
||||||
type CommitAffectedFiles struct {
|
type CommitAffectedFiles struct {
|
||||||
Filename string `json:"filename"`
|
Filename string `json:"filename"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CommitsSlice struct {
|
||||||
|
CommitDate string `json:"commit_date"`
|
||||||
|
Commits []Commit
|
||||||
|
}
|
||||||
|
|
||||||
|
type SortCommit []Commit
|
||||||
|
|
||||||
|
func (s SortCommit) Len() int { return len(s) }
|
||||||
|
func (s SortCommit) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||||
|
func (s SortCommit) Less(i, j int) bool { return s[i].CommitDate > s[j].CommitDate }
|
||||||
|
|
|
@ -6,12 +6,21 @@ package structs
|
||||||
|
|
||||||
// Tag represents a repository tag
|
// Tag represents a repository tag
|
||||||
type Tag struct {
|
type Tag struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Commit *CommitMeta `json:"commit"`
|
Commit *TagCommit `json:"commit"`
|
||||||
ZipballURL string `json:"zipball_url"`
|
ZipballURL string `json:"zipball_url"`
|
||||||
TarballURL string `json:"tarball_url"`
|
TarballURL string `json:"tarball_url"`
|
||||||
|
|
||||||
|
Tagger *CommitUser `json:"tagger"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagCommit struct {
|
||||||
|
*CommitMeta
|
||||||
|
Commiter *CommitUser `json:"commiter"`
|
||||||
|
Author *CommitUser `json:"author"`
|
||||||
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// AnnotatedTag represents an annotated tag
|
// AnnotatedTag represents an annotated tag
|
||||||
|
@ -39,3 +48,8 @@ type CreateTagOption struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
Target string `json:"target"`
|
Target string `json:"target"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type RepoBranchAndTagCount struct {
|
||||||
|
BranchCount int `json:"branch_count"`
|
||||||
|
TagCount int `json:"tag_count"`
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
package structs
|
||||||
|
|
||||||
|
type WikiesResponse struct {
|
||||||
|
WikiMeta
|
||||||
|
WikiCloneLink CloneLink `json:"wiki_clone_link"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WikiMeta struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Commit WikiCommit `json:"commit"`
|
||||||
|
FirstCommit WikiCommit `json:"-"`
|
||||||
|
//WikiCloneLink CloneLink `json:"wiki_clone_link"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WikiCommit struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Author WikiUser `json:"author"`
|
||||||
|
Commiter WikiUser `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WikiUser struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Email string `json:"email"`
|
||||||
|
When int64 `json:"when"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WikiResponse struct {
|
||||||
|
WikiMeta
|
||||||
|
CommitCounts int64 `json:"commit_counts"`
|
||||||
|
MdContent string `json:"md_content"`
|
||||||
|
SimpleContent string `json:"simple_content"`
|
||||||
|
WikiCloneLink CloneLink `json:"wiki_clone_link"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WikiOption struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Content string `json:"content"`
|
||||||
|
CommitMessage string `json:"commit_message"`
|
||||||
|
}
|
||||||
|
type CloneLink struct {
|
||||||
|
SSH string `json:"ssh"`
|
||||||
|
HTTPS string `json:"https"`
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
木兰公共许可证, 第1版
|
||||||
|
|
||||||
|
2020年12月 http://license.coscl.org.cn/MulanPublicLicenseV1
|
||||||
|
|
||||||
|
您对"贡献"的复制、使用、修改及分发受木兰公共许可证, 第1版("本许可证")的如下条款的约束:
|
||||||
|
|
||||||
|
0. 定义
|
||||||
|
"贡献"是指由"贡献者"许可在"本许可证"下的受版权法保护的作品,包括最初"贡献者"许可在"本许可证"下的作品及后续"贡献者"许可在"本许可证"下的"衍生作品"。
|
||||||
|
"贡献者"是指将受版权法保护的作品许可在"本许可证"下的自然人或"法人实体"。
|
||||||
|
"法人实体"是指提交贡献的机构及其"关联实体"。
|
||||||
|
"关联实体"是指,对"本许可证"下的行为方而言,控制、受控制或与其共同受控制的机构,此处的控制是指有受控方或共同受控方至少50%直接或间接的投票权、资金或其他有价证券。
|
||||||
|
"衍生作品"是指基于"贡献"创作的作品,具体包括对全部或部分"贡献"进行修改、重写、翻译、注释、组合或与之链接(包括动态链接或静态链接)而形成的作品。仅与"贡献"进行进程间通信或系统调用的作品是独立作品,不属于"衍生作品"。
|
||||||
|
"对应源代码"是指生成、安装和(对于可执行作品)运行目标代码所需的所有源文件和与之关联的接口定义文件,以及控制这些活动的脚本,但不包括编译环境、编译工具、云服务平台(如果有)。
|
||||||
|
"分发"是指通过任何媒介向他人提供"贡献"或"衍生作品"的行为,以及利用"贡献"或"衍生作品"通过网络远程给用户提供服务的行为,例如通过利用"贡献"或"衍生作品"搭建的云服务平台提供在线服务的行为。
|
||||||
|
|
||||||
|
1. 授予版权许可
|
||||||
|
每个"贡献者"根据"本许可证"授予您永久性的、全球性的、免费的、非独占的、不可撤销的版权许可,您可以复制、使用、修改、"分发"其"贡献"或"衍生作品",不论修改与否。
|
||||||
|
|
||||||
|
2. 授予专利许可
|
||||||
|
每个"贡献者"根据"本许可证"授予您永久性的、全球性的、免费的、非独占的、不可撤销的(根据本条规定撤销除外)专利许可,供您制造、委托制造、使用、许诺销售、销售、进口其"贡献"或以其他方式转移其"贡献"。前述专利许可仅限于"贡献者"现在或将来拥有或控制的其"贡献"中的专利权利要求,不包括仅因您或他人修改"贡献"而将必然会侵犯到的专利权利要求。如果您或您的"关联实体"直接或间接地,就"贡献"对任何人发起专利侵权诉讼(包括反诉或交叉诉讼)或其他专利维权行动,指控其侵犯专利权,则本许可证授予您对"贡献"的专利许可自您提起诉讼或发起维权行动之日终止。
|
||||||
|
|
||||||
|
3. 无商标许可
|
||||||
|
"本许可证"不提供对"贡献者"的商品名称、商标、服务标志或产品名称的商标许可,但您为满足第4条规定的声明义务而必须使用除外。
|
||||||
|
|
||||||
|
4. 分发限制
|
||||||
|
您可以在任何媒介中将您接收到的"贡献"或您的"衍生作品"以源程序形式或可执行形式重新"分发",但必须满足下列条件:
|
||||||
|
(1)您必须向接收者提供"本许可证"的副本,并保留"贡献"中的版权、商标、专利及免责声明;并且,
|
||||||
|
(2)如果您"分发"您接收到的"贡献",您必须使用"本许可证"提供该"贡献"的源代码副本;如果您 "分发"您的"衍生作品",您必须:
|
||||||
|
(i)随"衍生作品"向接收者提供使用"本许可证""分发"的您的"衍生作品"的"对应源代码"。如果您通过下载链接提供前述"对应源代码",则您应将下载链接地址置于"衍生作品"或其随附文档中的明显位置,有效期不少于自该"衍生作品""分发"之日起三年,并确保接收者可以获得"对应源代码";或者,
|
||||||
|
(ii)随"衍生作品"向接收者提供一个书面要约,表明您愿意提供使用"本许可证""分发"的您"衍生作品"的"对应源代码"。书面要约应放在"衍生作品"中的明显位置,并确保接收者根据书面要约可获取"对应源代码"的时间从您接到该请求之日起不得超过三个月,且有效期不少于自该"衍生作品""分发"之日起三年。
|
||||||
|
|
||||||
|
5. 违约与终止
|
||||||
|
如果您违反"本许可证",任何"贡献者"有权书面通知您终止其依据"本许可证"授予您的许可。该"贡献者"授予您的许可从您接到其终止通知之日起终止。但在如下两种情形下,即使您收到"贡献者"的通知也并不终止您的许可:
|
||||||
|
(1)您在接到终止通知之前已停止所有违反行为;
|
||||||
|
(2)您是首次收到该"贡献者"根据"本许可证"发出的书面终止通知,您在收到该通知后30天内停止所有违反行为。
|
||||||
|
|
||||||
|
只要下游接收者遵守"本许可证"的相关规定,您在"本许可证"下的许可终止,不影响下游接收者根据"本许可证"享有的权利。
|
||||||
|
|
||||||
|
6. 例外
|
||||||
|
如果您将"贡献"或您的"衍生作品"与采用GPLv3、AGPLv3、或前述许可证的后续版本(以下简称"GPL类许可证")的作品结合,且根据"GPL类许可证"的要求您有义务将形成的结合作品以对应的"GPL类许可证"许可的,您可以根据对应的"GPL类许可证"许可结合作品,只要您在分发该结合作品的同时向接收者提供"本许可证"的副本,并保留"贡献"中的版权、商标、专利及免责声明。任何"贡献者"不会因您根据前述原因选择"GPL类许可证"许可而授予该结合作品的接收者更多权利。
|
||||||
|
|
||||||
|
7. 免责声明与责任限制
|
||||||
|
"贡献"在提供时不带任何明示或默示的担保。在任何情况下,"贡献者"或版权所有者不对任何人因使用"贡献"而引发的任何直接或间接损失承担责任,不论因何种原因导致或者基于何种法律理论,即使其曾被建议有此种损失的可能性。
|
||||||
|
|
||||||
|
8. 语言
|
||||||
|
"本许可证"以中英文双语表述,中英文版本具有同等法律效力。如果中英文版本存在任何冲突不一致,以中文版为准。
|
||||||
|
|
||||||
|
条款结束
|
||||||
|
|
||||||
|
如何将木兰公共许可证,第1版,应用到您的软件
|
||||||
|
|
||||||
|
如果您希望将木兰公共许可证,第1版,应用到您的新软件,为了方便接收者查阅,建议您完成如下三步:
|
||||||
|
|
||||||
|
1, 请您补充如下声明中的空白,包括软件名、软件的首次发表年份以及您作为版权人的名字;
|
||||||
|
|
||||||
|
2, 请您在软件包的一级目录下创建以“LICENSE”为名的文件,将整个许可证文本放入该文件中;
|
||||||
|
|
||||||
|
3, 请将如下声明文本放入每个源文件的头部注释中。
|
||||||
|
|
||||||
|
Copyright (c) [Year] [name of copyright holder]
|
||||||
|
[Software Name] is licensed under Mulan Public License v1.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan Public License v1.
|
||||||
|
You may obtain a copy of Mulan Public License v1 at:
|
||||||
|
http://license.coscl.org.cn/MulanPublicLicenseV1
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan Public License v1 for more details.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Your reproduction, use, modification and Distribution of the Contribution shall be subject to Mulan Public License, Version 1 (this License) with following terms and conditions:
|
||||||
|
|
||||||
|
0. Definitions
|
||||||
|
|
||||||
|
Contribution means the copyrightable work licensed by a particular Contributor under this License, including the work licensed by the initial Contributor under this License and its Derivative Work licensed by any subsequent Contributor under this License.
|
||||||
|
|
||||||
|
Contributor means the Individual or Legal Entity who licenses its copyrightable work under this License.
|
||||||
|
|
||||||
|
Legal Entity means the entity making a Contribution and all its Affiliates.
|
||||||
|
|
||||||
|
Affiliates means entities that control, are controlled by, or are under common control with the acting entity under this License, ‘control’ means direct or indirect ownership of at least fifty percent (50%) of the voting power, capital or other securities of controlled or commonly controlled entity.
|
||||||
|
|
||||||
|
Derivative Work means works created based on Contribution, specifically including works formed by modifying, rewriting, translating, annotating, combining or linking to all or part of Contribution (including dynamic linking or static linking). Works which only communicate with Contribution through inter-process communication or system call, are independent works, rather than Derivative Work.
|
||||||
|
|
||||||
|
Corresponding Source Code means all the source code needed to generate, install, and (for an executable work) run the object code including the interface definition files associated with source files for the work, and scripts to control those activities, excluding of compilation environment and compilation tools, cloud services platform (if any).
|
||||||
|
|
||||||
|
Distribute (or Distribution) means the act of making the Contribution or Derivative Work available to others through any medium, and using the Contribution or Derivative Work to provide online services to users, such as the act of providing online services through a cloud service platform built using Contributions or Derivative Works.
|
||||||
|
|
||||||
|
1. Grant of copyright license
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable copyright license to reproduce, use, modify, or Distribute its Contribution or Derivative Work, with modification or not.
|
||||||
|
|
||||||
|
2. Grant of patent license
|
||||||
|
|
||||||
|
Subject to the terms and conditions of this License, each Contributor hereby grants to you a perpetual, worldwide, royalty-free, non-exclusive, irrevocable (except for revocation under this Section) patent license to make, have made, use, offer for sale, sell, import or otherwise transfer its Contribution, where such patent license is only limited to the patent claims owned or controlled by such Contributor now or in future which will be necessarily infringed by its Contribution alone, excluding of any patent claims solely be infringed by your modification. If you or your Affiliates directly or indirectly institute patent litigation (including a cross claim or counterclaim in a litigation) or other patent enforcement activities against any individual or entity by alleging that any Contribution infringes patents, then any patent license granted to you under this License for the Contribution shall terminate as of the date such litigation or activity is filed or taken.
|
||||||
|
|
||||||
|
3. No Trademark License
|
||||||
|
|
||||||
|
No trademark license is granted to use the trade names, trademarks, service marks, or product names of Contributor, except as required to fulfill notice requirements in Section 4.
|
||||||
|
|
||||||
|
4. Distribution Restriction
|
||||||
|
|
||||||
|
You may Distribute the Contribution you received or your Derivative Work through any medium with or without modification, whether in source or executable forms, provided that you meet the following conditions:
|
||||||
|
|
||||||
|
1) You must provide recipients with a copy of this License and retain copyright, trademark, patent and disclaimer statements in the Contribution; and,
|
||||||
|
|
||||||
|
2) If you Distribute the Contribution you received, you must provide copies of the Contribution’s source code under this License;
|
||||||
|
|
||||||
|
If you Distribute your Derivative Work, you have to:
|
||||||
|
|
||||||
|
(i) accompanying the Derivative work, provide recipients with Corresponding Source Code of your Derivative Work under this License. If you provide the Corresponding Source Code through a download link, you should place such link address prominently in the Derivative Work or its accompanying documents, and be valid no less than three years from your Distribution of the particular Derivative Work, and ensure that the recipients can acquire the Corresponding Source Code through the link; or,
|
||||||
|
|
||||||
|
(ii) accompanying the Derivative Work, provide recipients with a written offer indicating your willingness to provide the Corresponding Source Code of the Derivative Work licensed under this License. Such written offer shall be placed prominently in the Derivative Work or its accompanying documents. Without reasonable excuse, the recipient shall be able to acquire the Corresponding Source code of the Derivative work for no more than three months from your receipt of a valid request, and be valid no less than three years from your Distribution of the particular Derivative Work.
|
||||||
|
|
||||||
|
5. Breach and Termination
|
||||||
|
|
||||||
|
If you breach this License, any Contributor has the right to notify you in writing to terminate its license granted to you under this License. The license granted to you by such Contributor terminates upon your receipt of such notice of termination. Notwithstanding the foregoing, your license will not be terminated even if you receive a notice of termination from Contributor, provided that:
|
||||||
|
|
||||||
|
1) you have cured all the breaches prior to receiving such notice of termination; or,
|
||||||
|
|
||||||
|
2) it’s your first time to receive a notice of termination from such Contributor pursuant to this License, and you cured all the breaches within 30 days of receipt of such notice.
|
||||||
|
|
||||||
|
Termination of your license under this License shall not affect the downstream recipient's rights under this License, provided that the downstream recipient complies with this License.
|
||||||
|
|
||||||
|
6. Exceptions
|
||||||
|
|
||||||
|
If you combine Contribution or your Derivative Work with a work licensed under the GPLv3, AGPLv3 or subsequent versions of those licenses (hereinafter referred to as “GPL Style License”), and according to the GPL Style License, you have an obligation to make the combined work to be licensed under the corresponding GPL Style License, you can license such combined work under the GPL Style License, provided that when you Distribute the combined work, you also provide a copy of this License to the recipients, and retain copyright, trademarks, patents, and disclaimer statements in the Contribution. No Contributor will grant additional rights to the recipients of the combined work for your license under GPL Style License.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty and Limitation of liability
|
||||||
|
|
||||||
|
CONTRIBUTION ARE PROVIDED WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED. IN NO EVENT SHALL ANY CONTRIBUTOR OR COPYRIGHT HOLDER BE LIABLE TO YOU FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO ANY DIRECT, OR INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING FROM YOUR USE OR INABILITY TO USE THE CONTRIBUTION, NO MATTER HOW IT’S CAUSED OR BASED ON WHICH LEGAL THEORY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||||
|
|
||||||
|
8. Language
|
||||||
|
|
||||||
|
THIS LICENSE IS WRITTEN IN BOTH CHINESE AND ENGLISH, AND THE CHINESE VERSION AND ENGLISH VERSION SHALL HAVE THE SAME LEGAL EFFECT. IN THE CASE OF DIVERGENCE BETWEEN THE CHINESE AND ENGLISH VERSIONS, THE CHINESE VERSION SHALL PREVAIL.
|
||||||
|
|
||||||
|
End of the Terms and Conditions
|
||||||
|
|
||||||
|
How to apply the Mulan Public License,Version 1 (Mulan PublicLicense v1), to your software
|
||||||
|
|
||||||
|
To apply the Mulan Public License,Version 1 to your work, for easy identification by recipients, you are suggested to complete following three steps:
|
||||||
|
|
||||||
|
Fill in the blanks in following statement, including insert your software name, the year of the first publication of your software, and your name identified as the copyright owner;
|
||||||
|
Create a file named “LICENSE” which contains the whole context of this License in the first directory of your software package;
|
||||||
|
Attach the statement to the appropriate annotated syntax at the beginning of each source file.
|
||||||
|
Copyright (c) [Year] [name of copyright holder]
|
||||||
|
[Software Name] is licensed under Mulan Public License v1.
|
||||||
|
You can use this software according to the terms and conditions of the Mulan Public License v1.
|
||||||
|
You may obtain a copy of Mulan Public License v1 at:
|
||||||
|
http://license.coscl.org.cn/MulanPublicLicenseV1
|
||||||
|
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
|
||||||
|
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
|
||||||
|
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
|
||||||
|
See the Mulan Public License v1 for more details.
|
|
@ -0,0 +1,18 @@
|
||||||
|
本协议是您(如下也称"用户")与中国空气动力研究与发展中心(以下简称"中心"),关于国家数值风洞(NNW)"风雷(PHengLEI)"软件的协议,请认真阅读.
|
||||||
|
|
||||||
|
|
||||||
|
1. 申请者不得私自以拷贝、刻录等方式向第三者(包括同一单位内的其他人员)传播。所有用户均需填写本协议,向中心申请并获授权后方能免费获取.
|
||||||
|
|
||||||
|
2. 用户可以在"风雷(PHengLEI)"软件上添加、修改源代码,有权提出软件的使用反馈及意见,用户可以发表软件的使用体验及感受,但不得随意诽谤、中伤.
|
||||||
|
|
||||||
|
3. 用户享有其开发代码的知识产权。鼓励将开发成果返回至开发团队,或在"风雷(PHengLEI)"软件中开源,开发者将在代码中署名.
|
||||||
|
|
||||||
|
4. 若该软件被申请人用于学术研究,则相关论文成果中应引用国家数值风洞"风雷(PHengLEI)"软件; 若用于其它用途,需在显要处标注基于"风雷(PHengLEI)"软件开发.
|
||||||
|
|
||||||
|
5. 一旦申请使用本软件,即表示同意接受协议各项条件的约束。如果您不同意协议的条件,则不能获得使用本软件的权利.
|
||||||
|
|
||||||
|
6. "风雷(PHengLEI)"软件由中心开发,一切知识产权,以及与软件相关的所有信息内容,包括但不限于:文字表述及其组合、图标、图饰、图表、色彩、版面框架、有关数据、印刷材料、或电子文档等均受《中华人民共和国著作权法》、《中华人民共和国计算机软件保护条例》、《中华人民共和国商标法》、《中华人民共和国专利法》、反不正当竞争法和相应的国际条约以及其他知识产权法律法规的保护,除涉及第三方授权的软件或技术外,中心享有上述知识产权.
|
||||||
|
|
||||||
|
7. 您获得的只是本软件的使用权。本软件仅限中华人民共和国公民申请使用.
|
||||||
|
|
||||||
|
8. 本协议的最终解释权归中心.
|
|
@ -80,9 +80,11 @@ import (
|
||||||
"code.gitea.io/gitea/routers/api/v1/notify"
|
"code.gitea.io/gitea/routers/api/v1/notify"
|
||||||
"code.gitea.io/gitea/routers/api/v1/org"
|
"code.gitea.io/gitea/routers/api/v1/org"
|
||||||
"code.gitea.io/gitea/routers/api/v1/repo"
|
"code.gitea.io/gitea/routers/api/v1/repo"
|
||||||
|
report "code.gitea.io/gitea/routers/api/v1/reporter"
|
||||||
"code.gitea.io/gitea/routers/api/v1/settings"
|
"code.gitea.io/gitea/routers/api/v1/settings"
|
||||||
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
|
_ "code.gitea.io/gitea/routers/api/v1/swagger" // for swagger generation
|
||||||
"code.gitea.io/gitea/routers/api/v1/user"
|
"code.gitea.io/gitea/routers/api/v1/user"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/viewfile"
|
||||||
"code.gitea.io/gitea/services/auth"
|
"code.gitea.io/gitea/services/auth"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
|
||||||
|
@ -600,6 +602,11 @@ func Routes() *web.Route {
|
||||||
m.Get("/repository", settings.GetGeneralRepoSettings)
|
m.Get("/repository", settings.GetGeneralRepoSettings)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
m.Group("/activity", func() {
|
||||||
|
m.Get("", report.GetActivity)
|
||||||
|
m.Get("/project", report.GetActivityProject)
|
||||||
|
m.Get("/develop", report.GetActivityDevelop)
|
||||||
|
})
|
||||||
// Notifications
|
// Notifications
|
||||||
m.Group("/notifications", func() {
|
m.Group("/notifications", func() {
|
||||||
m.Combo("").
|
m.Combo("").
|
||||||
|
@ -739,6 +746,25 @@ func Routes() *web.Route {
|
||||||
Delete(repo.DeleteGitHook)
|
Delete(repo.DeleteGitHook)
|
||||||
})
|
})
|
||||||
}, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true))
|
}, reqToken(), reqAdmin(), reqGitHook(), context.ReferencesGitRepo(true))
|
||||||
|
|
||||||
|
m.Group("/commits/count", func() {
|
||||||
|
m.Get("/branch/*", context.RepoRefByType(context.RepoRefBranch), repo.GetCommitsCount)
|
||||||
|
m.Get("/tag/*", context.RepoRefByType(context.RepoRefTag), repo.GetCommitsCount)
|
||||||
|
m.Get("/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.GetCommitsCount)
|
||||||
|
})
|
||||||
|
m.Group("/pulls/:index", func() {
|
||||||
|
m.Get("/commits", context.RepoRef(), repo.GetPullCommits)
|
||||||
|
m.Get("/files", context.RepoRef(), repo.GetPullFiles)
|
||||||
|
m.Get("/issues", context.RepoRef(), repo.GetPullIssues)
|
||||||
|
})
|
||||||
|
// m.Get("/compare/*", context.RepoAssignment(), repo.MustBeNotEmpty, reqRepoCodeReader,
|
||||||
|
// repo.SetEditorconfigIfExists, repo.SetDiffViewStyle, repo.CompareDiff)
|
||||||
|
// m.Get("/src/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.GetFileContents)
|
||||||
|
m.Get("/src/commit/*", context.RepoRefByType(context.RepoRefCommit), repo.GetFileContents)
|
||||||
|
//end by coder
|
||||||
|
m.Group("/contributors", func() {
|
||||||
|
m.Get("", report.GetContributors) //获取仓库的所有构建者信息 ****
|
||||||
|
})
|
||||||
m.Group("/hooks", func() {
|
m.Group("/hooks", func() {
|
||||||
m.Combo("").Get(repo.ListHooks).
|
m.Combo("").Get(repo.ListHooks).
|
||||||
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
Post(bind(api.CreateHookOption{}), repo.CreateHook)
|
||||||
|
@ -747,6 +773,7 @@ func Routes() *web.Route {
|
||||||
Patch(bind(api.EditHookOption{}), repo.EditHook).
|
Patch(bind(api.EditHookOption{}), repo.EditHook).
|
||||||
Delete(repo.DeleteHook)
|
Delete(repo.DeleteHook)
|
||||||
m.Post("/tests", context.RepoRefForAPI, repo.TestHook)
|
m.Post("/tests", context.RepoRefForAPI, repo.TestHook)
|
||||||
|
m.Get("/hooktasks", repo.ListHookTask)
|
||||||
})
|
})
|
||||||
}, reqToken(), reqAdmin(), reqWebhooksEnabled())
|
}, reqToken(), reqAdmin(), reqWebhooksEnabled())
|
||||||
m.Group("/collaborators", func() {
|
m.Group("/collaborators", func() {
|
||||||
|
@ -765,10 +792,16 @@ func Routes() *web.Route {
|
||||||
}, reqToken())
|
}, reqToken())
|
||||||
m.Get("/raw/*", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
|
m.Get("/raw/*", context.RepoRefForAPI, reqRepoReader(models.UnitTypeCode), repo.GetRawFile)
|
||||||
m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive)
|
m.Get("/archive/*", reqRepoReader(models.UnitTypeCode), repo.GetArchive)
|
||||||
|
|
||||||
|
m.Get("/find", viewfile.FindFiles)
|
||||||
|
m.Group("/releases", func() {
|
||||||
|
m.Get("/latest", viewfile.LatestRelease)
|
||||||
|
})
|
||||||
m.Combo("/forks").Get(repo.ListForks).
|
m.Combo("/forks").Get(repo.ListForks).
|
||||||
Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
|
Post(reqToken(), reqRepoReader(models.UnitTypeCode), bind(api.CreateForkOption{}), repo.CreateFork)
|
||||||
m.Group("/branches", func() {
|
m.Group("/branches", func() {
|
||||||
m.Get("", repo.ListBranches)
|
m.Get("", repo.ListBranches)
|
||||||
|
m.Get("/branches_slice", repo.ListBranchesSlice)
|
||||||
m.Get("/*", repo.GetBranch)
|
m.Get("/*", repo.GetBranch)
|
||||||
m.Delete("/*", context.ReferencesGitRepo(false), reqRepoWriter(models.UnitTypeCode), repo.DeleteBranch)
|
m.Delete("/*", context.ReferencesGitRepo(false), reqRepoWriter(models.UnitTypeCode), repo.DeleteBranch)
|
||||||
m.Post("", reqRepoWriter(models.UnitTypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch)
|
m.Post("", reqRepoWriter(models.UnitTypeCode), bind(api.CreateBranchRepoOption{}), repo.CreateBranch)
|
||||||
|
@ -782,6 +815,24 @@ func Routes() *web.Route {
|
||||||
m.Delete("", repo.DeleteBranchProtection)
|
m.Delete("", repo.DeleteBranchProtection)
|
||||||
})
|
})
|
||||||
}, reqToken(), reqAdmin())
|
}, reqToken(), reqAdmin())
|
||||||
|
m.Group("/wikies", func() {
|
||||||
|
m.Combo("").Get(repo.ListWikiPages).
|
||||||
|
Post(bind(api.WikiOption{}), repo.CreateWiki)
|
||||||
|
m.Group("/{page}", func() {
|
||||||
|
m.Combo("").Get(repo.GetWiki).
|
||||||
|
Patch(bind(api.WikiOption{}), repo.EditWiki).
|
||||||
|
Delete(repo.DeleteWiki)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
m.Group("/readme", func() {
|
||||||
|
m.Get("", repo.GetReadmeContents)
|
||||||
|
m.Get("/*", repo.GetReadmeContentsByPath)
|
||||||
|
})
|
||||||
|
m.Get("/commits_slice", repo.GetAllCommitsSliceByTime)
|
||||||
|
// m.Get("/branchtagcount", repo.BranchTagCount)
|
||||||
|
m.Group("/branch_tag_count", func() {
|
||||||
|
m.Get("", repo.BranchTagCount)
|
||||||
|
}, reqRepoReader(models.UnitTypeCode), context.ReferencesGitRepo(true))
|
||||||
m.Group("/tags", func() {
|
m.Group("/tags", func() {
|
||||||
m.Get("", repo.ListTags)
|
m.Get("", repo.ListTags)
|
||||||
m.Get("/*", repo.GetTag)
|
m.Get("/*", repo.GetTag)
|
||||||
|
|
|
@ -206,7 +206,21 @@ func Create(ctx *context.APIContext) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.JSON(http.StatusCreated, convert.ToOrganization(org))
|
// ctx.JSON(http.StatusCreated, convert.ToOrganization(org))
|
||||||
|
// 根据业务需要 自定义创建组织时将默认团队同时返回.
|
||||||
|
Team, err := models.GetTeam(org.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrUserNotExist(err) {
|
||||||
|
ctx.NotFound()
|
||||||
|
} else {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetTeam", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiOrg := convert.ToOrganizationExt(org)
|
||||||
|
apiOrg.OwnerTeam = convert.ToTeam(Team)
|
||||||
|
ctx.JSON(http.StatusCreated, apiOrg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get get an organization
|
// Get get an organization
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
@ -288,6 +289,92 @@ func ListBranches(ctx *context.APIContext) {
|
||||||
ctx.JSON(http.StatusOK, &apiBranches)
|
ctx.JSON(http.StatusOK, &apiBranches)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ListBranches list all the branches of a repository
|
||||||
|
func ListBranchesSlice(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/branches/branches_slice repository repoListBranchesSlice
|
||||||
|
// ---
|
||||||
|
// summary: List a repository's branches, Group sort.
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/BranchList"
|
||||||
|
|
||||||
|
// listOptions := utils.GetListOptions(ctx)
|
||||||
|
// skip, _ := listOptions.GetStartEnd()
|
||||||
|
// branches, totalNumOfBranches, err := repo_module.GetBranches(ctx.Repo.Repository, skip, listOptions.PageSize)
|
||||||
|
branches, totalNumOfBranches, err := repo_module.GetBranchesNoLimit(ctx.Repo.Repository)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiBranches := make([]*api.Branch, len(branches))
|
||||||
|
apiBranchesList := []api.Branch{}
|
||||||
|
for i := range branches {
|
||||||
|
c, err := branches[i].GetCommit()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
branchProtection, err := ctx.Repo.Repository.GetBranchProtection(branches[i].Name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetBranchProtection", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apiBranches[i], err = convert.ToBranch(ctx.Repo.Repository, branches[i], c, branchProtection, ctx.User, ctx.Repo.IsAdmin())
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "convert.ToBranch", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apiBranchesList = append(apiBranchesList, *apiBranches[i])
|
||||||
|
sort.Sort(api.SortBranch(apiBranchesList))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// ctx.SetLinkHeader(int(totalNumOfBranches), listOptions.PageSize)
|
||||||
|
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", totalNumOfBranches))
|
||||||
|
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, Link")
|
||||||
|
// ctx.JSON(http.StatusOK, &apiBranches)
|
||||||
|
ctx.JSON(http.StatusOK, BranchesSliceByProtection(ctx, apiBranchesList))
|
||||||
|
}
|
||||||
|
|
||||||
|
func BranchesSliceByProtection(ctx *context.APIContext, branchList []api.Branch) []api.BranchesSlice {
|
||||||
|
// group by protection
|
||||||
|
sort.Sort(api.SortBranch(branchList))
|
||||||
|
branchSlice := make([]api.BranchesSlice, 0)
|
||||||
|
i := 0
|
||||||
|
var j int
|
||||||
|
for {
|
||||||
|
if i >= len(branchList) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
for j = i + 1; j < len(branchList) && (branchList[i].BranchKind == branchList[j].BranchKind); j++ {
|
||||||
|
}
|
||||||
|
// get the same branches
|
||||||
|
sameBranchSlice := branchList[i:j]
|
||||||
|
// sort by time
|
||||||
|
sort.Sort(api.SortBranchTime(sameBranchSlice))
|
||||||
|
branchSlice = append(branchSlice, api.BranchesSlice{
|
||||||
|
BranchName: convert.ToBranchType(branchList[i].BranchKind).Name(),
|
||||||
|
Branches: sameBranchSlice,
|
||||||
|
})
|
||||||
|
i = j
|
||||||
|
}
|
||||||
|
return branchSlice
|
||||||
|
}
|
||||||
|
|
||||||
// GetBranchProtection gets a branch protection
|
// GetBranchProtection gets a branch protection
|
||||||
func GetBranchProtection(ctx *context.APIContext) {
|
func GetBranchProtection(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
|
// swagger:operation GET /repos/{owner}/{repo}/branch_protections/{name} repository repoGetBranchProtection
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
@ -219,3 +220,179 @@ func GetAllCommits(ctx *context.APIContext) {
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, &apiCommits)
|
ctx.JSON(http.StatusOK, &apiCommits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAllCommitsSliceByTime(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/commits_slice repository repoGetAllCommitsSlice
|
||||||
|
// ---
|
||||||
|
// summary: Get a list of all commits from a repository and sort by time
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: sha
|
||||||
|
// in: query
|
||||||
|
// description: SHA or branch to start listing commits from (usually 'master')
|
||||||
|
// type: string
|
||||||
|
// - name: page
|
||||||
|
// in: query
|
||||||
|
// description: page number of results to return (1-based)
|
||||||
|
// type: integer
|
||||||
|
// - name: limit
|
||||||
|
// in: query
|
||||||
|
// description: page size of results
|
||||||
|
// type: integer
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/CommitList"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
// "409":
|
||||||
|
// "$ref": "#/responses/EmptyRepository"
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
ctx.JSON(http.StatusConflict, api.APIError{
|
||||||
|
Message: "Git Repository is empty.",
|
||||||
|
URL: setting.API.SwaggerURL,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
gitRepo, err := git.OpenRepository(ctx.Repo.Repository.RepoPath())
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "OpenRepository", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer gitRepo.Close()
|
||||||
|
|
||||||
|
listOptions := utils.GetListOptions(ctx)
|
||||||
|
if listOptions.Page <= 0 {
|
||||||
|
listOptions.Page = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if listOptions.PageSize > setting.Git.CommitsRangeSize {
|
||||||
|
listOptions.PageSize = setting.Git.CommitsRangeSize
|
||||||
|
}
|
||||||
|
|
||||||
|
sha := ctx.Query("sha")
|
||||||
|
|
||||||
|
var baseCommit *git.Commit
|
||||||
|
if len(sha) == 0 {
|
||||||
|
// no sha supplied - use default branch
|
||||||
|
head, err := gitRepo.GetHEADBranch()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetHEADBranch", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
baseCommit, err = gitRepo.GetBranchCommit(head.Name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// get commit specified by sha
|
||||||
|
baseCommit, err = gitRepo.GetCommit(sha)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total commit count
|
||||||
|
commitsCountTotal, err := baseCommit.CommitsCount()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetCommitsCount", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pageCount := int(math.Ceil(float64(commitsCountTotal) / float64(listOptions.PageSize)))
|
||||||
|
|
||||||
|
// Query commits
|
||||||
|
commits, err := baseCommit.CommitsByRange(listOptions.Page, listOptions.PageSize)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "CommitsByRange", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userCache := make(map[string]*models.User)
|
||||||
|
|
||||||
|
apiCommits := make([]*api.Commit, commits.Len())
|
||||||
|
apiCommitsList := []api.Commit{}
|
||||||
|
i := 0
|
||||||
|
for commitPointer := commits.Front(); commitPointer != nil; commitPointer = commitPointer.Next() {
|
||||||
|
commit := commitPointer.Value.(*git.Commit)
|
||||||
|
|
||||||
|
// Create json struct
|
||||||
|
apiCommits[i], err = convert.ToCommit(ctx.Repo.Repository, commit, userCache)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "toCommit", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apiCommitsList = append(apiCommitsList, *apiCommits[i])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// kept for backwards compatibility
|
||||||
|
ctx.Header().Set("X-Page", strconv.Itoa(listOptions.Page))
|
||||||
|
ctx.Header().Set("X-PerPage", strconv.Itoa(listOptions.PageSize))
|
||||||
|
ctx.Header().Set("X-Total", strconv.FormatInt(commitsCountTotal, 10))
|
||||||
|
ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount))
|
||||||
|
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOptions.Page < pageCount))
|
||||||
|
|
||||||
|
ctx.SetLinkHeader(int(commitsCountTotal), listOptions.PageSize)
|
||||||
|
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", commitsCountTotal))
|
||||||
|
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link")
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, CommitSplitSlice(apiCommitsList))
|
||||||
|
}
|
||||||
|
|
||||||
|
func CommitSplitSlice(CommitsList []api.Commit) []api.CommitsSlice {
|
||||||
|
// sort by time
|
||||||
|
sort.Sort(api.SortCommit(CommitsList))
|
||||||
|
Commits := make([]api.CommitsSlice, 0)
|
||||||
|
i := 0
|
||||||
|
var j int
|
||||||
|
for {
|
||||||
|
if i >= len(CommitsList) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// Detect equal CommitData,
|
||||||
|
for j = i + 1; j < len(CommitsList) && CommitsList[i].CommitDate == CommitsList[j].CommitDate; j++ {
|
||||||
|
}
|
||||||
|
// if equal, put commitdata in an array
|
||||||
|
commitDate := CommitsList[i].CommitDate
|
||||||
|
commitDatalist := CommitsList[i:j]
|
||||||
|
i = j // variable value
|
||||||
|
// get all the values,,,Commits
|
||||||
|
Commits = append(Commits, api.CommitsSlice{
|
||||||
|
CommitDate: commitDate,
|
||||||
|
Commits: commitDatalist,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return Commits
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取某一个分支或标签的 commits 数量
|
||||||
|
func GetCommitsCount(ctx *context.APIContext) {
|
||||||
|
var err error
|
||||||
|
// ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch)
|
||||||
|
// if err != nil {
|
||||||
|
// ctx.ServerError("GetBranchCommit", err)
|
||||||
|
// return
|
||||||
|
// }
|
||||||
|
ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetCommitsCount", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, &ctx.Repo)
|
||||||
|
}
|
||||||
|
|
|
@ -6,12 +6,16 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/base"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/repofiles"
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
|
@ -286,7 +290,7 @@ func UpdateFile(ctx *context.APIContext) {
|
||||||
// consumes:
|
// consumes:
|
||||||
// - application/json
|
// - application/json
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/jsoncontent
|
||||||
// parameters:
|
// parameters:
|
||||||
// - name: owner
|
// - name: owner
|
||||||
// in: path
|
// in: path
|
||||||
|
@ -596,3 +600,262 @@ func GetContentsList(ctx *context.APIContext) {
|
||||||
// same as GetContents(), this function is here because swagger fails if path is empty in GetContents() interface
|
// same as GetContents(), this function is here because swagger fails if path is empty in GetContents() interface
|
||||||
GetContents(ctx)
|
GetContents(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetReadmeContents Get the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir
|
||||||
|
func GetReadmeContents(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/readme repository repoGetReadmeContents
|
||||||
|
// ---
|
||||||
|
// summary: Gets the README.md's contents (if a file) of an entry in a repository ***
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: ref
|
||||||
|
// in: query
|
||||||
|
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/ContentsResponse"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
if !canReadFiles(ctx.Repo) {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", models.ErrUserDoesNotHaveAccessToRepo{
|
||||||
|
UserID: ctx.User.ID,
|
||||||
|
RepoName: ctx.Repo.Repository.LowerName,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// treePath := ctx.Params("*")
|
||||||
|
ref := ctx.QueryTrim("ref")
|
||||||
|
|
||||||
|
if fileList, err := repofiles.GetContentsOrList(ctx.Repo.Repository, "README.md", ref); err != nil {
|
||||||
|
if git.IsErrNotExist(err) {
|
||||||
|
ctx.NotFound("GetContentsOrList", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", err)
|
||||||
|
} else {
|
||||||
|
ctx.JSON(http.StatusOK, fileList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetReadmeContentsByPath(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/readme/{dir} repository repoGetReadmeContentsByPath
|
||||||
|
// ---
|
||||||
|
// summary: Gets the metadata and contents (if a file) of an entry in a repository, or a list of entries if a dir ***
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: dir
|
||||||
|
// in: path
|
||||||
|
// description: path of the dir, file, symlink or submodule in the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: ref
|
||||||
|
// in: query
|
||||||
|
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/ContentsResponse"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
if !canReadFiles(ctx.Repo) {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", models.ErrUserDoesNotHaveAccessToRepo{
|
||||||
|
UserID: ctx.User.ID,
|
||||||
|
RepoName: ctx.Repo.Repository.LowerName,
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
treePath := ctx.Params("*")
|
||||||
|
ref := ctx.QueryTrim("ref")
|
||||||
|
newTreePath := treePath + "/" + "README.md"
|
||||||
|
if fileList, err := repofiles.GetContentsOrList(ctx.Repo.Repository, newTreePath, ref); err != nil {
|
||||||
|
if git.IsErrNotExist(err) {
|
||||||
|
ctx.NotFound("GetContentsOrList", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetContentsOrList", err)
|
||||||
|
} else {
|
||||||
|
ctx.JSON(http.StatusOK, fileList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeURL(address string) string {
|
||||||
|
u, err := url.Parse(address)
|
||||||
|
if err != nil {
|
||||||
|
return address
|
||||||
|
}
|
||||||
|
u.User = nil
|
||||||
|
return u.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
tplMigrating base.TplName = "repo/migrating"
|
||||||
|
tplRepoEMPTY base.TplName = "repo/empty"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetFileContents(ctx *context.APIContext) {
|
||||||
|
if len(ctx.Repo.Units) > 0 {
|
||||||
|
if ctx.Repo.Repository.IsBeingCreated() {
|
||||||
|
task, err := models.GetMigratingTask(ctx.Repo.Repository.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("models.GetMigratingTask", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cfg, err := task.MigrateConfig()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("task.MigrateConfig", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Repo"] = ctx.Repo
|
||||||
|
ctx.Data["MigrateTask"] = task
|
||||||
|
ctx.Data["CloneAddr"] = safeURL(cfg.CloneAddr)
|
||||||
|
ctx.HTML(200, tplMigrating)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstUnit *models.Unit
|
||||||
|
for _, repoUnit := range ctx.Repo.Units {
|
||||||
|
if repoUnit.Type == models.UnitTypeCode {
|
||||||
|
renderCode(ctx.Context)
|
||||||
|
fileContent := struct {
|
||||||
|
Content interface{}
|
||||||
|
}{
|
||||||
|
Content: ctx.Data["FileContent"],
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, fileContent)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
unit, ok := models.Units[repoUnit.Type]
|
||||||
|
if ok && (firstUnit == nil || !firstUnit.IsLessThan(unit)) {
|
||||||
|
firstUnit = &unit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if firstUnit != nil {
|
||||||
|
ctx.Redirect(fmt.Sprintf("%s/%s%s", setting.AppSubURL, ctx.Repo.Repository.FullName(), firstUnit.URI))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.NotFound("Home", fmt.Errorf(ctx.Tr("units.error.no_unit_allowed_repo")))
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderCode(ctx *context.Context) {
|
||||||
|
ctx.Data["PageIsViewCode"] = true
|
||||||
|
|
||||||
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
ctx.HTML(200, tplRepoEMPTY)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
title := ctx.Repo.Repository.Owner.Name + "/" + ctx.Repo.Repository.Name
|
||||||
|
if len(ctx.Repo.Repository.Description) > 0 {
|
||||||
|
title += ": " + ctx.Repo.Repository.Description
|
||||||
|
}
|
||||||
|
ctx.Data["Title"] = title
|
||||||
|
|
||||||
|
branchLink := ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL()
|
||||||
|
treeLink := branchLink
|
||||||
|
// rawLink := ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL()
|
||||||
|
|
||||||
|
if len(ctx.Repo.TreePath) > 0 {
|
||||||
|
treeLink += "/" + ctx.Repo.TreePath
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Topics of this repo
|
||||||
|
renderRepoTopics(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get current entry user currently looking at.
|
||||||
|
entry, err := ctx.Repo.Commit.GetTreeEntryByPath(ctx.Repo.TreePath)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLanguageStats(ctx)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
// renderDirectory(ctx, treeLink)
|
||||||
|
} else {
|
||||||
|
// renderFile(ctx, entry, treeLink, rawLink)
|
||||||
|
}
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var treeNames []string
|
||||||
|
paths := make([]string, 0, 5)
|
||||||
|
if len(ctx.Repo.TreePath) > 0 {
|
||||||
|
treeNames = strings.Split(ctx.Repo.TreePath, "/")
|
||||||
|
for i := range treeNames {
|
||||||
|
paths = append(paths, strings.Join(treeNames[:i+1], "/"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["HasParentPath"] = true
|
||||||
|
if len(paths)-2 >= 0 {
|
||||||
|
ctx.Data["ParentPath"] = "/" + paths[len(paths)-2]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Paths"] = paths
|
||||||
|
ctx.Data["TreeLink"] = treeLink
|
||||||
|
ctx.Data["TreeNames"] = treeNames
|
||||||
|
ctx.Data["BranchLink"] = branchLink
|
||||||
|
// ctx.HTML(200, tplRepoHome)
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderRepoTopics(ctx *context.Context) {
|
||||||
|
topics, err := models.FindTopics(&models.FindTopicOptions{
|
||||||
|
RepoID: ctx.Repo.Repository.ID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("models.FindTopics", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Topics"] = topics
|
||||||
|
}
|
||||||
|
|
||||||
|
func renderLanguageStats(ctx *context.Context) {
|
||||||
|
langs, err := ctx.Repo.Repository.GetTopLanguageStats(5)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("Repo.GetTopLanguageStats", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["LanguageStats"] = langs
|
||||||
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/webhook"
|
"code.gitea.io/gitea/services/webhook"
|
||||||
|
// "code.gitea.io/gitea/"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListHooks list all hooks of a repository
|
// ListHooks list all hooks of a repository
|
||||||
|
@ -269,3 +270,57 @@ func DeleteHook(ctx *context.APIContext) {
|
||||||
}
|
}
|
||||||
ctx.Status(http.StatusNoContent)
|
ctx.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ListHookTask(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/hooks/{id}/hooktasks repository repoGetHookTasks
|
||||||
|
// ---
|
||||||
|
// summary: Get a hooktasks ***
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: id
|
||||||
|
// in: path
|
||||||
|
// description: id of the hook
|
||||||
|
// type: integer
|
||||||
|
// format: int64
|
||||||
|
// required: true
|
||||||
|
// - name: page
|
||||||
|
// in: query
|
||||||
|
// description: page number of results to return (1-based)
|
||||||
|
// type: integer
|
||||||
|
// - name: limit
|
||||||
|
// in: query
|
||||||
|
// description: page size of results
|
||||||
|
// type: integer
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/HookTaskList"
|
||||||
|
|
||||||
|
hook, err := utils.GetRepoHook(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":id"))
|
||||||
|
if err != nil {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
hookTasks, err := models.GetHookTasksByRepoIDAndHookID(ctx.Repo.Repository.ID, hook.ID, utils.GetListOptions(ctx))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetHookTasksByRepoIDAndHookID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
apiHookTasks := make([]*api.HookTask, len(hookTasks))
|
||||||
|
for i := range hookTasks {
|
||||||
|
apiHookTasks[i] = convert.ToHookTask(hookTasks[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(http.StatusOK, &apiHookTasks)
|
||||||
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package repo
|
package repo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"container/list"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
|
@ -19,11 +20,15 @@ import (
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
"code.gitea.io/gitea/modules/notification"
|
"code.gitea.io/gitea/modules/notification"
|
||||||
|
"code.gitea.io/gitea/modules/setting"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/timeutil"
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
"code.gitea.io/gitea/services/forms"
|
"code.gitea.io/gitea/services/forms"
|
||||||
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
|
|
||||||
|
pull_prepare "code.gitea.io/gitea/routers/web/repo"
|
||||||
issue_service "code.gitea.io/gitea/services/issue"
|
issue_service "code.gitea.io/gitea/services/issue"
|
||||||
pull_service "code.gitea.io/gitea/services/pull"
|
pull_service "code.gitea.io/gitea/services/pull"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
|
@ -128,7 +133,7 @@ func ListPullRequests(ctx *context.APIContext) {
|
||||||
func GetPullRequest(ctx *context.APIContext) {
|
func GetPullRequest(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index} repository repoGetPullRequest
|
// swagger:operation GET /repos/{owner}/{repo}/pulls/{index} repository repoGetPullRequest
|
||||||
// ---
|
// ---
|
||||||
// summary: Get a pull request
|
// summary: Get a pull request ***
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// parameters:
|
// parameters:
|
||||||
|
@ -172,6 +177,68 @@ func GetPullRequest(ctx *context.APIContext) {
|
||||||
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
|
ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// issue := checkPullInfo(ctx.Context)
|
||||||
|
issue := pr.Issue
|
||||||
|
if issue == nil {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pull := issue.PullRequest
|
||||||
|
// get pull commits nums
|
||||||
|
var commits *list.List
|
||||||
|
var prInfo *git.CompareInfo
|
||||||
|
if pull.HasMerged {
|
||||||
|
prInfo = pull_prepare.PrepareMergedViewPullInfo(ctx.Context, issue)
|
||||||
|
} else {
|
||||||
|
prInfo = pull_prepare.PrepareViewPullInfo(ctx.Context, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
} else if prInfo == nil {
|
||||||
|
ctx.NotFound("ViewPullCommits", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var commitNum int
|
||||||
|
commits = prInfo.Commits
|
||||||
|
commits = models.ValidateCommitsWithEmails(commits)
|
||||||
|
commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
|
||||||
|
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||||
|
commitNum = commits.Len()
|
||||||
|
|
||||||
|
//get pull changedfils
|
||||||
|
var (
|
||||||
|
// diffRepoPath string
|
||||||
|
startCommitID string
|
||||||
|
endCommitID string
|
||||||
|
gitRepo = ctx.Repo.GitRepo
|
||||||
|
)
|
||||||
|
gitRepo = ctx.Repo.GitRepo
|
||||||
|
headCommitId, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetRefCommitID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
startCommitID = prInfo.MergeBase
|
||||||
|
endCommitID = headCommitId
|
||||||
|
ctx.Data["WhitespaceBehavior"] = ""
|
||||||
|
|
||||||
|
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(gitRepo,
|
||||||
|
startCommitID, endCommitID, setting.Git.MaxGitDiffLines,
|
||||||
|
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
|
||||||
|
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var changedFiles int
|
||||||
|
changedFiles = diff.NumFiles
|
||||||
|
pr.CommitNum = commitNum
|
||||||
|
pr.ChangedFiles = changedFiles
|
||||||
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr, ctx.User))
|
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr, ctx.User))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1264,3 +1331,213 @@ func GetPullRequestCommits(ctx *context.APIContext) {
|
||||||
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link")
|
ctx.Header().Set("Access-Control-Expose-Headers", "X-Total-Count, X-PerPage, X-Total, X-PageCount, X-HasMore, Link")
|
||||||
ctx.JSON(http.StatusOK, &apiCommits)
|
ctx.JSON(http.StatusOK, &apiCommits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetPullCommits(ctx *context.APIContext) {
|
||||||
|
// issue := checkPullInfo(ctx.Context)
|
||||||
|
pr, err := models.GetPullRequestByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
|
issue := pr.Issue
|
||||||
|
if issue == nil {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pull := issue.PullRequest
|
||||||
|
|
||||||
|
var commits *list.List
|
||||||
|
var prInfo *git.CompareInfo
|
||||||
|
if pull.HasMerged {
|
||||||
|
prInfo = pull_prepare.PrepareMergedViewPullInfo(ctx.Context, issue)
|
||||||
|
} else {
|
||||||
|
prInfo = pull_prepare.PrepareViewPullInfo(ctx.Context, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
} else if prInfo == nil {
|
||||||
|
ctx.NotFound("ViewPullCommits", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["Username"] = ctx.Repo.Owner.Name
|
||||||
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
|
|
||||||
|
var commitNum int
|
||||||
|
commits = prInfo.Commits
|
||||||
|
commits = models.ValidateCommitsWithEmails(commits)
|
||||||
|
commits = models.ParseCommitsWithSignature(commits, ctx.Repo.Repository)
|
||||||
|
commits = models.ParseCommitsWithStatus(commits, ctx.Repo.Repository)
|
||||||
|
commitNum = commits.Len()
|
||||||
|
//get pull changedfils
|
||||||
|
var (
|
||||||
|
// diffRepoPath string
|
||||||
|
startCommitID string
|
||||||
|
endCommitID string
|
||||||
|
gitRepo = ctx.Repo.GitRepo
|
||||||
|
)
|
||||||
|
gitRepo = ctx.Repo.GitRepo
|
||||||
|
headCommitId, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetRefCommitID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
startCommitID = prInfo.MergeBase
|
||||||
|
endCommitID = headCommitId
|
||||||
|
ctx.Data["WhitespaceBehavior"] = ""
|
||||||
|
|
||||||
|
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(gitRepo,
|
||||||
|
startCommitID, endCommitID, setting.Git.MaxGitDiffLines,
|
||||||
|
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
|
||||||
|
gitdiff.GetWhitespaceFlag(ctx.Data["WhitespaceBehavior"].(string)))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var changedFiles int
|
||||||
|
changedFiles = diff.NumFiles
|
||||||
|
pr.CommitNum = commitNum
|
||||||
|
pr.ChangedFiles = changedFiles
|
||||||
|
ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(pr, ctx.User))
|
||||||
|
}
|
||||||
|
func GetPullFiles(ctx *context.APIContext) {
|
||||||
|
issue := checkPullInfo(ctx.Context)
|
||||||
|
if issue == nil {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pull := issue.PullRequest
|
||||||
|
|
||||||
|
whitespaceFlags := map[string]string{
|
||||||
|
"ignore-all": "-w",
|
||||||
|
"ignore-change": "-b",
|
||||||
|
"ignore-eol": "--ignore-space-at-eol",
|
||||||
|
"": ""}
|
||||||
|
|
||||||
|
var (
|
||||||
|
// diffRepoPath string
|
||||||
|
startCommitID string
|
||||||
|
endCommitID string
|
||||||
|
gitRepo *git.Repository
|
||||||
|
)
|
||||||
|
|
||||||
|
var prInfo *git.CompareInfo
|
||||||
|
if pull.HasMerged {
|
||||||
|
prInfo = pull_prepare.PrepareMergedViewPullInfo(ctx.Context, issue)
|
||||||
|
} else {
|
||||||
|
prInfo = pull_prepare.PrepareViewPullInfo(ctx.Context, issue)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
} else if prInfo == nil {
|
||||||
|
ctx.NotFound("ViewPullFiles", nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// diffRepoPath = ctx.Repo.GitRepo.Path
|
||||||
|
gitRepo = ctx.Repo.GitRepo
|
||||||
|
|
||||||
|
headCommitID, err := gitRepo.GetRefCommitID(pull.GetGitRefName())
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetRefCommitID", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
startCommitID = prInfo.MergeBase
|
||||||
|
endCommitID = headCommitID
|
||||||
|
|
||||||
|
ctx.Data["WhitespaceBehavior"] = ""
|
||||||
|
diff, err := gitdiff.GetDiffRangeWithWhitespaceBehavior(ctx.Repo.GitRepo,
|
||||||
|
startCommitID, endCommitID, setting.Git.MaxGitDiffLines,
|
||||||
|
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles,
|
||||||
|
whitespaceFlags[ctx.Data["WhitespaceBehavior"].(string)])
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetDiffRangeWithWhitespaceBehavior", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = diff.LoadComments(issue, ctx.User); err != nil {
|
||||||
|
ctx.ServerError("LoadComments", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileDiff := struct {
|
||||||
|
*gitdiff.Diff
|
||||||
|
LatestSha string
|
||||||
|
}{
|
||||||
|
Diff: diff,
|
||||||
|
LatestSha: endCommitID,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, fileDiff)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPullInfo(ctx *context.Context) *models.Issue {
|
||||||
|
issue, err := models.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||||
|
if err != nil {
|
||||||
|
// if models.IsErrIssueNotExist(err) {
|
||||||
|
// ctx.NotFound("GetIssueByIndex", err)
|
||||||
|
// } else {
|
||||||
|
// ctx.ServerError("GetIssueByIndex", err)
|
||||||
|
// }
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err = issue.LoadPoster(); err != nil {
|
||||||
|
// ctx.ServerError("LoadPoster", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := issue.LoadRepo(); err != nil {
|
||||||
|
// ctx.ServerError("LoadRepo", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
ctx.Data["Title"] = fmt.Sprintf("#%d - %s", issue.Index, issue.Title)
|
||||||
|
ctx.Data["Issue"] = issue
|
||||||
|
|
||||||
|
if !issue.IsPull {
|
||||||
|
// ctx.NotFound("ViewPullCommits", nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = issue.LoadPullRequest(); err != nil {
|
||||||
|
// ctx.ServerError("LoadPullRequest", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = issue.PullRequest.LoadHeadRepo(); err != nil {
|
||||||
|
// ctx.ServerError("LoadHeadRepo", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.IsSigned {
|
||||||
|
// Update issue-user.
|
||||||
|
if err = issue.ReadBy(ctx.User.ID); err != nil {
|
||||||
|
// ctx.ServerError("ReadBy", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return issue
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPullIssues(ctx *context.APIContext) {
|
||||||
|
issue := checkPullInfo(ctx.Context)
|
||||||
|
if issue == nil {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies, err := issue.BlockingDependencies()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("BlockingDependencies", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, dependencies)
|
||||||
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"code.gitea.io/gitea/modules/validation"
|
"code.gitea.io/gitea/modules/validation"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
|
"code.gitea.io/gitea/services/gitdiff"
|
||||||
repo_service "code.gitea.io/gitea/services/repository"
|
repo_service "code.gitea.io/gitea/services/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1050,3 +1051,160 @@ func GetIssueTemplates(ctx *context.APIContext) {
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, ctx.IssueTemplatesFromDefaultBranch())
|
ctx.JSON(http.StatusOK, ctx.IssueTemplatesFromDefaultBranch())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MustBeNotEmpty render when a repo is a empty git dir
|
||||||
|
func MustBeNotEmpty(ctx *context.Context) {
|
||||||
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
ctx.NotFound("MustBeNotEmpty", nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//SetEditorConfigIfExists set editor config as render variable
|
||||||
|
func SetEditorconfigIfExists(ctx *context.Context) {
|
||||||
|
if ctx.Repo.Repository.IsEmpty {
|
||||||
|
ctx.Data["Edidorconfig"] = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ec, err := ctx.Repo.GetEditorconfig()
|
||||||
|
|
||||||
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
|
description := fmt.Sprintf("Error while getting .Editconfig file: %v", err)
|
||||||
|
if err := models.CreateRepositoryNotice(description); err != nil {
|
||||||
|
ctx.ServerError("CreateRepositoryNotice", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Data["Editorconfig"] = ec
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetdiffViewStyle set diff style as render variable
|
||||||
|
func SetDiffViewStyle(ctx *context.Context) {
|
||||||
|
|
||||||
|
queryStyle := ctx.Query("style")
|
||||||
|
if !ctx.IsSigned {
|
||||||
|
ctx.Data["IsSplitStyle"] = queryStyle == "split"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
userStyle = ctx.User.DiffViewStyle
|
||||||
|
style string
|
||||||
|
)
|
||||||
|
|
||||||
|
if queryStyle == "unified" || queryStyle == "split" {
|
||||||
|
style = queryStyle
|
||||||
|
} else if userStyle == "unified" || userStyle == "split" {
|
||||||
|
style = userStyle
|
||||||
|
} else {
|
||||||
|
style = "unified"
|
||||||
|
}
|
||||||
|
ctx.Data["IsSplitStyle"] = style == "split"
|
||||||
|
if err := ctx.User.UpdateDiffViewStyle(style); err != nil {
|
||||||
|
ctx.ServerError("ErrUpdateDiffViewStyle", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareCompareDiff renders compare diff page
|
||||||
|
func PrepareCompareDiff(
|
||||||
|
ctx *context.Context,
|
||||||
|
headUser *models.User,
|
||||||
|
headRepo *models.Repository,
|
||||||
|
headGitRepo *git.Repository,
|
||||||
|
compareInfo *git.CompareInfo,
|
||||||
|
baseBranch, headBranch string) bool {
|
||||||
|
|
||||||
|
var (
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
// Get diff information.
|
||||||
|
ctx.Data["CommitRepoLink"] = headRepo.Link()
|
||||||
|
|
||||||
|
headCommitID := headBranch
|
||||||
|
if ctx.Data["HeadIsCommit"] == false {
|
||||||
|
if ctx.Data["HeadIsTag"] == true {
|
||||||
|
headCommitID, err = headGitRepo.GetTagCommitID(headBranch)
|
||||||
|
} else {
|
||||||
|
headCommitID, err = headGitRepo.GetBranchCommitID(headBranch)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetRefCommitID", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.Data["AfterCommitID"] = headCommitID
|
||||||
|
|
||||||
|
if headCommitID == compareInfo.MergeBase {
|
||||||
|
ctx.Data["IsNothingToCompare"] = true
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
repoPath := models.RepoPath(headUser.Name, headRepo.Name)
|
||||||
|
|
||||||
|
// gitRepo, _ := git.OpenRepository(repoPath)
|
||||||
|
gitRepo, _ := git.OpenRepository(repoPath)
|
||||||
|
|
||||||
|
diff, err := gitdiff.GetDiffRange(gitRepo,
|
||||||
|
compareInfo.MergeBase, headCommitID, setting.Git.MaxGitDiffLines,
|
||||||
|
setting.Git.MaxGitDiffLineCharacters, setting.Git.MaxGitDiffFiles)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetDiffRange", err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
ctx.Data["Diff"] = diff
|
||||||
|
ctx.Data["DiffNotAvailable"] = diff.NumFiles == 0
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
type CompareCommit struct {
|
||||||
|
*git.Commit
|
||||||
|
Sha string
|
||||||
|
ParentShas []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func CompareDiff(ctx *context.APIContext) {
|
||||||
|
|
||||||
|
var form api.CreatePullRequestOption
|
||||||
|
|
||||||
|
headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch := parseCompareInfo(ctx, form)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer headGitRepo.Close()
|
||||||
|
|
||||||
|
_ = PrepareCompareDiff(ctx.Context, headUser, headRepo, headGitRepo, compareInfo, baseBranch, headBranch)
|
||||||
|
if ctx.Written() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make([]CompareCommit, 0)
|
||||||
|
for commit := compareInfo.Commits.Front(); commit != nil; commit = commit.Next() {
|
||||||
|
temp := commit.Value.(*git.Commit)
|
||||||
|
compareCommit := CompareCommit{
|
||||||
|
temp,
|
||||||
|
temp.ID.String(),
|
||||||
|
make([]string, 0),
|
||||||
|
}
|
||||||
|
for i := 0; i < len(temp.Parents); i++ {
|
||||||
|
compareCommit.ParentShas = append(compareCommit.ParentShas, temp.Parents[i].String())
|
||||||
|
}
|
||||||
|
result = append(result, compareCommit)
|
||||||
|
}
|
||||||
|
|
||||||
|
different := struct {
|
||||||
|
Commits []CompareCommit
|
||||||
|
Diff interface{}
|
||||||
|
LatestSha string
|
||||||
|
}{
|
||||||
|
Commits: result,
|
||||||
|
Diff: ctx.Context.Data["Diff"],
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(different.Commits) != 0 {
|
||||||
|
different.LatestSha = different.Commits[0].Sha
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.JSON(200, different)
|
||||||
|
}
|
||||||
|
|
|
@ -7,11 +7,14 @@ package repo
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
"code.gitea.io/gitea/modules/web"
|
"code.gitea.io/gitea/modules/web"
|
||||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||||
|
@ -61,6 +64,21 @@ func ListTags(ctx *context.APIContext) {
|
||||||
apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i])
|
apiTags[i] = convert.ToTag(ctx.Repo.Repository, tags[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tagsCountTotal, err := ctx.Repo.GitRepo.GetTagCount()
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetTagCount", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pageCount := int(math.Ceil(float64(tagsCountTotal) / float64(listOpts.PageSize)))
|
||||||
|
ctx.Header().Set("X-Page", strconv.Itoa(listOpts.Page))
|
||||||
|
ctx.Header().Set("X-PerPage", strconv.Itoa(listOpts.PageSize))
|
||||||
|
ctx.Header().Set("X-Total", strconv.FormatInt(tagsCountTotal, 10))
|
||||||
|
ctx.Header().Set("X-PageCount", strconv.Itoa(pageCount))
|
||||||
|
ctx.Header().Set("X-HasMore", strconv.FormatBool(listOpts.Page < pageCount))
|
||||||
|
|
||||||
|
ctx.SetLinkHeader(int(tagsCountTotal), listOpts.PageSize)
|
||||||
|
ctx.Header().Set("X-Total-Count", fmt.Sprintf("%d", tagsCountTotal))
|
||||||
|
|
||||||
ctx.JSON(http.StatusOK, &apiTags)
|
ctx.JSON(http.StatusOK, &apiTags)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,3 +278,42 @@ func DeleteTag(ctx *context.APIContext) {
|
||||||
|
|
||||||
ctx.Status(http.StatusNoContent)
|
ctx.Status(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func BranchTagCount(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/branch_tag_count repository repoBranchTagCount
|
||||||
|
// ---
|
||||||
|
// summary: List a repository's tags***
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/RepoBranchAndTagCount"
|
||||||
|
|
||||||
|
tags, err := ctx.Repo.GitRepo.GetTagInfos(0, 0) // tags info
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetTags", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
repo := ctx.Repo.Repository
|
||||||
|
_, countAll, err := repo_module.GetBranches(repo, -1, -1) //get count of the branch
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetBranches", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
result := api.RepoBranchAndTagCount{
|
||||||
|
BranchCount: countAll,
|
||||||
|
TagCount: len(tags),
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, result)
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,475 @@
|
||||||
|
package repo
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/markup"
|
||||||
|
"code.gitea.io/gitea/modules/markup/markdown"
|
||||||
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/modules/web"
|
||||||
|
webWiki "code.gitea.io/gitea/routers/web/repo"
|
||||||
|
|
||||||
|
wiki_service "code.gitea.io/gitea/services/wiki"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListWikiPages(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/wikies repository repoWikiList
|
||||||
|
// ---
|
||||||
|
// summary: List the wikies in a repository
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/WikiList"
|
||||||
|
|
||||||
|
if !ctx.Repo.Repository.HasWiki() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wikiCloneWiki := ctx.Repo.Repository.WikiCloneLink()
|
||||||
|
wikiRepo, commit, err := webWiki.FindWikiRepoCommit(ctx.Context)
|
||||||
|
if err != nil {
|
||||||
|
if wikiRepo != nil {
|
||||||
|
wikiRepo.Close()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if wikiRepo != nil {
|
||||||
|
wikiRepo.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
entries, err := commit.ListEntries()
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("entries", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
pages := make([]api.WikiesResponse, 0, len(entries))
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsRegular() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
lastCommit, firstCommit, _ := wikiRepo.GetFirstAndLastCommitByPath("master", entry.Name())
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetCommitByPath", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wikiName, err := wiki_service.FilenameToName(entry.Name())
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrWikiInvalidFileName(err) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ctx.ServerError("FilenameToName", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
pages = append(pages, api.WikiesResponse{
|
||||||
|
WikiCloneLink: api.CloneLink{
|
||||||
|
HTTPS: wikiCloneWiki.HTTPS,
|
||||||
|
SSH: wikiCloneWiki.SSH,
|
||||||
|
},
|
||||||
|
WikiMeta: api.WikiMeta{
|
||||||
|
Name: wikiName,
|
||||||
|
Commit: api.WikiCommit{
|
||||||
|
Author: api.WikiUser{
|
||||||
|
Name: lastCommit.Author.Name,
|
||||||
|
Email: lastCommit.Author.Email,
|
||||||
|
When: lastCommit.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
Commiter: api.WikiUser{
|
||||||
|
Name: lastCommit.Committer.Name,
|
||||||
|
Email: lastCommit.Committer.Email,
|
||||||
|
When: lastCommit.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
ID: lastCommit.ID.String(),
|
||||||
|
Message: lastCommit.Message(),
|
||||||
|
},
|
||||||
|
FirstCommit: api.WikiCommit{
|
||||||
|
Author: api.WikiUser{
|
||||||
|
Name: firstCommit.Author.Name,
|
||||||
|
Email: firstCommit.Author.Email,
|
||||||
|
When: firstCommit.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
Commiter: api.WikiUser{
|
||||||
|
Name: firstCommit.Committer.Name,
|
||||||
|
Email: firstCommit.Committer.Email,
|
||||||
|
When: firstCommit.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
ID: firstCommit.ID.String(),
|
||||||
|
Message: firstCommit.Message(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
//sort by time
|
||||||
|
sort.Slice(pages, func(i, j int) bool {
|
||||||
|
return pages[i].FirstCommit.Author.When > pages[j].FirstCommit.Author.When
|
||||||
|
})
|
||||||
|
ctx.JSON(http.StatusOK, pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateWiki(ctx *context.APIContext) {
|
||||||
|
// swagger:operation POST /repos/{owner}/{repo}/wikies repository repoCreateWiki
|
||||||
|
// ---
|
||||||
|
// summary: Create a wiki in a repository
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/WikiOption"
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/Wiki"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.WikiOption)
|
||||||
|
|
||||||
|
err := wiki_service.CheckFile(form.Name)
|
||||||
|
if err != nil {
|
||||||
|
ctx.FileNameError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
wikiName := wiki_service.NormalizeWikiName(form.Name)
|
||||||
|
wikiCloneLink := ctx.Repo.Repository.WikiCloneLink()
|
||||||
|
|
||||||
|
if err := wiki_service.AddWikiPage(ctx.User, ctx.Repo.Repository, wikiName, form.Content, form.CommitMessage); err != nil {
|
||||||
|
|
||||||
|
if models.IsErrWikiReservedName(err) {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "WikiNameIsReservedPage", "wiki名称是被保留的.")
|
||||||
|
} else if models.IsErrWikiAlreadyExist(err) {
|
||||||
|
ctx.Error(http.StatusConflict, "WikiNameAlreadyExist", "wiki名称已存在")
|
||||||
|
} else {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "AddWikiPage", err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wikiRepo, commit, _ := webWiki.FindWikiRepoCommit(ctx.Context)
|
||||||
|
data, entry, pageFilename, _ := webWiki.WikiContentsByName(ctx.Context, commit, form.Name)
|
||||||
|
metas := ctx.Repo.Repository.ComposeDocumentMetas()
|
||||||
|
|
||||||
|
var rctx = &markup.RenderContext{
|
||||||
|
URLPrefix: ctx.Repo.RepoLink,
|
||||||
|
Metas: metas,
|
||||||
|
IsWiki: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil {
|
||||||
|
if wikiRepo != nil {
|
||||||
|
wikiRepo.Close()
|
||||||
|
}
|
||||||
|
ctx.ServerError("Render", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||||
|
c, err := wikiRepo.GetCommitByPath(entry.Name())
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrWikiInvalidFileName(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
wiki := api.WikiResponse{
|
||||||
|
WikiCloneLink: api.CloneLink{
|
||||||
|
HTTPS: wikiCloneLink.HTTPS,
|
||||||
|
SSH: wikiCloneLink.SSH,
|
||||||
|
},
|
||||||
|
WikiMeta: api.WikiMeta{
|
||||||
|
Name: form.Name,
|
||||||
|
Commit: api.WikiCommit{
|
||||||
|
Author: api.WikiUser{
|
||||||
|
Name: c.Author.Name,
|
||||||
|
Email: c.Author.Email,
|
||||||
|
When: c.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
Commiter: api.WikiUser{
|
||||||
|
Name: c.Committer.Name,
|
||||||
|
Email: c.Committer.Email,
|
||||||
|
When: c.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
ID: c.ID.String(),
|
||||||
|
Message: c.Message(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CommitCounts: commitsCount,
|
||||||
|
MdContent: string(data),
|
||||||
|
SimpleContent: buf.String(),
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, wiki)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetWiki(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/wikies/{pagename} repository repoGetWiki
|
||||||
|
// ---
|
||||||
|
// summary: Get a Wiki
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: pagename
|
||||||
|
// in: path
|
||||||
|
// description: name of the wikipage
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/Wiki"
|
||||||
|
|
||||||
|
wikiRepo, commit, _ := webWiki.FindWikiRepoCommit(ctx.Context)
|
||||||
|
|
||||||
|
wikiCloneWiki := ctx.Repo.Repository.WikiCloneLink()
|
||||||
|
|
||||||
|
// pageName := wiki_service.NormalizeWikiName(ctx.Context.Params(":page"))
|
||||||
|
pageName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
|
||||||
|
if len(pageName) == 0 {
|
||||||
|
pageName = "Home"
|
||||||
|
}
|
||||||
|
data, entry, pageFilename, noEntry := webWiki.WikiContentsByName(ctx.Context, commit, pageName)
|
||||||
|
if noEntry {
|
||||||
|
ctx.NotFound()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if entry == nil || ctx.Written() {
|
||||||
|
if wikiRepo != nil {
|
||||||
|
wikiRepo.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metas := ctx.Repo.Repository.ComposeDocumentMetas()
|
||||||
|
|
||||||
|
var rctx = &markup.RenderContext{
|
||||||
|
URLPrefix: ctx.Repo.RepoLink,
|
||||||
|
Metas: metas,
|
||||||
|
IsWiki: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil {
|
||||||
|
if wikiRepo != nil {
|
||||||
|
wikiRepo.Close()
|
||||||
|
}
|
||||||
|
ctx.ServerError("Render", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c, err := wikiRepo.GetCommitByPath(entry.Name())
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrWikiInvalidFileName(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||||
|
wiki := api.WikiResponse{
|
||||||
|
WikiCloneLink: api.CloneLink{
|
||||||
|
HTTPS: wikiCloneWiki.HTTPS,
|
||||||
|
SSH: wikiCloneWiki.SSH,
|
||||||
|
},
|
||||||
|
WikiMeta: api.WikiMeta{
|
||||||
|
Name: pageName,
|
||||||
|
Commit: api.WikiCommit{
|
||||||
|
Author: api.WikiUser{
|
||||||
|
Name: c.Author.Name,
|
||||||
|
Email: c.Author.Email,
|
||||||
|
When: c.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
Commiter: api.WikiUser{
|
||||||
|
Name: c.Committer.Name,
|
||||||
|
Email: c.Committer.Email,
|
||||||
|
When: c.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
ID: c.ID.String(),
|
||||||
|
Message: c.Message(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CommitCounts: commitsCount,
|
||||||
|
MdContent: string(data),
|
||||||
|
SimpleContent: buf.String(),
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, wiki)
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditWiki(ctx *context.APIContext) {
|
||||||
|
// swagger:operation PATCH /repos/{owner}/{repo}/wikies/{pagename} repository repoEditWiki
|
||||||
|
// ---
|
||||||
|
// summary: Edit a wiki in a repository
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: pagename
|
||||||
|
// in: path
|
||||||
|
// description: name of the wiki
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: body
|
||||||
|
// in: body
|
||||||
|
// schema:
|
||||||
|
// "$ref": "#/definitions/WikiOption"
|
||||||
|
// responses:
|
||||||
|
// "201":
|
||||||
|
// "$ref": "#/responses/Wiki"
|
||||||
|
|
||||||
|
form := web.GetForm(ctx).(*api.WikiOption)
|
||||||
|
oldWikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
|
||||||
|
newWikiName := wiki_service.NormalizeWikiName(form.Name)
|
||||||
|
err1 := wiki_service.CheckFile(newWikiName)
|
||||||
|
if err1 != nil {
|
||||||
|
ctx.FileNameError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
wikiRepo, commit, _ := webWiki.FindWikiRepoCommit(ctx.Context)
|
||||||
|
|
||||||
|
if _, _, _, noEntry := webWiki.WikiContentsByName(ctx.Context, commit, oldWikiName); noEntry {
|
||||||
|
ctx.Error(http.StatusNotFound, "WikiNotFound", "wiki不存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, _, _, noEntry := webWiki.WikiContentsByName(ctx.Context, commit, newWikiName); oldWikiName != newWikiName && !noEntry {
|
||||||
|
ctx.Error(http.StatusConflict, "WikiNameAlreadyExist", "wiki名称已存在")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(form.CommitMessage) == 0 {
|
||||||
|
form.CommitMessage = ctx.Tr("repo.editor.update", form.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := wiki_service.EditWikiPage(ctx.User, ctx.Repo.Repository, oldWikiName, newWikiName, form.Content, form.CommitMessage); err != nil {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "EditWikiPage", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, newCommit, _ := webWiki.FindWikiRepoCommit(ctx.Context)
|
||||||
|
data, entry, pageFilename, _ := webWiki.WikiContentsByName(ctx.Context, newCommit, newWikiName)
|
||||||
|
c, err := wikiRepo.GetCommitByPath(entry.Name())
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrWikiInvalidFileName(err) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metas := ctx.Repo.Repository.ComposeDocumentMetas()
|
||||||
|
// PageContent := markdown.RenderWiki(data, ctx.Repo.RepoLink, metas)
|
||||||
|
|
||||||
|
var rctx = &markup.RenderContext{
|
||||||
|
URLPrefix: ctx.Repo.RepoLink,
|
||||||
|
Metas: metas,
|
||||||
|
IsWiki: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf strings.Builder
|
||||||
|
if err := markdown.Render(rctx, bytes.NewReader(data), &buf); err != nil {
|
||||||
|
if wikiRepo != nil {
|
||||||
|
wikiRepo.Close()
|
||||||
|
}
|
||||||
|
ctx.ServerError("Render", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename)
|
||||||
|
|
||||||
|
wiki := api.WikiResponse{
|
||||||
|
WikiMeta: api.WikiMeta{
|
||||||
|
Name: form.Name,
|
||||||
|
Commit: api.WikiCommit{
|
||||||
|
Author: api.WikiUser{
|
||||||
|
Name: c.Author.Name,
|
||||||
|
Email: c.Author.Email,
|
||||||
|
When: c.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
Commiter: api.WikiUser{
|
||||||
|
Name: c.Committer.Name,
|
||||||
|
Email: c.Committer.Email,
|
||||||
|
When: c.Author.When.Unix(),
|
||||||
|
},
|
||||||
|
ID: c.ID.String(),
|
||||||
|
Message: c.Message(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
CommitCounts: commitsCount,
|
||||||
|
MdContent: string(data),
|
||||||
|
SimpleContent: buf.String(),
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, wiki)
|
||||||
|
}
|
||||||
|
func DeleteWiki(ctx *context.APIContext) {
|
||||||
|
// swagger:operation DELETE /repos/{owner}/{repo}/wikies/{pagename} repository repoDeleteWiki
|
||||||
|
// ---
|
||||||
|
// summary: Delete a wiki in a repository
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: pagename
|
||||||
|
// in: path
|
||||||
|
// description: name of the wiki
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "204":
|
||||||
|
// "$ref": "#/responses/empty"
|
||||||
|
// "500":
|
||||||
|
// "$ref": "#/responses/noFound"
|
||||||
|
|
||||||
|
wikiName := wiki_service.NormalizeWikiName(ctx.Params(":page"))
|
||||||
|
if len(wikiName) == 0 {
|
||||||
|
wikiName = "Home"
|
||||||
|
}
|
||||||
|
|
||||||
|
err2 := wiki_service.DeleteWikiPage(ctx.User, ctx.Repo.Repository, wikiName)
|
||||||
|
if err2 != nil {
|
||||||
|
ctx.FileExistError()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,228 @@
|
||||||
|
package reporter
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetActivity(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /activity activity activity
|
||||||
|
// ---
|
||||||
|
// summary: Statistics of commit and pull request data,Platform required data **
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: from
|
||||||
|
// in: query
|
||||||
|
// description: Query begin timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: to
|
||||||
|
// in: query
|
||||||
|
// description: Query end timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/PlatformDTO"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
opt := GetParamOption(ctx)
|
||||||
|
|
||||||
|
if opt == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := models.GetActivity(opt)
|
||||||
|
fmt.Println("-==========list====\n", list)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetActivity", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, list)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PrjectDTO struct {
|
||||||
|
Project interface{} `json:"project"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetActivityProject(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /activity/project activity project
|
||||||
|
// ---
|
||||||
|
// summary: Statistics of submitted data by project **
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: from
|
||||||
|
// in: query
|
||||||
|
// description: Query begin timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: to
|
||||||
|
// in: query
|
||||||
|
// description: Query end timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: top
|
||||||
|
// in: query
|
||||||
|
// description: Display the previous n records
|
||||||
|
// type: integer
|
||||||
|
// required: false
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/PlatformDTO"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
opt := GetParamOption(ctx)
|
||||||
|
if opt == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
list, err := models.GetActivityProject(opt)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetActivityProject", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data := PrjectDTO{
|
||||||
|
Project: list,
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DevelopDTO struct {
|
||||||
|
Develop interface{} `json:"develop"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetActivityDevelop(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /activity/develop activity develop
|
||||||
|
// ---
|
||||||
|
// summary: Statistics of submitted data by developers **
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: from
|
||||||
|
// in: query
|
||||||
|
// description: Query begin timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: to
|
||||||
|
// in: query
|
||||||
|
// description: Query end timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: top
|
||||||
|
// in: query
|
||||||
|
// description: Display the previous n records
|
||||||
|
// type: integer
|
||||||
|
// required: false
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/PlatformDTO"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
opt := GetParamOption(ctx)
|
||||||
|
if opt == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
list, err := models.GetActivityDevelop(opt)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetActivityDevelop", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
data := DevelopDTO{
|
||||||
|
Develop: list,
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, data)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetParamOption(ctx *context.APIContext) (opt *models.GetGetActivityOptions) {
|
||||||
|
Layout := "2006-01-02 15:04:05"
|
||||||
|
//##top
|
||||||
|
top := ctx.QueryInt64("top")
|
||||||
|
if top <= 0 {
|
||||||
|
top = 5
|
||||||
|
} else if top >= 20 {
|
||||||
|
top = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
//##from
|
||||||
|
FromDate, err := strconv.ParseInt(ctx.QueryTrim("from"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusBadRequest, "param.from", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if FromDate <= 0 {
|
||||||
|
ctx.Error(http.StatusBadRequest, "param.from", fmt.Errorf("请指定from参数"))
|
||||||
|
return
|
||||||
|
//from=time.Now().Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
from := time.Unix(FromDate, 0).Format(Layout)
|
||||||
|
//fmt.Println("********from:",from," ", FromDate," convert:",time.Unix( FromDate,0).Format("2006-01-02 15:04:05"))
|
||||||
|
|
||||||
|
//##to
|
||||||
|
ToDate, err := strconv.ParseInt(ctx.QueryTrim("to"), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusBadRequest, "param.to ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ToDate <= 0 {
|
||||||
|
ctx.Error(http.StatusBadRequest, "param.to", fmt.Errorf("请指定to参数"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
to := time.Unix(ToDate, 0).Format(Layout)
|
||||||
|
|
||||||
|
//fmt.Println("********to:",to ," ", ToDate," convert:",time.Unix( ToDate,0).Format(Layout))
|
||||||
|
opt = &models.GetGetActivityOptions{
|
||||||
|
FromDateUnix: FromDate,
|
||||||
|
ToDateUnix: ToDate,
|
||||||
|
FromDate: from,
|
||||||
|
ToDate: to,
|
||||||
|
Top: top,
|
||||||
|
}
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetContributors(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/contributors repository contributors
|
||||||
|
// ---
|
||||||
|
// summary: Get all builder information in the repository ***
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/ContributorsDtoList"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/noFound"
|
||||||
|
|
||||||
|
opts := models.GetContributorsOptionsExt{
|
||||||
|
UserId: ctx.Repo.Owner.ID,
|
||||||
|
RepoId: ctx.Repo.Repository.ID,
|
||||||
|
}
|
||||||
|
list, err := models.GetContributors(opts)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Error(http.StatusNotFound, "not found the contributors", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, list)
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,3 +22,10 @@ type swaggerResponseAccessToken struct {
|
||||||
// in:body
|
// in:body
|
||||||
Body api.AccessToken `json:"body"`
|
Body api.AccessToken `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PlatformDTO
|
||||||
|
// swagger:response PlatformDTO
|
||||||
|
type swaggerReponsePlatformDTO struct {
|
||||||
|
//in:body
|
||||||
|
Body models.PlatformDTO `json:"body"`
|
||||||
|
}
|
||||||
|
|
|
@ -23,6 +23,9 @@ type swaggerParameterBodies struct {
|
||||||
// in:body
|
// in:body
|
||||||
DeleteEmailOption api.DeleteEmailOption
|
DeleteEmailOption api.DeleteEmailOption
|
||||||
|
|
||||||
|
// in:body
|
||||||
|
WikiOption api.WikiOption
|
||||||
|
|
||||||
// in:body
|
// in:body
|
||||||
CreateHookOption api.CreateHookOption
|
CreateHookOption api.CreateHookOption
|
||||||
// in:body
|
// in:body
|
||||||
|
|
|
@ -5,7 +5,9 @@
|
||||||
package swagger
|
package swagger
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
api "code.gitea.io/gitea/modules/structs"
|
api "code.gitea.io/gitea/modules/structs"
|
||||||
|
"code.gitea.io/gitea/routers/api/v1/viewfile"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Repository
|
// Repository
|
||||||
|
@ -50,6 +52,26 @@ type swaggerResponseBranchProtectionList struct {
|
||||||
Body []api.BranchProtection `json:"body"`
|
Body []api.BranchProtection `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Wiki
|
||||||
|
// swagger:response Wiki
|
||||||
|
type swaggerResponseWiki struct {
|
||||||
|
// in:body
|
||||||
|
Body api.WikiResponse `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// WikiList
|
||||||
|
// swagger:response WikiList
|
||||||
|
type swaggerResponseWikiList struct {
|
||||||
|
// in:body
|
||||||
|
Body api.WikiesResponse `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RepoBranchAndTagCount
|
||||||
|
// swagger:response RepoBranchAndTagCount
|
||||||
|
type swaggerResponseRepoBranchAndTagCount struct {
|
||||||
|
Body api.RepoBranchAndTagCount `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
// TagList
|
// TagList
|
||||||
// swagger:response TagList
|
// swagger:response TagList
|
||||||
type swaggerResponseTagList struct {
|
type swaggerResponseTagList struct {
|
||||||
|
@ -99,6 +121,13 @@ type swaggerResponseHookList struct {
|
||||||
Body []api.Hook `json:"body"`
|
Body []api.Hook `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HookTaskList
|
||||||
|
// swagger:response HookTaskList
|
||||||
|
type swaggerResponseHookTaskList struct {
|
||||||
|
// in:body
|
||||||
|
Body []api.HookTask `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
// GitHook
|
// GitHook
|
||||||
// swagger:response GitHook
|
// swagger:response GitHook
|
||||||
type swaggerResponseGitHook struct {
|
type swaggerResponseGitHook struct {
|
||||||
|
@ -316,3 +345,17 @@ type swaggerCombinedStatus struct {
|
||||||
// in: body
|
// in: body
|
||||||
Body api.CombinedStatus `json:"body"`
|
Body api.CombinedStatus `json:"body"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SearchFileItemList
|
||||||
|
// swagger:response SearchFileItem
|
||||||
|
type swaggerSearchFileItemList struct {
|
||||||
|
// in: body
|
||||||
|
Body []viewfile.SearchFileItem `json:"body"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ContributorsDtoList
|
||||||
|
// swagger:response ContributorsDto
|
||||||
|
type swaggerResponseContributorsDtoList struct {
|
||||||
|
// in: body
|
||||||
|
Body []models.ContributorsDto `json:"body"`
|
||||||
|
}
|
||||||
|
|
|
@ -132,7 +132,7 @@ func GetAuthenticatedUser(ctx *context.APIContext) {
|
||||||
func GetUserHeatmapData(ctx *context.APIContext) {
|
func GetUserHeatmapData(ctx *context.APIContext) {
|
||||||
// swagger:operation GET /users/{username}/heatmap user userGetHeatmapData
|
// swagger:operation GET /users/{username}/heatmap user userGetHeatmapData
|
||||||
// ---
|
// ---
|
||||||
// summary: Get a user's heatmap
|
// summary: Get a user's heatmap ***
|
||||||
// produces:
|
// produces:
|
||||||
// - application/json
|
// - application/json
|
||||||
// parameters:
|
// parameters:
|
||||||
|
@ -141,18 +141,41 @@ func GetUserHeatmapData(ctx *context.APIContext) {
|
||||||
// description: username of user to get
|
// description: username of user to get
|
||||||
// type: string
|
// type: string
|
||||||
// required: true
|
// required: true
|
||||||
|
// - name: start
|
||||||
|
// in: query
|
||||||
|
// description: Query start timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: end
|
||||||
|
// in: query
|
||||||
|
// description: Query end timestamp
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
// responses:
|
// responses:
|
||||||
// "200":
|
// "200":
|
||||||
// "$ref": "#/responses/UserHeatmapData"
|
// "$ref": "#/responses/UserHeatmapData"
|
||||||
// "404":
|
// "404":
|
||||||
// "$ref": "#/responses/notFound"
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
user := GetUserByParams(ctx)
|
// user := GetUserByParams(ctx)
|
||||||
if ctx.Written() {
|
|
||||||
|
timeStampOptions := utils.GetTimestampOptions(ctx)
|
||||||
|
|
||||||
|
// get the user to throw a err if it does not exist
|
||||||
|
// user, err := models.GetUserByName(ctx.Params(":username"))
|
||||||
|
user, err := models.GetUserByName(ctx.Params(":username"))
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrUserNotExist(err) {
|
||||||
|
ctx.Status(http.StatusNotFound)
|
||||||
|
} else {
|
||||||
|
ctx.Error(http.StatusInternalServerError, "GetUserByName", err)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
heatmap, err := models.GetUserHeatmapDataByUser(user, ctx.User)
|
// heatmap, err := models.GetUserHeatmapDataByUser(user, ctx.User)
|
||||||
|
heatmap, err := models.GetUserHeatMapDataByUserWithTimeStamp(user, timeStampOptions)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err)
|
ctx.Error(http.StatusInternalServerError, "GetUserHeatmapDataByUser", err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -66,6 +66,10 @@ func CheckCreateHookOption(ctx *context.APIContext, form *api.CreateHookOption)
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
|
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if !models.IsValidHookHttpMethod(form.Config["http_method"]) {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid http method")
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,6 +216,10 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
|
||||||
if url, ok := form.Config["url"]; ok {
|
if url, ok := form.Config["url"]; ok {
|
||||||
w.URL = url
|
w.URL = url
|
||||||
}
|
}
|
||||||
|
if secret, ok := form.Config["secret"]; ok {
|
||||||
|
w.Secret = secret
|
||||||
|
}
|
||||||
|
|
||||||
if ct, ok := form.Config["content_type"]; ok {
|
if ct, ok := form.Config["content_type"]; ok {
|
||||||
if !models.IsValidHookContentType(ct) {
|
if !models.IsValidHookContentType(ct) {
|
||||||
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
|
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid content type")
|
||||||
|
@ -220,6 +228,14 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
|
||||||
w.ContentType = models.ToHookContentType(ct)
|
w.ContentType = models.ToHookContentType(ct)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if hw, ok := form.Config["http_method"]; ok {
|
||||||
|
if !models.IsValidHookHttpMethod(hw) {
|
||||||
|
ctx.Error(http.StatusUnprocessableEntity, "", "Invalid http method")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
w.HTTPMethod = hw
|
||||||
|
}
|
||||||
|
|
||||||
if w.Type == models.SLACK {
|
if w.Type == models.SLACK {
|
||||||
if channel, ok := form.Config["channel"]; ok {
|
if channel, ok := form.Config["channel"]; ok {
|
||||||
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
json := jsoniter.ConfigCompatibleWithStandardLibrary
|
||||||
|
@ -248,16 +264,29 @@ func editHook(ctx *context.APIContext, form *api.EditHookOption, w *models.Webho
|
||||||
w.Create = util.IsStringInSlice(string(models.HookEventCreate), form.Events, true)
|
w.Create = util.IsStringInSlice(string(models.HookEventCreate), form.Events, true)
|
||||||
w.Push = util.IsStringInSlice(string(models.HookEventPush), form.Events, true)
|
w.Push = util.IsStringInSlice(string(models.HookEventPush), form.Events, true)
|
||||||
w.PullRequest = util.IsStringInSlice(string(models.HookEventPullRequest), form.Events, true)
|
w.PullRequest = util.IsStringInSlice(string(models.HookEventPullRequest), form.Events, true)
|
||||||
w.Create = util.IsStringInSlice(string(models.HookEventCreate), form.Events, true)
|
// w.Create = util.IsStringInSlice(string(models.HookEventCreate), form.Events, true)
|
||||||
w.Delete = util.IsStringInSlice(string(models.HookEventDelete), form.Events, true)
|
w.Delete = util.IsStringInSlice(string(models.HookEventDelete), form.Events, true)
|
||||||
w.Fork = util.IsStringInSlice(string(models.HookEventFork), form.Events, true)
|
w.Fork = util.IsStringInSlice(string(models.HookEventFork), form.Events, true)
|
||||||
w.Issues = util.IsStringInSlice(string(models.HookEventIssues), form.Events, true)
|
w.Issues = util.IsStringInSlice(string(models.HookEventIssues), form.Events, true)
|
||||||
w.IssueComment = util.IsStringInSlice(string(models.HookEventIssueComment), form.Events, true)
|
w.Issues = issuesHook(form.Events, "issues_only")
|
||||||
|
w.IssueAssign = issuesHook(form.Events, string(models.HookEventIssueAssign))
|
||||||
|
w.IssueLabel = issuesHook(form.Events, string(models.HookEventIssueLabel))
|
||||||
|
w.IssueMilestone = issuesHook(form.Events, string(models.HookEventIssueMilestone))
|
||||||
|
w.IssueComment = issuesHook(form.Events, string(models.HookEventIssueComment))
|
||||||
|
// w.IssueComment = util.IsStringInSlice(string(models.HookEventIssueComment), form.Events, true)
|
||||||
w.Push = util.IsStringInSlice(string(models.HookEventPush), form.Events, true)
|
w.Push = util.IsStringInSlice(string(models.HookEventPush), form.Events, true)
|
||||||
w.PullRequest = util.IsStringInSlice(string(models.HookEventPullRequest), form.Events, true)
|
// w.PullRequest = util.IsStringInSlice(string(models.HookEventPullRequest), form.Events, true)
|
||||||
|
w.PullRequest = pullHook(form.Events, "pull_request_only")
|
||||||
|
w.PullRequestAssign = pullHook(form.Events, string(models.HookEventPullRequestAssign))
|
||||||
|
w.PullRequestLabel = pullHook(form.Events, string(models.HookEventPullRequestLabel))
|
||||||
|
w.PullRequestMilestone = pullHook(form.Events, string(models.HookEventPullRequestMilestone))
|
||||||
|
w.PullRequestComment = pullHook(form.Events, string(models.HookEventPullRequestComment))
|
||||||
|
w.PullRequestReview = pullHook(form.Events, "pull_request_review")
|
||||||
|
w.PullRequestSync = pullHook(form.Events, string(models.HookEventPullRequestSync))
|
||||||
w.Repository = util.IsStringInSlice(string(models.HookEventRepository), form.Events, true)
|
w.Repository = util.IsStringInSlice(string(models.HookEventRepository), form.Events, true)
|
||||||
w.Release = util.IsStringInSlice(string(models.HookEventRelease), form.Events, true)
|
w.Release = util.IsStringInSlice(string(models.HookEventRelease), form.Events, true)
|
||||||
w.BranchFilter = form.BranchFilter
|
w.BranchFilter = form.BranchFilter
|
||||||
|
w.HookEvent.BranchFilter = form.BranchFilter
|
||||||
|
|
||||||
if err := w.UpdateEvent(); err != nil {
|
if err := w.UpdateEvent(); err != nil {
|
||||||
ctx.Error(http.StatusInternalServerError, "UpdateEvent", err)
|
ctx.Error(http.StatusInternalServerError, "UpdateEvent", err)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/context"
|
"code.gitea.io/gitea/modules/context"
|
||||||
"code.gitea.io/gitea/modules/convert"
|
"code.gitea.io/gitea/modules/convert"
|
||||||
|
"code.gitea.io/gitea/modules/timeutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
|
// GetQueryBeforeSince return parsed time (unix format) from URL query's before and since
|
||||||
|
@ -66,3 +67,11 @@ func GetListOptions(ctx *context.APIContext) models.ListOptions {
|
||||||
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
|
PageSize: convert.ToCorrectPageSize(ctx.QueryInt("limit")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetTimestampOptions returns list options using the start to end
|
||||||
|
func GetTimestampOptions(ctx *context.APIContext) models.TimestampOptions {
|
||||||
|
return models.TimestampOptions{
|
||||||
|
Start: timeutil.TimeStamp(ctx.QueryInt("start")),
|
||||||
|
End: timeutil.TimeStamp(ctx.QueryInt("end")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,219 @@
|
||||||
|
package viewfile
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"code.gitea.io/gitea/models"
|
||||||
|
"code.gitea.io/gitea/modules/context"
|
||||||
|
"code.gitea.io/gitea/modules/git"
|
||||||
|
"code.gitea.io/gitea/modules/repofiles"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FindFiles(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/find repository find
|
||||||
|
// ---
|
||||||
|
// summary: The search file contains subdirectories, which is a custom interface *****
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: ref
|
||||||
|
// in: query
|
||||||
|
// description: "The name of the commit/branch/tag. Default the repository’s default branch (usually master)"
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// - name: q
|
||||||
|
// in: query
|
||||||
|
// description: "Search keywords"
|
||||||
|
// type: string
|
||||||
|
// required: false
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/SearchFileItemList"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
treePath := ctx.Repo.TreePath
|
||||||
|
|
||||||
|
ref := ctx.Query("ref")
|
||||||
|
if ref == "" {
|
||||||
|
ref = ctx.Repo.Repository.DefaultBranch
|
||||||
|
}
|
||||||
|
|
||||||
|
keyWords := ctx.Query("q")
|
||||||
|
if keyWords == "" {
|
||||||
|
|
||||||
|
}
|
||||||
|
fileList, err := FindFileFromPathEtx(ctx, treePath, ref, keyWords)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("FindFileFromPathEtx", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.JSON(http.StatusOK, fileList)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
type SearchFileItem struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
SHA string `json:"sha"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
URL *string `json:"url"`
|
||||||
|
HTMLURL *string `json:"html_url"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindFileFromPathEtx(ctx *context.APIContext, treePath, ref, key string) (fileList []*SearchFileItem, err error) {
|
||||||
|
if ctx.Repo.GitRepo == nil {
|
||||||
|
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||||
|
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("OpenRepository", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if ctx.Repo.GitRepo != nil {
|
||||||
|
ctx.Repo.GitRepo.Close()
|
||||||
|
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// get the commit object for the ref
|
||||||
|
commit, err := ctx.Repo.GitRepo.GetCommit(ref)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("GetCommit", err)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tree, err2 := commit.SubTree(treePath)
|
||||||
|
if err2 != nil {
|
||||||
|
ctx.ServerError("SubTree", err)
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
entries, err3 := tree.ListEntriesRecursive()
|
||||||
|
if err3 != nil {
|
||||||
|
ctx.ServerError("ListEntries", err3)
|
||||||
|
return nil, err3
|
||||||
|
}
|
||||||
|
fileList = make([]*SearchFileItem, 0, 0)
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filename := filepath.Base(entry.Name())
|
||||||
|
if strings.Contains(strings.ToLower(filename), strings.ToLower(key)) || key == "" {
|
||||||
|
name := entry.Name()
|
||||||
|
treePath = name
|
||||||
|
|
||||||
|
selfURL, err := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", ctx.Repo.Repository.APIURL(), treePath, ref))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
selfURLString := selfURL.String()
|
||||||
|
|
||||||
|
refType := ctx.Repo.GitRepo.GetRefType(ref)
|
||||||
|
if refType == "invalid" {
|
||||||
|
return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref)
|
||||||
|
}
|
||||||
|
htmlURL, err := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", ctx.Repo.Repository.HTMLURL(), refType, ref, treePath))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
htmlURLString := htmlURL.String()
|
||||||
|
item := &SearchFileItem{
|
||||||
|
Name: filename,
|
||||||
|
Path: treePath,
|
||||||
|
SHA: entry.ID.String(),
|
||||||
|
Type: entry.Type(),
|
||||||
|
Size: entry.Size(),
|
||||||
|
URL: &selfURLString,
|
||||||
|
HTMLURL: &htmlURLString,
|
||||||
|
}
|
||||||
|
// Now populate the rest of the ContentsResponse based on entry type
|
||||||
|
if entry.IsRegular() || entry.IsExecutable() {
|
||||||
|
item.Type = string(repofiles.ContentTypeRegular)
|
||||||
|
} else if entry.IsDir() {
|
||||||
|
item.Type = string(repofiles.ContentTypeDir)
|
||||||
|
} else if entry.IsLink() {
|
||||||
|
item.Type = string(repofiles.ContentTypeLink)
|
||||||
|
} else if entry.IsSubModule() {
|
||||||
|
item.Type = string(repofiles.ContentTypeSubmodule)
|
||||||
|
}
|
||||||
|
fileList = append(fileList, item)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func LatestRelease(ctx *context.APIContext) {
|
||||||
|
// swagger:operation GET /repos/{owner}/{repo}/releases/latest repository latest
|
||||||
|
// ---
|
||||||
|
// summary: Get the last updated Release version of the repository., which is a custom interface ****
|
||||||
|
// produces:
|
||||||
|
// - application/json
|
||||||
|
// parameters:
|
||||||
|
// - name: owner
|
||||||
|
// in: path
|
||||||
|
// description: owner of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// - name: repo
|
||||||
|
// in: path
|
||||||
|
// description: name of the repo
|
||||||
|
// type: string
|
||||||
|
// required: true
|
||||||
|
// responses:
|
||||||
|
// "200":
|
||||||
|
// "$ref": "#/responses/release"
|
||||||
|
// "404":
|
||||||
|
// "$ref": "#/responses/notFound"
|
||||||
|
|
||||||
|
var err error
|
||||||
|
if ctx.Repo.GitRepo == nil {
|
||||||
|
repoPath := models.RepoPath(ctx.Repo.Owner.Name, ctx.Repo.Repository.Name)
|
||||||
|
ctx.Repo.GitRepo, err = git.OpenRepository(repoPath)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ServerError("Reporef Invalid repo"+repoPath, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer func() {
|
||||||
|
if ctx.Repo.GitRepo != nil {
|
||||||
|
ctx.Repo.GitRepo.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
release, err := models.GetLatestReleaseByRepoIDExt(ctx.Repo.Repository.ID)
|
||||||
|
fmt.Println("****************ctx.Repo.Repository.ID:", ctx.Repo.Repository.ID, " ", release, " ", err)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if models.IsErrReleaseNotExist(err) {
|
||||||
|
ctx.NotFound("LatestRelease", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.ServerError("GetLatestReleaseByRepoIDExt", err)
|
||||||
|
return
|
||||||
|
|
||||||
|
}
|
||||||
|
if err := release.LoadAttributes(); err != nil {
|
||||||
|
ctx.ServerError("LoadAttributes", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
release.Publisher.Passwd = ""
|
||||||
|
ctx.JSON(http.StatusOK, release)
|
||||||
|
|
||||||
|
}
|
|
@ -87,7 +87,7 @@ func findEntryForFile(commit *git.Commit, target string) (*git.TreeEntry, error)
|
||||||
return commit.GetTreeEntryByPath(unescapedTarget)
|
return commit.GetTreeEntryByPath(unescapedTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
func findWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
|
func FindWikiRepoCommit(ctx *context.Context) (*git.Repository, *git.Commit, error) {
|
||||||
wikiRepo, err := git.OpenRepository(ctx.Repo.Repository.WikiPath())
|
wikiRepo, err := git.OpenRepository(ctx.Repo.Repository.WikiPath())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ctx.ServerError("OpenRepository", err)
|
ctx.ServerError("OpenRepository", err)
|
||||||
|
@ -120,7 +120,7 @@ func wikiContentsByEntry(ctx *context.Context, entry *git.TreeEntry) []byte {
|
||||||
|
|
||||||
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
// wikiContentsByName returns the contents of a wiki page, along with a boolean
|
||||||
// indicating whether the page exists. Writes to ctx if an error occurs.
|
// indicating whether the page exists. Writes to ctx if an error occurs.
|
||||||
func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
func WikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName string) ([]byte, *git.TreeEntry, string, bool) {
|
||||||
pageFilename := wiki_service.NameToFilename(wikiName)
|
pageFilename := wiki_service.NameToFilename(wikiName)
|
||||||
entry, err := findEntryForFile(commit, pageFilename)
|
entry, err := findEntryForFile(commit, pageFilename)
|
||||||
if err != nil && !git.IsErrNotExist(err) {
|
if err != nil && !git.IsErrNotExist(err) {
|
||||||
|
@ -133,7 +133,7 @@ func wikiContentsByName(ctx *context.Context, commit *git.Commit, wikiName strin
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
wikiRepo, commit, err := FindWikiRepoCommit(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
@ -190,7 +190,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
ctx.Data["RequireHighlightJS"] = true
|
ctx.Data["RequireHighlightJS"] = true
|
||||||
|
|
||||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
data, entry, pageFilename, noEntry := WikiContentsByName(ctx, commit, pageName)
|
||||||
if noEntry {
|
if noEntry {
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
||||||
}
|
}
|
||||||
|
@ -201,7 +201,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
sidebarContent, _, _, _ := wikiContentsByName(ctx, commit, "_Sidebar")
|
sidebarContent, _, _, _ := WikiContentsByName(ctx, commit, "_Sidebar")
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
@ -209,7 +209,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
footerContent, _, _, _ := wikiContentsByName(ctx, commit, "_Footer")
|
footerContent, _, _, _ := WikiContentsByName(ctx, commit, "_Footer")
|
||||||
if ctx.Written() {
|
if ctx.Written() {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
@ -263,7 +263,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) {
|
||||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
wikiRepo, commit, err := FindWikiRepoCommit(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
@ -288,7 +288,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
||||||
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
ctx.Data["Reponame"] = ctx.Repo.Repository.Name
|
||||||
|
|
||||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||||
data, entry, pageFilename, noEntry := wikiContentsByName(ctx, commit, pageName)
|
data, entry, pageFilename, noEntry := WikiContentsByName(ctx, commit, pageName)
|
||||||
if noEntry {
|
if noEntry {
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
||||||
}
|
}
|
||||||
|
@ -337,7 +337,7 @@ func renderRevisionPage(ctx *context.Context) (*git.Repository, *git.TreeEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
func renderEditPage(ctx *context.Context) {
|
func renderEditPage(ctx *context.Context) {
|
||||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
wikiRepo, commit, err := FindWikiRepoCommit(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
@ -365,7 +365,7 @@ func renderEditPage(ctx *context.Context) {
|
||||||
ctx.Data["RequireHighlightJS"] = true
|
ctx.Data["RequireHighlightJS"] = true
|
||||||
|
|
||||||
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
//lookup filename in wiki - get filecontent, gitTree entry , real filename
|
||||||
data, entry, _, noEntry := wikiContentsByName(ctx, commit, pageName)
|
data, entry, _, noEntry := WikiContentsByName(ctx, commit, pageName)
|
||||||
if noEntry {
|
if noEntry {
|
||||||
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
ctx.Redirect(ctx.Repo.RepoLink + "/wiki/_pages")
|
||||||
}
|
}
|
||||||
|
@ -472,7 +472,7 @@ func WikiPages(ctx *context.Context) {
|
||||||
ctx.Data["PageIsWiki"] = true
|
ctx.Data["PageIsWiki"] = true
|
||||||
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
|
ctx.Data["CanWriteWiki"] = ctx.Repo.CanWrite(models.UnitTypeWiki) && !ctx.Repo.Repository.IsArchived
|
||||||
|
|
||||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
wikiRepo, commit, err := FindWikiRepoCommit(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
@ -521,7 +521,7 @@ func WikiPages(ctx *context.Context) {
|
||||||
|
|
||||||
// WikiRaw outputs raw blob requested by user (image for example)
|
// WikiRaw outputs raw blob requested by user (image for example)
|
||||||
func WikiRaw(ctx *context.Context) {
|
func WikiRaw(ctx *context.Context) {
|
||||||
wikiRepo, commit, err := findWikiRepoCommit(ctx)
|
wikiRepo, commit, err := FindWikiRepoCommit(ctx)
|
||||||
defer func() {
|
defer func() {
|
||||||
if wikiRepo != nil {
|
if wikiRepo != nil {
|
||||||
wikiRepo.Close()
|
wikiRepo.Close()
|
||||||
|
|
|
@ -1210,6 +1210,13 @@ func readFileName(rd *strings.Reader) (string, bool) {
|
||||||
return name[2:], ambiguity
|
return name[2:], ambiguity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDiffRange builds a Diff between two commits of a repository.
|
||||||
|
// passing the empty string as beforeCommitID returns a diff from the
|
||||||
|
// parent commit.
|
||||||
|
func GetDiffRange(gitRepo *git.Repository, beforeCommitID, afterCommitID string, maxLines, maxLineCharacters, maxFiles int) (*Diff, error) {
|
||||||
|
return GetDiffRangeWithWhitespaceBehavior(gitRepo, beforeCommitID, afterCommitID, maxLines, maxLineCharacters, maxFiles, "")
|
||||||
|
}
|
||||||
|
|
||||||
// GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository.
|
// GetDiffRangeWithWhitespaceBehavior builds a Diff between two commits of a repository.
|
||||||
// Passing the empty string as beforeCommitID returns a diff from the parent commit.
|
// Passing the empty string as beforeCommitID returns a diff from the parent commit.
|
||||||
// The whitespaceBehavior is either an empty string or a git flag
|
// The whitespaceBehavior is either an empty string or a git flag
|
||||||
|
|
|
@ -6,17 +6,17 @@
|
||||||
package wiki
|
package wiki
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"code.gitea.io/gitea/models"
|
"code.gitea.io/gitea/models"
|
||||||
"code.gitea.io/gitea/modules/git"
|
"code.gitea.io/gitea/modules/git"
|
||||||
"code.gitea.io/gitea/modules/log"
|
"code.gitea.io/gitea/modules/log"
|
||||||
repo_module "code.gitea.io/gitea/modules/repository"
|
repo_module "code.gitea.io/gitea/modules/repository"
|
||||||
"code.gitea.io/gitea/modules/sync"
|
"code.gitea.io/gitea/modules/sync"
|
||||||
"code.gitea.io/gitea/modules/util"
|
"code.gitea.io/gitea/modules/util"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -64,6 +64,16 @@ func FilenameToName(filename string) (string, error) {
|
||||||
return NormalizeWikiName(unescaped), nil
|
return NormalizeWikiName(unescaped), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check filename
|
||||||
|
func CheckFile(filename string) error {
|
||||||
|
if len(filename) <= 150 {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
err := errors.New("The name is too long, please be less than 200 bytes")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// InitWiki initializes a wiki for repository,
|
// InitWiki initializes a wiki for repository,
|
||||||
// it does nothing when repository already has wiki.
|
// it does nothing when repository already has wiki.
|
||||||
func InitWiki(repo *models.Repository) error {
|
func InitWiki(repo *models.Repository) error {
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue