2014-03-15 20:03:23 +04:00
// Copyright 2014 The Gogs Authors. All rights reserved.
2017-09-12 09:48:13 +03:00
// Copyright 2017 The Gitea Authors. All rights reserved.
2014-03-15 20:03:23 +04:00
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-03-11 19:56:52 +03:00
package context
2014-03-15 20:03:23 +04:00
import (
2014-03-20 08:12:33 +04:00
"fmt"
2016-08-12 03:07:09 +03:00
"io/ioutil"
2019-01-31 00:04:19 +03:00
"net/url"
2015-10-31 19:04:04 +03:00
"path"
2014-03-17 12:47:42 +04:00
"strings"
2014-03-16 10:28:24 +04:00
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/models"
2017-10-26 04:37:33 +03:00
"code.gitea.io/gitea/modules/cache"
2019-03-27 12:33:00 +03:00
"code.gitea.io/gitea/modules/git"
2017-10-30 05:04:25 +03:00
"code.gitea.io/gitea/modules/log"
2020-09-11 17:48:39 +03:00
"code.gitea.io/gitea/modules/markup/markdown"
2016-11-10 19:24:48 +03:00
"code.gitea.io/gitea/modules/setting"
2020-09-11 17:48:39 +03:00
api "code.gitea.io/gitea/modules/structs"
2017-10-26 04:37:33 +03:00
2019-08-23 19:40:30 +03:00
"gitea.com/macaron/macaron"
2019-10-16 00:24:16 +03:00
"github.com/editorconfig/editorconfig-core-go/v2"
2019-08-23 19:40:30 +03:00
"github.com/unknwon/com"
2014-03-15 20:03:23 +04:00
)
2020-09-11 17:48:39 +03:00
// IssueTemplateDirCandidates issue templates directory
var IssueTemplateDirCandidates = [ ] string {
"ISSUE_TEMPLATE" ,
"issue_template" ,
".gitea/ISSUE_TEMPLATE" ,
".gitea/issue_template" ,
".github/ISSUE_TEMPLATE" ,
".github/issue_template" ,
".gitlab/ISSUE_TEMPLATE" ,
".gitlab/issue_template" ,
}
2016-11-25 09:51:01 +03:00
// PullRequest contains informations to make a pull request
2016-03-14 00:37:44 +03:00
type PullRequest struct {
BaseRepo * models . Repository
Allowed bool
SameRepo bool
HeadInfo string // [<user>:]<branch>
}
2017-03-15 03:52:01 +03:00
// Repository contains information to operate a repository
2016-03-14 00:37:44 +03:00
type Repository struct {
2018-11-28 14:26:14 +03:00
models . Permission
2016-03-14 00:37:44 +03:00
IsWatching bool
IsViewBranch bool
IsViewTag bool
IsViewCommit bool
Repository * models . Repository
Owner * models . User
Commit * git . Commit
Tag * git . Tag
GitRepo * git . Repository
BranchName string
TagName string
2016-08-25 07:35:03 +03:00
TreePath string
2016-03-14 00:37:44 +03:00
CommitID string
RepoLink string
CloneLink models . CloneLink
CommitsCount int64
Mirror * models . Mirror
2016-08-30 12:08:38 +03:00
PullRequest * PullRequest
2016-03-14 00:37:44 +03:00
}
2016-08-30 12:08:38 +03:00
// CanEnableEditor returns true if repository is editable and user has proper access level.
func ( r * Repository ) CanEnableEditor ( ) bool {
2019-01-23 21:58:38 +03:00
return r . Permission . CanWrite ( models . UnitTypeCode ) && r . Repository . CanEnableEditor ( ) && r . IsViewBranch && ! r . Repository . IsArchived
2016-08-30 12:08:38 +03:00
}
2017-10-15 22:59:24 +03:00
// CanCreateBranch returns true if repository is editable and user has proper access level.
func ( r * Repository ) CanCreateBranch ( ) bool {
2018-11-28 14:26:14 +03:00
return r . Permission . CanWrite ( models . UnitTypeCode ) && r . Repository . CanCreateBranch ( )
2017-10-15 22:59:24 +03:00
}
2019-01-23 21:58:38 +03:00
// RepoMustNotBeArchived checks if a repo is archived
func RepoMustNotBeArchived ( ) macaron . Handler {
return func ( ctx * Context ) {
if ctx . Repo . Repository . IsArchived {
ctx . NotFound ( "IsArchived" , fmt . Errorf ( ctx . Tr ( "repo.archive.title" ) ) )
}
}
}
2020-01-15 11:32:57 +03:00
// CanCommitToBranchResults represents the results of CanCommitToBranch
type CanCommitToBranchResults struct {
CanCommitToBranch bool
EditorEnabled bool
UserCanPush bool
RequireSigned bool
WillSign bool
SigningKey string
WontSignReason string
}
2017-05-02 03:49:55 +03:00
// CanCommitToBranch returns true if repository is editable and user has proper access level
2018-08-08 06:17:11 +03:00
// and branch is not protected for push
2020-01-15 11:32:57 +03:00
func ( r * Repository ) CanCommitToBranch ( doer * models . User ) ( CanCommitToBranchResults , error ) {
protectedBranch , err := models . GetProtectedBranchBy ( r . Repository . ID , r . BranchName )
2017-05-02 03:49:55 +03:00
if err != nil {
2020-01-15 11:32:57 +03:00
return CanCommitToBranchResults { } , err
2017-05-02 03:49:55 +03:00
}
2020-01-15 11:32:57 +03:00
userCanPush := true
requireSigned := false
if protectedBranch != nil {
userCanPush = protectedBranch . CanUserPush ( doer . ID )
requireSigned = protectedBranch . RequireSignedCommits
}
2020-09-19 19:44:55 +03:00
sign , keyID , _ , err := r . Repository . SignCRUDAction ( doer , r . Repository . RepoPath ( ) , git . BranchPrefix + r . BranchName )
2020-01-15 11:32:57 +03:00
canCommit := r . CanEnableEditor ( ) && userCanPush
if requireSigned {
canCommit = canCommit && sign
}
wontSignReason := ""
if err != nil {
if models . IsErrWontSign ( err ) {
wontSignReason = string ( err . ( * models . ErrWontSign ) . Reason )
err = nil
} else {
wontSignReason = "error"
}
}
return CanCommitToBranchResults {
CanCommitToBranch : canCommit ,
EditorEnabled : r . CanEnableEditor ( ) ,
UserCanPush : userCanPush ,
RequireSigned : requireSigned ,
WillSign : sign ,
SigningKey : keyID ,
WontSignReason : wontSignReason ,
} , err
2017-05-02 03:49:55 +03:00
}
2017-09-12 09:48:13 +03:00
// CanUseTimetracker returns whether or not a user can use the timetracker.
func ( r * Repository ) CanUseTimetracker ( issue * models . Issue , user * models . User ) bool {
// Checking for following:
// 1. Is timetracker enabled
// 2. Is the user a contributor, admin, poster or assignee and do the repository policies require this?
2018-05-09 19:29:04 +03:00
isAssigned , _ := models . IsUserAssignedToIssue ( issue , user )
2017-09-12 09:48:13 +03:00
return r . Repository . IsTimetrackerEnabled ( ) && ( ! r . Repository . AllowOnlyContributorsToTrackTime ( ) ||
2020-01-20 15:00:32 +03:00
r . Permission . CanWriteIssuesOrPulls ( issue . IsPull ) || issue . IsPoster ( user . ID ) || isAssigned )
2017-09-12 09:48:13 +03:00
}
2018-07-18 00:23:58 +03:00
// CanCreateIssueDependencies returns whether or not a user can create dependencies.
2020-01-19 09:43:38 +03:00
func ( r * Repository ) CanCreateIssueDependencies ( user * models . User , isPull bool ) bool {
return r . Repository . IsDependenciesEnabled ( ) && r . Permission . CanWriteIssuesOrPulls ( isPull )
2018-07-18 00:23:58 +03:00
}
2017-10-26 04:37:33 +03:00
// GetCommitsCount returns cached commit count for current view
func ( r * Repository ) GetCommitsCount ( ) ( int64 , error ) {
var contextName string
if r . IsViewBranch {
contextName = r . BranchName
} else if r . IsViewTag {
contextName = r . TagName
} else {
contextName = r . CommitID
}
return cache . GetInt64 ( r . Repository . GetCommitsCountCacheKey ( contextName , r . IsViewBranch || r . IsViewTag ) , func ( ) ( int64 , error ) {
return r . Commit . CommitsCount ( )
} )
}
2017-10-30 05:04:25 +03:00
// BranchNameSubURL sub-URL for the BranchName field
func ( r * Repository ) BranchNameSubURL ( ) string {
switch {
case r . IsViewBranch :
return "branch/" + r . BranchName
case r . IsViewTag :
return "tag/" + r . BranchName
case r . IsViewCommit :
return "commit/" + r . BranchName
}
2019-04-02 10:48:31 +03:00
log . Error ( "Unknown view type for repo: %v" , r )
2017-10-30 05:04:25 +03:00
return ""
}
2019-04-17 19:06:35 +03:00
// FileExists returns true if a file exists in the given repo branch
func ( r * Repository ) FileExists ( path string , branch string ) ( bool , error ) {
if branch == "" {
branch = r . Repository . DefaultBranch
}
commit , err := r . GitRepo . GetBranchCommit ( branch )
if err != nil {
return false , err
}
if _ , err := commit . GetTreeEntryByPath ( path ) ; err != nil {
return false , err
}
return true , nil
}
2016-08-12 03:07:09 +03:00
// GetEditorconfig returns the .editorconfig definition if found in the
// HEAD of the default repo branch.
func ( r * Repository ) GetEditorconfig ( ) ( * editorconfig . Editorconfig , error ) {
2019-10-13 16:23:14 +03:00
if r . GitRepo == nil {
return nil , nil
}
2016-08-12 03:07:09 +03:00
commit , err := r . GitRepo . GetBranchCommit ( r . Repository . DefaultBranch )
if err != nil {
return nil , err
}
treeEntry , err := commit . GetTreeEntryByPath ( ".editorconfig" )
if err != nil {
return nil , err
}
2017-11-29 04:50:39 +03:00
if treeEntry . Blob ( ) . Size ( ) >= setting . UI . MaxDisplayFileSize {
return nil , git . ErrNotExist { ID : "" , RelPath : ".editorconfig" }
}
2019-04-19 15:17:27 +03:00
reader , err := treeEntry . Blob ( ) . DataAsync ( )
2016-08-12 03:07:09 +03:00
if err != nil {
return nil , err
}
2019-04-19 15:17:27 +03:00
defer reader . Close ( )
2020-10-16 08:06:27 +03:00
return editorconfig . Parse ( reader )
2016-08-12 03:07:09 +03:00
}
2016-11-25 09:51:01 +03:00
// RetrieveBaseRepo retrieves base repository
2015-09-01 18:43:53 +03:00
func RetrieveBaseRepo ( ctx * Context , repo * models . Repository ) {
// Non-fork repository will not return error in this method.
if err := repo . GetBaseRepo ( ) ; err != nil {
if models . IsErrRepoNotExist ( err ) {
repo . IsFork = false
repo . ForkID = 0
return
}
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBaseRepo" , err )
2015-09-01 18:43:53 +03:00
return
} else if err = repo . BaseRepo . GetOwner ( ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "BaseRepo.GetOwner" , err )
2015-09-01 18:43:53 +03:00
return
}
}
2019-11-11 18:15:29 +03:00
// RetrieveTemplateRepo retrieves template repository used to generate this repository
func RetrieveTemplateRepo ( ctx * Context , repo * models . Repository ) {
// Non-generated repository will not return error in this method.
if err := repo . GetTemplateRepo ( ) ; err != nil {
if models . IsErrRepoNotExist ( err ) {
repo . TemplateID = 0
return
}
ctx . ServerError ( "GetTemplateRepo" , err )
return
} else if err = repo . TemplateRepo . GetOwner ( ) ; err != nil {
ctx . ServerError ( "TemplateRepo.GetOwner" , err )
return
}
2019-11-20 22:44:35 +03:00
perm , err := models . GetUserRepoPermission ( repo . TemplateRepo , ctx . User )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
}
if ! perm . CanRead ( models . UnitTypeCode ) {
2019-11-11 18:15:29 +03:00
repo . TemplateID = 0
}
}
2017-09-23 16:24:24 +03:00
// ComposeGoGetImport returns go-get-import meta content.
func ComposeGoGetImport ( owner , repo string ) string {
2019-05-28 00:08:38 +03:00
/// setting.AppUrl is guaranteed to be parse as url
appURL , _ := url . Parse ( setting . AppURL )
return path . Join ( appURL . Host , setting . AppSubURL , url . PathEscape ( owner ) , url . PathEscape ( repo ) )
2016-08-08 00:29:16 +03:00
}
2017-09-23 16:24:24 +03:00
// EarlyResponseForGoGetMeta responses appropriate go-get meta with status 200
2016-08-08 00:29:16 +03:00
// if user does not have actual access to the requested repository,
// or the owner or repository does not exist at all.
// This is particular a workaround for "go get" command which does not respect
// .netrc file.
2017-09-23 16:24:24 +03:00
func EarlyResponseForGoGetMeta ( ctx * Context ) {
username := ctx . Params ( ":username" )
2019-09-06 16:44:59 +03:00
reponame := strings . TrimSuffix ( ctx . Params ( ":reponame" ) , ".git" )
if username == "" || reponame == "" {
ctx . PlainText ( 400 , [ ] byte ( "invalid repository path" ) )
return
}
2016-08-08 00:29:16 +03:00
ctx . PlainText ( 200 , [ ] byte ( com . Expand ( ` <meta name="go-import" content=" { GoGetImport} git { CloneLink}"> ` ,
map [ string ] string {
2019-09-06 16:44:59 +03:00
"GoGetImport" : ComposeGoGetImport ( username , reponame ) ,
2017-09-23 16:24:24 +03:00
"CloneLink" : models . ComposeHTTPSCloneURL ( username , reponame ) ,
2016-08-08 00:29:16 +03:00
} ) ) )
}
2017-02-05 17:35:03 +03:00
// RedirectToRepo redirect to a differently-named repository
func RedirectToRepo ( ctx * Context , redirectRepoID int64 ) {
ownerName := ctx . Params ( ":username" )
previousRepoName := ctx . Params ( ":reponame" )
repo , err := models . GetRepositoryByID ( redirectRepoID )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2017-02-05 17:35:03 +03:00
return
}
redirectPath := strings . Replace (
ctx . Req . URL . Path ,
fmt . Sprintf ( "%s/%s" , ownerName , previousRepoName ) ,
2020-01-12 12:36:21 +03:00
repo . FullName ( ) ,
2017-02-05 17:35:03 +03:00
1 ,
)
2019-04-25 08:51:40 +03:00
if ctx . Req . URL . RawQuery != "" {
redirectPath += "?" + ctx . Req . URL . RawQuery
}
2019-10-22 21:50:24 +03:00
ctx . Redirect ( path . Join ( setting . AppSubURL , redirectPath ) )
2017-02-05 17:35:03 +03:00
}
2017-09-19 14:44:49 +03:00
2017-10-05 10:32:25 +03:00
func repoAssignment ( ctx * Context , repo * models . Repository ) {
2018-11-28 14:26:14 +03:00
var err error
2019-03-05 23:15:24 +03:00
if err = repo . GetOwner ( ) ; err != nil {
ctx . ServerError ( "GetOwner" , err )
return
}
2018-11-28 14:26:14 +03:00
ctx . Repo . Permission , err = models . GetUserRepoPermission ( repo , ctx . User )
if err != nil {
ctx . ServerError ( "GetUserRepoPermission" , err )
return
2017-10-05 10:32:25 +03:00
}
// Check access.
2018-11-28 14:26:14 +03:00
if ctx . Repo . Permission . AccessMode == models . AccessModeNone {
2017-10-05 10:32:25 +03:00
if ctx . Query ( "go-get" ) == "1" {
EarlyResponseForGoGetMeta ( ctx )
return
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "no access right" , nil )
2017-10-05 10:32:25 +03:00
return
}
ctx . Data [ "HasAccess" ] = true
2018-11-28 14:26:14 +03:00
ctx . Data [ "Permission" ] = & ctx . Repo . Permission
2017-10-05 10:32:25 +03:00
if repo . IsMirror {
var err error
ctx . Repo . Mirror , err = models . GetMirrorByRepoID ( repo . ID )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetMirror" , err )
2017-10-05 10:32:25 +03:00
return
}
ctx . Data [ "MirrorEnablePrune" ] = ctx . Repo . Mirror . EnablePrune
ctx . Data [ "MirrorInterval" ] = ctx . Repo . Mirror . Interval
ctx . Data [ "Mirror" ] = ctx . Repo . Mirror
}
ctx . Repo . Repository = repo
ctx . Data [ "RepoName" ] = ctx . Repo . Repository . Name
2019-01-18 03:01:04 +03:00
ctx . Data [ "IsEmptyRepo" ] = ctx . Repo . Repository . IsEmpty
2017-10-05 10:32:25 +03:00
}
// RepoIDAssignment returns a macaron handler which assigns the repo to the context.
2017-09-18 17:52:20 +03:00
func RepoIDAssignment ( ) macaron . Handler {
return func ( ctx * Context ) {
repoID := ctx . ParamsInt64 ( ":repoid" )
// Get repository.
repo , err := models . GetRepositoryByID ( repoID )
if err != nil {
if models . IsErrRepoNotExist ( err ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetRepositoryByID" , nil )
2017-09-18 17:52:20 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByID" , err )
2017-09-18 17:52:20 +03:00
}
return
}
2017-10-05 10:32:25 +03:00
repoAssignment ( ctx , repo )
2017-09-18 17:52:20 +03:00
}
}
2017-02-05 17:35:03 +03:00
2016-11-25 09:51:01 +03:00
// RepoAssignment returns a macaron to handle repository assignment
2017-03-18 13:59:07 +03:00
func RepoAssignment ( ) macaron . Handler {
2014-07-26 08:24:27 +04:00
return func ( ctx * Context ) {
2014-03-15 20:03:23 +04:00
var (
2015-10-31 19:04:04 +03:00
owner * models . User
err error
2014-03-15 20:03:23 +04:00
)
2014-07-26 08:24:27 +04:00
userName := ctx . Params ( ":username" )
repoName := ctx . Params ( ":reponame" )
2014-03-30 06:09:59 +04:00
2015-02-05 16:29:08 +03:00
// Check if the user is the same as the repository owner
2015-02-13 10:14:57 +03:00
if ctx . IsSigned && ctx . User . LowerName == strings . ToLower ( userName ) {
2015-10-31 19:04:04 +03:00
owner = ctx . User
2015-02-05 16:29:08 +03:00
} else {
2015-10-31 19:04:04 +03:00
owner , err = models . GetUserByName ( userName )
2014-03-15 20:03:23 +04:00
if err != nil {
2015-08-05 06:14:17 +03:00
if models . IsErrUserNotExist ( err ) {
2016-08-08 00:29:16 +03:00
if ctx . Query ( "go-get" ) == "1" {
2017-09-23 16:24:24 +03:00
EarlyResponseForGoGetMeta ( ctx )
2016-08-08 00:29:16 +03:00
return
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetUserByName" , nil )
2014-08-14 10:12:21 +04:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetUserByName" , err )
2014-03-15 20:03:23 +04:00
}
return
}
}
2015-10-31 19:04:04 +03:00
ctx . Repo . Owner = owner
2016-08-30 12:08:38 +03:00
ctx . Data [ "Username" ] = ctx . Repo . Owner . Name
2014-03-15 20:03:23 +04:00
2014-08-24 17:09:05 +04:00
// Get repository.
2016-07-23 20:08:22 +03:00
repo , err := models . GetRepositoryByName ( owner . ID , repoName )
2014-03-15 20:03:23 +04:00
if err != nil {
2015-03-16 11:04:27 +03:00
if models . IsErrRepoNotExist ( err ) {
2017-02-05 17:35:03 +03:00
redirectRepoID , err := models . LookupRepoRedirect ( owner . ID , repoName )
if err == nil {
RedirectToRepo ( ctx , redirectRepoID )
} else if models . IsErrRepoRedirectNotExist ( err ) {
if ctx . Query ( "go-get" ) == "1" {
2017-09-23 16:24:24 +03:00
EarlyResponseForGoGetMeta ( ctx )
2017-02-05 17:35:03 +03:00
return
}
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetRepositoryByName" , nil )
2017-02-05 17:35:03 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "LookupRepoRedirect" , err )
2016-08-08 00:29:16 +03:00
}
2015-02-05 16:29:08 +03:00
} else {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetRepositoryByName" , err )
2014-03-15 20:03:23 +04:00
}
2014-07-26 08:24:27 +04:00
return
2014-03-30 06:09:59 +04:00
}
2017-02-02 15:33:56 +03:00
repo . Owner = owner
2014-04-12 05:47:39 +04:00
2017-10-05 10:32:25 +03:00
repoAssignment ( ctx , repo )
if ctx . Written ( ) {
2015-02-05 16:29:08 +03:00
return
2014-04-12 05:47:39 +04:00
}
2014-03-30 09:30:17 +04:00
2016-07-15 16:53:43 +03:00
ctx . Repo . RepoLink = repo . Link ( )
2014-08-15 14:29:41 +04:00
ctx . Data [ "RepoLink" ] = ctx . Repo . RepoLink
2015-09-02 12:09:12 +03:00
ctx . Data [ "RepoRelPath" ] = ctx . Repo . Owner . Name + "/" + ctx . Repo . Repository . Name
2014-03-30 07:38:41 +04:00
2019-02-20 02:09:47 +03:00
unit , err := ctx . Repo . Repository . GetUnit ( models . UnitTypeExternalTracker )
if err == nil {
ctx . Data [ "RepoExternalIssuesLink" ] = unit . ExternalTrackerConfig ( ) . ExternalTrackerURL
}
2020-11-05 17:20:42 +03:00
ctx . Data [ "NumTags" ] , err = models . GetReleaseCountByRepoID ( ctx . Repo . Repository . ID , models . FindReleasesOptions {
IncludeTags : true ,
2017-08-28 17:06:10 +03:00
} )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetReleaseCountByRepoID" , err )
2017-08-28 17:06:10 +03:00
return
}
2020-11-05 17:20:42 +03:00
ctx . Data [ "NumReleases" ] , err = models . GetReleaseCountByRepoID ( ctx . Repo . Repository . ID , models . FindReleasesOptions { } )
if err != nil {
ctx . ServerError ( "GetReleaseCountByRepoID" , err )
return
}
2014-04-14 05:00:12 +04:00
2016-03-04 23:43:01 +03:00
ctx . Data [ "Title" ] = owner . Name + "/" + repo . Name
ctx . Data [ "Repository" ] = repo
ctx . Data [ "Owner" ] = ctx . Repo . Repository . Owner
ctx . Data [ "IsRepositoryOwner" ] = ctx . Repo . IsOwner ( )
ctx . Data [ "IsRepositoryAdmin" ] = ctx . Repo . IsAdmin ( )
2019-09-24 04:30:40 +03:00
ctx . Data [ "RepoOwnerIsOrganization" ] = repo . Owner . IsOrganization ( )
2018-11-28 14:26:14 +03:00
ctx . Data [ "CanWriteCode" ] = ctx . Repo . CanWrite ( models . UnitTypeCode )
ctx . Data [ "CanWriteIssues" ] = ctx . Repo . CanWrite ( models . UnitTypeIssues )
ctx . Data [ "CanWritePulls" ] = ctx . Repo . CanWrite ( models . UnitTypePullRequests )
2016-03-04 23:43:01 +03:00
2017-10-15 18:06:07 +03:00
if ctx . Data [ "CanSignedUserFork" ] , err = ctx . Repo . Repository . CanUserFork ( ctx . User ) ; err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "CanUserFork" , err )
2017-10-15 18:06:07 +03:00
return
}
2016-02-28 04:48:39 +03:00
ctx . Data [ "DisableSSH" ] = setting . SSH . Disabled
2017-07-15 17:21:51 +03:00
ctx . Data [ "ExposeAnonSSH" ] = setting . SSH . ExposeAnonymous
2016-10-04 19:58:14 +03:00
ctx . Data [ "DisableHTTP" ] = setting . Repository . DisableHTTPGit
2017-10-27 09:10:54 +03:00
ctx . Data [ "RepoSearchEnabled" ] = setting . Indexer . RepoIndexerEnabled
2015-12-01 04:45:55 +03:00
ctx . Data [ "CloneLink" ] = repo . CloneLink ( )
ctx . Data [ "WikiCloneLink" ] = repo . WikiCloneLink ( )
2014-03-30 07:38:41 +04:00
2015-10-03 02:58:36 +03:00
if ctx . IsSigned {
2016-07-23 20:08:22 +03:00
ctx . Data [ "IsWatchingRepo" ] = models . IsWatching ( ctx . User . ID , repo . ID )
ctx . Data [ "IsStaringRepo" ] = models . IsStaring ( ctx . User . ID , repo . ID )
2015-10-03 02:58:36 +03:00
}
2019-04-15 23:48:35 +03:00
if repo . IsFork {
RetrieveBaseRepo ( ctx , repo )
if ctx . Written ( ) {
return
}
}
2019-11-11 18:15:29 +03:00
if repo . IsGenerated ( ) {
RetrieveTemplateRepo ( ctx , repo )
if ctx . Written ( ) {
return
}
}
2019-10-16 16:08:01 +03:00
// Disable everything when the repo is being created
if ctx . Repo . Repository . IsBeingCreated ( ) {
2017-03-18 13:59:07 +03:00
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
2014-03-30 09:30:17 +04:00
return
2014-03-30 06:09:59 +04:00
}
2014-03-15 20:03:23 +04:00
2019-10-13 16:23:14 +03:00
gitRepo , err := git . OpenRepository ( models . RepoPath ( userName , repoName ) )
if err != nil {
ctx . ServerError ( "RepoAssignment Invalid repo " + models . RepoPath ( userName , repoName ) , err )
return
}
ctx . Repo . GitRepo = gitRepo
2019-11-13 10:01:19 +03:00
// We opened it, we should close it
defer func ( ) {
// If it's been set to nil then assume someone else has closed it.
if ctx . Repo . GitRepo != nil {
ctx . Repo . GitRepo . Close ( )
}
} ( )
2019-10-16 16:08:01 +03:00
// Stop at this point when the repo is empty.
if ctx . Repo . Repository . IsEmpty {
ctx . Data [ "BranchName" ] = ctx . Repo . Repository . DefaultBranch
2019-11-13 10:01:19 +03:00
ctx . Next ( )
2019-10-16 16:08:01 +03:00
return
}
2019-10-13 16:23:14 +03:00
tags , err := ctx . Repo . GitRepo . GetTags ( )
if err != nil {
ctx . ServerError ( "GetTags" , err )
return
}
ctx . Data [ "Tags" ] = tags
2014-04-13 05:35:36 +04:00
brs , err := ctx . Repo . GitRepo . GetBranches ( )
2014-04-11 08:01:38 +04:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranches" , err )
2014-09-23 21:47:54 +04:00
return
2014-04-11 08:01:38 +04:00
}
ctx . Data [ "Branches" ] = brs
2017-12-03 23:52:30 +03:00
ctx . Data [ "BranchesCount" ] = len ( brs )
2014-07-22 16:46:04 +04:00
2019-10-13 16:23:14 +03:00
ctx . Data [ "TagName" ] = ctx . Repo . TagName
2014-07-22 16:46:04 +04:00
// If not branch selected, try default one.
// If default branch doesn't exists, fall back to some other branch.
2015-08-08 17:43:14 +03:00
if len ( ctx . Repo . BranchName ) == 0 {
if len ( ctx . Repo . Repository . DefaultBranch ) > 0 && gitRepo . IsBranchExist ( ctx . Repo . Repository . DefaultBranch ) {
2014-07-22 16:46:04 +04:00
ctx . Repo . BranchName = ctx . Repo . Repository . DefaultBranch
} else if len ( brs ) > 0 {
ctx . Repo . BranchName = brs [ 0 ]
}
}
ctx . Data [ "BranchName" ] = ctx . Repo . BranchName
2015-08-31 10:24:28 +03:00
ctx . Data [ "CommitID" ] = ctx . Repo . CommitID
2015-08-03 22:27:07 +03:00
2017-03-15 03:52:01 +03:00
// People who have push access or have forked repository can propose a new pull request.
2020-05-05 01:44:30 +03:00
canPush := ctx . Repo . CanWrite ( models . UnitTypeCode ) || ( ctx . IsSigned && ctx . User . HasForkedRepo ( ctx . Repo . Repository . ID ) )
canCompare := false
// Pull request is allowed if this is a fork repository
// and base repository accepts pull requests.
if repo . BaseRepo != nil && repo . BaseRepo . AllowsPulls ( ) {
canCompare = true
ctx . Data [ "BaseRepo" ] = repo . BaseRepo
ctx . Repo . PullRequest . BaseRepo = repo . BaseRepo
ctx . Repo . PullRequest . Allowed = canPush
ctx . Repo . PullRequest . HeadInfo = ctx . Repo . Owner . Name + ":" + ctx . Repo . BranchName
} else if repo . AllowsPulls ( ) {
// Or, this is repository accepts pull requests between branches.
canCompare = true
ctx . Data [ "BaseRepo" ] = repo
ctx . Repo . PullRequest . BaseRepo = repo
ctx . Repo . PullRequest . Allowed = canPush
ctx . Repo . PullRequest . SameRepo = true
ctx . Repo . PullRequest . HeadInfo = ctx . Repo . BranchName
2016-03-07 07:57:46 +03:00
}
2020-05-05 01:44:30 +03:00
ctx . Data [ "CanCompareOrPull" ] = canCompare
2016-03-07 07:57:46 +03:00
ctx . Data [ "PullRequestCtx" ] = ctx . Repo . PullRequest
2015-10-31 19:04:04 +03:00
if ctx . Query ( "go-get" ) == "1" {
2017-09-23 16:24:24 +03:00
ctx . Data [ "GoGetImport" ] = ComposeGoGetImport ( owner . Name , repo . Name )
2017-11-27 03:58:54 +03:00
prefix := setting . AppURL + path . Join ( owner . Name , repo . Name , "src" , "branch" , ctx . Repo . BranchName )
2015-10-31 19:04:04 +03:00
ctx . Data [ "GoDocDirectory" ] = prefix + "{/dir}"
ctx . Data [ "GoDocFile" ] = prefix + "{/dir}/{file}#L{line}"
}
2019-11-13 10:01:19 +03:00
ctx . Next ( )
2014-03-15 20:03:23 +04:00
}
}
2014-05-06 03:58:13 +04:00
2017-10-30 05:04:25 +03:00
// RepoRefType type of repo reference
type RepoRefType int
const (
// RepoRefLegacy unknown type, make educated guess and redirect.
// for backward compatibility with previous URL scheme
RepoRefLegacy RepoRefType = iota
2017-11-04 02:23:59 +03:00
// RepoRefAny is for usage where educated guess is needed
// but redirect can not be made
RepoRefAny
2017-10-30 05:04:25 +03:00
// RepoRefBranch branch
RepoRefBranch
// RepoRefTag tag
RepoRefTag
// RepoRefCommit commit
RepoRefCommit
2018-11-18 21:45:40 +03:00
// RepoRefBlob blob
RepoRefBlob
2017-10-30 05:04:25 +03:00
)
// RepoRef handles repository reference names when the ref name is not
// explicitly given
2015-12-05 01:20:23 +03:00
func RepoRef ( ) macaron . Handler {
2017-10-30 05:04:25 +03:00
// since no ref name is explicitly specified, ok to just use branch
return RepoRefByType ( RepoRefBranch )
}
2019-11-11 10:37:28 +03:00
// RefTypeIncludesBranches returns true if ref type can be a branch
func ( rt RepoRefType ) RefTypeIncludesBranches ( ) bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefBranch {
return true
}
return false
}
// RefTypeIncludesTags returns true if ref type can be a tag
func ( rt RepoRefType ) RefTypeIncludesTags ( ) bool {
if rt == RepoRefLegacy || rt == RepoRefAny || rt == RepoRefTag {
return true
}
return false
}
2017-10-30 05:04:25 +03:00
func getRefNameFromPath ( ctx * Context , path string , isExist func ( string ) bool ) string {
refName := ""
parts := strings . Split ( path , "/" )
for i , part := range parts {
refName = strings . TrimPrefix ( refName + "/" + part , "/" )
if isExist ( refName ) {
ctx . Repo . TreePath = strings . Join ( parts [ i + 1 : ] , "/" )
return refName
}
}
return ""
}
func getRefName ( ctx * Context , pathType RepoRefType ) string {
path := ctx . Params ( "*" )
switch pathType {
2017-11-04 02:23:59 +03:00
case RepoRefLegacy , RepoRefAny :
2017-10-30 05:04:25 +03:00
if refName := getRefName ( ctx , RepoRefBranch ) ; len ( refName ) > 0 {
return refName
}
if refName := getRefName ( ctx , RepoRefTag ) ; len ( refName ) > 0 {
return refName
}
2017-11-04 20:26:38 +03:00
if refName := getRefName ( ctx , RepoRefCommit ) ; len ( refName ) > 0 {
return refName
}
2018-11-18 21:45:40 +03:00
if refName := getRefName ( ctx , RepoRefBlob ) ; len ( refName ) > 0 {
return refName
}
2017-11-04 20:26:38 +03:00
ctx . Repo . TreePath = path
return ctx . Repo . Repository . DefaultBranch
2017-10-30 05:04:25 +03:00
case RepoRefBranch :
return getRefNameFromPath ( ctx , path , ctx . Repo . GitRepo . IsBranchExist )
case RepoRefTag :
return getRefNameFromPath ( ctx , path , ctx . Repo . GitRepo . IsTagExist )
case RepoRefCommit :
parts := strings . Split ( path , "/" )
if len ( parts ) > 0 && len ( parts [ 0 ] ) == 40 {
ctx . Repo . TreePath = strings . Join ( parts [ 1 : ] , "/" )
return parts [ 0 ]
}
2018-11-18 21:45:40 +03:00
case RepoRefBlob :
_ , err := ctx . Repo . GitRepo . GetBlob ( path )
if err != nil {
return ""
}
return path
2017-10-30 05:04:25 +03:00
default :
2019-04-02 10:48:31 +03:00
log . Error ( "Unrecognized path type: %v" , path )
2017-10-30 05:04:25 +03:00
}
return ""
}
// RepoRefByType handles repository reference name for a specific type
// of repository reference
func RepoRefByType ( refType RepoRefType ) macaron . Handler {
2015-12-05 01:20:23 +03:00
return func ( ctx * Context ) {
// Empty repository does not have reference information.
2019-01-18 03:01:04 +03:00
if ctx . Repo . Repository . IsEmpty {
2015-12-05 01:20:23 +03:00
return
}
var (
refName string
err error
)
// For API calls.
if ctx . Repo . GitRepo == nil {
repoPath := models . RepoPath ( ctx . Repo . Owner . Name , ctx . Repo . Repository . Name )
2016-08-30 12:08:38 +03:00
ctx . Repo . GitRepo , err = git . OpenRepository ( repoPath )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "RepoRef Invalid repo " + repoPath , err )
2015-12-05 01:20:23 +03:00
return
}
2019-11-13 10:01:19 +03:00
// We opened it, we should close it
defer func ( ) {
// If it's been set to nil then assume someone else has closed it.
if ctx . Repo . GitRepo != nil {
ctx . Repo . GitRepo . Close ( )
}
} ( )
2015-12-05 01:20:23 +03:00
}
// Get default branch.
if len ( ctx . Params ( "*" ) ) == 0 {
refName = ctx . Repo . Repository . DefaultBranch
2017-10-30 05:04:25 +03:00
ctx . Repo . BranchName = refName
2015-12-05 01:20:23 +03:00
if ! ctx . Repo . GitRepo . IsBranchExist ( refName ) {
brs , err := ctx . Repo . GitRepo . GetBranches ( )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranches" , err )
2015-12-05 01:20:23 +03:00
return
2017-05-29 05:25:23 +03:00
} else if len ( brs ) == 0 {
2019-01-18 03:01:04 +03:00
err = fmt . Errorf ( "No branches in non-empty repository %s" ,
2017-05-29 05:25:23 +03:00
ctx . Repo . GitRepo . Path )
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranches" , err )
2017-07-04 04:29:57 +03:00
return
2015-12-05 01:20:23 +03:00
}
refName = brs [ 0 ]
}
2015-12-10 04:46:05 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( refName )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranchCommit" , err )
2015-12-05 01:20:23 +03:00
return
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
2015-12-09 08:32:53 +03:00
ctx . Repo . IsViewBranch = true
2015-12-05 01:20:23 +03:00
} else {
2017-10-30 05:04:25 +03:00
refName = getRefName ( ctx , refType )
ctx . Repo . BranchName = refName
2019-11-11 10:37:28 +03:00
if refType . RefTypeIncludesBranches ( ) && ctx . Repo . GitRepo . IsBranchExist ( refName ) {
2015-12-09 08:32:53 +03:00
ctx . Repo . IsViewBranch = true
2015-12-05 01:20:23 +03:00
2015-12-10 04:46:05 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( refName )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetBranchCommit" , err )
2015-12-05 01:20:23 +03:00
return
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
2019-11-11 10:37:28 +03:00
} else if refType . RefTypeIncludesTags ( ) && ctx . Repo . GitRepo . IsTagExist ( refName ) {
2015-12-09 08:32:53 +03:00
ctx . Repo . IsViewTag = true
2015-12-10 04:46:05 +03:00
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetTagCommit ( refName )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetTagCommit" , err )
2015-12-05 01:20:23 +03:00
return
}
ctx . Repo . CommitID = ctx . Repo . Commit . ID . String ( )
} else if len ( refName ) == 40 {
2015-12-09 08:32:53 +03:00
ctx . Repo . IsViewCommit = true
2015-12-05 01:20:23 +03:00
ctx . Repo . CommitID = refName
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetCommit ( refName )
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GetCommit" , nil )
2015-12-05 01:20:23 +03:00
return
}
} else {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "RepoRef invalid repo" , fmt . Errorf ( "branch or tag not exist: %s" , refName ) )
2015-12-05 01:20:23 +03:00
return
}
2017-10-30 05:04:25 +03:00
if refType == RepoRefLegacy {
// redirect from old URL scheme to new URL scheme
2017-12-07 04:09:02 +03:00
ctx . Redirect ( path . Join (
setting . AppSubURL ,
2018-09-07 04:37:02 +03:00
strings . TrimSuffix ( ctx . Req . URL . Path , ctx . Params ( "*" ) ) ,
2017-12-07 04:09:02 +03:00
ctx . Repo . BranchNameSubURL ( ) ,
ctx . Repo . TreePath ) )
2017-10-30 05:04:25 +03:00
return
}
2015-12-05 01:20:23 +03:00
}
ctx . Data [ "BranchName" ] = ctx . Repo . BranchName
2017-10-30 05:04:25 +03:00
ctx . Data [ "BranchNameSubURL" ] = ctx . Repo . BranchNameSubURL ( )
2015-12-05 01:20:23 +03:00
ctx . Data [ "CommitID" ] = ctx . Repo . CommitID
2016-08-30 12:08:38 +03:00
ctx . Data [ "TreePath" ] = ctx . Repo . TreePath
2015-12-09 08:32:53 +03:00
ctx . Data [ "IsViewBranch" ] = ctx . Repo . IsViewBranch
ctx . Data [ "IsViewTag" ] = ctx . Repo . IsViewTag
ctx . Data [ "IsViewCommit" ] = ctx . Repo . IsViewCommit
2017-10-15 22:59:24 +03:00
ctx . Data [ "CanCreateBranch" ] = ctx . Repo . CanCreateBranch ( )
2015-12-05 01:20:23 +03:00
2017-10-26 04:37:33 +03:00
ctx . Repo . CommitsCount , err = ctx . Repo . GetCommitsCount ( )
2015-12-05 01:20:23 +03:00
if err != nil {
2018-01-11 00:34:17 +03:00
ctx . ServerError ( "GetCommitsCount" , err )
2015-12-05 01:20:23 +03:00
return
}
ctx . Data [ "CommitsCount" ] = ctx . Repo . CommitsCount
2019-11-13 10:01:19 +03:00
ctx . Next ( )
2015-12-05 01:20:23 +03:00
}
}
2014-12-07 04:22:48 +03:00
// GitHookService checks if repository Git hooks service has been enabled.
2014-10-07 01:50:00 +04:00
func GitHookService ( ) macaron . Handler {
return func ( ctx * Context ) {
2015-11-04 02:40:52 +03:00
if ! ctx . User . CanEditGitHook ( ) {
2018-01-11 00:34:17 +03:00
ctx . NotFound ( "GitHookService" , nil )
2014-10-07 01:50:00 +04:00
return
}
}
}
2017-02-04 18:53:46 +03:00
// UnitTypes returns a macaron middleware to set unit types to context variables.
func UnitTypes ( ) macaron . Handler {
return func ( ctx * Context ) {
ctx . Data [ "UnitTypeCode" ] = models . UnitTypeCode
ctx . Data [ "UnitTypeIssues" ] = models . UnitTypeIssues
ctx . Data [ "UnitTypePullRequests" ] = models . UnitTypePullRequests
ctx . Data [ "UnitTypeReleases" ] = models . UnitTypeReleases
ctx . Data [ "UnitTypeWiki" ] = models . UnitTypeWiki
ctx . Data [ "UnitTypeExternalWiki" ] = models . UnitTypeExternalWiki
ctx . Data [ "UnitTypeExternalTracker" ] = models . UnitTypeExternalTracker
2020-08-17 06:07:38 +03:00
ctx . Data [ "UnitTypeProjects" ] = models . UnitTypeProjects
2017-02-04 18:53:46 +03:00
}
}
2020-09-11 17:48:39 +03:00
// IssueTemplatesFromDefaultBranch checks for issue templates in the repo's default branch
func ( ctx * Context ) IssueTemplatesFromDefaultBranch ( ) [ ] api . IssueTemplate {
var issueTemplates [ ] api . IssueTemplate
if ctx . Repo . Commit == nil {
var err error
ctx . Repo . Commit , err = ctx . Repo . GitRepo . GetBranchCommit ( ctx . Repo . Repository . DefaultBranch )
if err != nil {
return issueTemplates
}
}
for _ , dirName := range IssueTemplateDirCandidates {
tree , err := ctx . Repo . Commit . SubTree ( dirName )
if err != nil {
continue
}
entries , err := tree . ListEntries ( )
if err != nil {
return issueTemplates
}
for _ , entry := range entries {
if strings . HasSuffix ( entry . Name ( ) , ".md" ) {
if entry . Blob ( ) . Size ( ) >= setting . UI . MaxDisplayFileSize {
log . Debug ( "Issue template is too large: %s" , entry . Name ( ) )
continue
}
r , err := entry . Blob ( ) . DataAsync ( )
if err != nil {
log . Debug ( "DataAsync: %v" , err )
continue
}
defer r . Close ( )
data , err := ioutil . ReadAll ( r )
if err != nil {
log . Debug ( "ReadAll: %v" , err )
continue
}
var it api . IssueTemplate
content , err := markdown . ExtractMetadata ( string ( data ) , & it )
if err != nil {
log . Debug ( "ExtractMetadata: %v" , err )
continue
}
it . Content = content
it . FileName = entry . Name ( )
if it . Valid ( ) {
issueTemplates = append ( issueTemplates , it )
}
}
}
if len ( issueTemplates ) > 0 {
return issueTemplates
}
}
return issueTemplates
}