2021-12-02

旧版到新版的合并
This commit is contained in:
wonderful 2021-12-02 11:14:29 +08:00
commit ac7cec05a1
49 changed files with 4160 additions and 70 deletions

4
linux.bat Normal file
View File

@ -0,0 +1,4 @@
set go111module=on
set GOOS=linux
set GOARCH=amd64
go build -tags='bindata' -o release\linux\gitea

5
mac.bat Normal file
View File

@ -0,0 +1,5 @@
set go111module=on
SET GOOS=darwin
SET GOARCH=amd64
go build -tags='bindata' -o release\mac\gitea

130
models/action_ext.go Normal file
View File

@ -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"`
}

View File

@ -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

View File

@ -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.

View File

@ -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()
}
}

View File

@ -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
}

View File

@ -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)

View File

@ -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) {

View File

@ -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,
}
}

View File

@ -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(),

View File

@ -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,

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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
} }
} }

View File

@ -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())

View File

@ -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

View File

@ -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"`
}

View File

@ -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

View File

@ -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"`

View File

@ -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 }

View File

@ -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"`
}

44
modules/structs/wiki.go Normal file
View File

@ -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"`
}

View File

@ -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 Contributions 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) its 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 ITS 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 LicenseVersion 1 (Mulan PublicLicense v1), to your software
To apply the Mulan Public LicenseVersion 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.

18
options/license/PHengLEI Normal file
View File

@ -0,0 +1,18 @@
本协议是您(如下也称"用户")与中国空气动力研究与发展中心(以下简称"中心"关于国家数值风洞NNW"风雷PHengLEI"软件的协议,请认真阅读.
1. 申请者不得私自以拷贝、刻录等方式向第三者(包括同一单位内的其他人员)传播。所有用户均需填写本协议,向中心申请并获授权后方能免费获取.
2. 用户可以在"风雷PHengLEI"软件上添加、修改源代码,有权提出软件的使用反馈及意见,用户可以发表软件的使用体验及感受,但不得随意诽谤、中伤.
3. 用户享有其开发代码的知识产权。鼓励将开发成果返回至开发团队,或在"风雷PHengLEI"软件中开源,开发者将在代码中署名.
4. 若该软件被申请人用于学术研究,则相关论文成果中应引用国家数值风洞"风雷PHengLEI"软件; 若用于其它用途,需在显要处标注基于"风雷PHengLEI"软件开发.
5. 一旦申请使用本软件,即表示同意接受协议各项条件的约束。如果您不同意协议的条件,则不能获得使用本软件的权利.
6. "风雷PHengLEI"软件由中心开发,一切知识产权,以及与软件相关的所有信息内容,包括但不限于:文字表述及其组合、图标、图饰、图表、色彩、版面框架、有关数据、印刷材料、或电子文档等均受《中华人民共和国著作权法》、《中华人民共和国计算机软件保护条例》、《中华人民共和国商标法》、《中华人民共和国专利法》、反不正当竞争法和相应的国际条约以及其他知识产权法律法规的保护,除涉及第三方授权的软件或技术外,中心享有上述知识产权.
7. 您获得的只是本软件的使用权。本软件仅限中华人民共和国公民申请使用.
8. 本协议的最终解释权归中心.

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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 repositorys 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 repositorys 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
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

View File

@ -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)
}

475
routers/api/v1/repo/wiki.go Normal file
View File

@ -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
}
}

View File

@ -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)
}

View File

@ -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"`
}

View File

@ -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

View File

@ -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"`
}

View File

@ -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

View File

@ -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)

View File

@ -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")),
}
}

View File

@ -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 repositorys 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)
}

View File

@ -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()

View File

@ -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

View File

@ -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