Decouple the different contexts from each other (#24786)
Replace #16455 Close #21803 Mixing different Gitea contexts together causes some problems: 1. Unable to respond proper content when error occurs, eg: Web should respond HTML while API should respond JSON 2. Unclear dependency, eg: it's unclear when Context is used in APIContext, which fields should be initialized, which methods are necessary. To make things clear, this PR introduces a Base context, it only provides basic Req/Resp/Data features. This PR mainly moves code. There are still many legacy problems and TODOs in code, leave unrelated changes to future PRs.
This commit is contained in:
		
					parent
					
						
							
								6ba4f89723
							
						
					
				
			
			
				commit
				
					
						6b33152b7d
					
				
			
		
					 57 changed files with 885 additions and 781 deletions
				
			
		| 
						 | 
					@ -13,18 +13,32 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/auth"
 | 
						"code.gitea.io/gitea/models/auth"
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/cache"
 | 
						"code.gitea.io/gitea/models/unit"
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
 | 
						mc "code.gitea.io/gitea/modules/cache"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/git"
 | 
						"code.gitea.io/gitea/modules/git"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/httpcache"
 | 
						"code.gitea.io/gitea/modules/httpcache"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
					
 | 
				
			||||||
 | 
						"gitea.com/go-chi/cache"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// APIContext is a specific context for API service
 | 
					// APIContext is a specific context for API service
 | 
				
			||||||
type APIContext struct {
 | 
					type APIContext struct {
 | 
				
			||||||
	*Context
 | 
						*Base
 | 
				
			||||||
	Org *APIOrganization
 | 
					
 | 
				
			||||||
 | 
						Cache cache.Cache
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Doer        *user_model.User // current signed-in user
 | 
				
			||||||
 | 
						IsSigned    bool
 | 
				
			||||||
 | 
						IsBasicAuth bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Repo    *Repository
 | 
				
			||||||
 | 
						Org     *APIOrganization
 | 
				
			||||||
 | 
						Package *Package
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Currently, we have the following common fields in error response:
 | 
					// Currently, we have the following common fields in error response:
 | 
				
			||||||
| 
						 | 
					@ -128,11 +142,6 @@ type apiContextKeyType struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var apiContextKey = apiContextKeyType{}
 | 
					var apiContextKey = apiContextKeyType{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithAPIContext set up api context in request
 | 
					 | 
				
			||||||
func WithAPIContext(req *http.Request, ctx *APIContext) *http.Request {
 | 
					 | 
				
			||||||
	return req.WithContext(context.WithValue(req.Context(), apiContextKey, ctx))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetAPIContext returns a context for API routes
 | 
					// GetAPIContext returns a context for API routes
 | 
				
			||||||
func GetAPIContext(req *http.Request) *APIContext {
 | 
					func GetAPIContext(req *http.Request) *APIContext {
 | 
				
			||||||
	return req.Context().Value(apiContextKey).(*APIContext)
 | 
						return req.Context().Value(apiContextKey).(*APIContext)
 | 
				
			||||||
| 
						 | 
					@ -195,21 +204,21 @@ func (ctx *APIContext) CheckForOTP() {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
 | 
						otpHeader := ctx.Req.Header.Get("X-Gitea-OTP")
 | 
				
			||||||
	twofa, err := auth.GetTwoFactorByUID(ctx.Context.Doer.ID)
 | 
						twofa, err := auth.GetTwoFactorByUID(ctx.Doer.ID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if auth.IsErrTwoFactorNotEnrolled(err) {
 | 
							if auth.IsErrTwoFactorNotEnrolled(err) {
 | 
				
			||||||
			return // No 2FA enrollment for this user
 | 
								return // No 2FA enrollment for this user
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Context.Error(http.StatusInternalServerError)
 | 
							ctx.Error(http.StatusInternalServerError, "GetTwoFactorByUID", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ok, err := twofa.ValidateTOTP(otpHeader)
 | 
						ok, err := twofa.ValidateTOTP(otpHeader)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Context.Error(http.StatusInternalServerError)
 | 
							ctx.Error(http.StatusInternalServerError, "ValidateTOTP", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		ctx.Context.Error(http.StatusUnauthorized)
 | 
							ctx.Error(http.StatusUnauthorized, "", nil)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -218,23 +227,17 @@ func (ctx *APIContext) CheckForOTP() {
 | 
				
			||||||
func APIContexter() func(http.Handler) http.Handler {
 | 
					func APIContexter() func(http.Handler) http.Handler {
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(next http.Handler) http.Handler {
 | 
				
			||||||
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
			locale := middleware.Locale(w, req)
 | 
								base, baseCleanUp := NewBaseContext(w, req)
 | 
				
			||||||
			ctx := APIContext{
 | 
								ctx := &APIContext{
 | 
				
			||||||
				Context: &Context{
 | 
									Base:  base,
 | 
				
			||||||
					Resp:   NewResponse(w),
 | 
									Cache: mc.GetCache(),
 | 
				
			||||||
					Data:   middleware.GetContextData(req.Context()),
 | 
									Repo:  &Repository{PullRequest: &PullRequest{}},
 | 
				
			||||||
					Locale: locale,
 | 
									Org:   &APIOrganization{},
 | 
				
			||||||
					Cache:  cache.GetCache(),
 | 
					 | 
				
			||||||
					Repo: &Repository{
 | 
					 | 
				
			||||||
						PullRequest: &PullRequest{},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
					Org: &Organization{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Org: &APIOrganization{},
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			defer ctx.Close()
 | 
								defer baseCleanUp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Req = WithAPIContext(WithContext(req, ctx.Context), &ctx)
 | 
								ctx.Base.AppendContextValue(apiContextKey, ctx)
 | 
				
			||||||
 | 
								ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
 | 
								// If request sends files, parse them here otherwise the Query() can't be parsed and the CsrfToken will be invalid.
 | 
				
			||||||
			if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
 | 
								if ctx.Req.Method == "POST" && strings.Contains(ctx.Req.Header.Get("Content-Type"), "multipart/form-data") {
 | 
				
			||||||
| 
						 | 
					@ -247,8 +250,6 @@ func APIContexter() func(http.Handler) http.Handler {
 | 
				
			||||||
			httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
 | 
								httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
 | 
				
			||||||
			ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 | 
								ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Data["Context"] = &ctx
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
			next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
								next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -301,7 +302,7 @@ func ReferencesGitRepo(allowEmpty ...bool) func(ctx *APIContext) (cancel context
 | 
				
			||||||
			return func() {
 | 
								return func() {
 | 
				
			||||||
				// If it's been set to nil then assume someone else has closed it.
 | 
									// If it's been set to nil then assume someone else has closed it.
 | 
				
			||||||
				if ctx.Repo.GitRepo != nil {
 | 
									if ctx.Repo.GitRepo != nil {
 | 
				
			||||||
					ctx.Repo.GitRepo.Close()
 | 
										_ = ctx.Repo.GitRepo.Close()
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -337,7 +338,7 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		refName := getRefName(ctx.Context, RepoRefAny)
 | 
							refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
							if ctx.Repo.GitRepo.IsBranchExist(refName) {
 | 
				
			||||||
			ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 | 
								ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName)
 | 
				
			||||||
| 
						 | 
					@ -368,3 +369,53 @@ func RepoRefForAPI(next http.Handler) http.Handler {
 | 
				
			||||||
		next.ServeHTTP(w, req)
 | 
							next.ServeHTTP(w, req)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HasAPIError returns true if error occurs in form validation.
 | 
				
			||||||
 | 
					func (ctx *APIContext) HasAPIError() bool {
 | 
				
			||||||
 | 
						hasErr, ok := ctx.Data["HasError"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return hasErr.(bool)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetErrMsg returns error message in form validation.
 | 
				
			||||||
 | 
					func (ctx *APIContext) GetErrMsg() string {
 | 
				
			||||||
 | 
						msg, _ := ctx.Data["ErrorMsg"].(string)
 | 
				
			||||||
 | 
						if msg == "" {
 | 
				
			||||||
 | 
							msg = "invalid form data"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return msg
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// NotFoundOrServerError use error check function to determine if the error
 | 
				
			||||||
 | 
					// is about not found. It responds with 404 status code for not found error,
 | 
				
			||||||
 | 
					// or error context description for logging purpose of 500 server error.
 | 
				
			||||||
 | 
					func (ctx *APIContext) NotFoundOrServerError(logMsg string, errCheck func(error) bool, logErr error) {
 | 
				
			||||||
 | 
						if errCheck(logErr) {
 | 
				
			||||||
 | 
							ctx.JSON(http.StatusNotFound, nil)
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Error(http.StatusInternalServerError, "NotFoundOrServerError", logMsg)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsUserSiteAdmin returns true if current user is a site admin
 | 
				
			||||||
 | 
					func (ctx *APIContext) IsUserSiteAdmin() bool {
 | 
				
			||||||
 | 
						return ctx.IsSigned && ctx.Doer.IsAdmin
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsUserRepoAdmin returns true if current user is admin in current repo
 | 
				
			||||||
 | 
					func (ctx *APIContext) IsUserRepoAdmin() bool {
 | 
				
			||||||
 | 
						return ctx.Repo.IsAdmin()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// IsUserRepoWriter returns true if current user has write privilege in current repo
 | 
				
			||||||
 | 
					func (ctx *APIContext) IsUserRepoWriter(unitTypes []unit.Type) bool {
 | 
				
			||||||
 | 
						for _, unitType := range unitTypes {
 | 
				
			||||||
 | 
							if ctx.Repo.CanWrite(unitType) {
 | 
				
			||||||
 | 
								return true
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										300
									
								
								modules/context/base.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										300
									
								
								modules/context/base.go
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,300 @@
 | 
				
			||||||
 | 
					// Copyright 2020 The Gitea Authors. All rights reserved.
 | 
				
			||||||
 | 
					// SPDX-License-Identifier: MIT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"context"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
 | 
						"strconv"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/httplib"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/json"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/translation"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-chi/chi/v5"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type contextValuePair struct {
 | 
				
			||||||
 | 
						key     any
 | 
				
			||||||
 | 
						valueFn func() any
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Base struct {
 | 
				
			||||||
 | 
						originCtx     context.Context
 | 
				
			||||||
 | 
						contextValues []contextValuePair
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Resp ResponseWriter
 | 
				
			||||||
 | 
						Req  *http.Request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Data is prepared by ContextDataStore middleware, this field only refers to the pre-created/prepared ContextData.
 | 
				
			||||||
 | 
						// Although it's mainly used for MVC templates, sometimes it's also used to pass data between middlewares/handler
 | 
				
			||||||
 | 
						Data middleware.ContextData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Locale is mainly for Web context, although the API context also uses it in some cases: message response, form validation
 | 
				
			||||||
 | 
						Locale translation.Locale
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) Deadline() (deadline time.Time, ok bool) {
 | 
				
			||||||
 | 
						return b.originCtx.Deadline()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) Done() <-chan struct{} {
 | 
				
			||||||
 | 
						return b.originCtx.Done()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) Err() error {
 | 
				
			||||||
 | 
						return b.originCtx.Err()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) Value(key any) any {
 | 
				
			||||||
 | 
						for _, pair := range b.contextValues {
 | 
				
			||||||
 | 
							if pair.key == key {
 | 
				
			||||||
 | 
								return pair.valueFn()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return b.originCtx.Value(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) AppendContextValueFunc(key any, valueFn func() any) any {
 | 
				
			||||||
 | 
						b.contextValues = append(b.contextValues, contextValuePair{key, valueFn})
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) AppendContextValue(key, value any) any {
 | 
				
			||||||
 | 
						b.contextValues = append(b.contextValues, contextValuePair{key, func() any { return value }})
 | 
				
			||||||
 | 
						return b
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) GetData() middleware.ContextData {
 | 
				
			||||||
 | 
						return b.Data
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
 | 
				
			||||||
 | 
					func (b *Base) AppendAccessControlExposeHeaders(names ...string) {
 | 
				
			||||||
 | 
						val := b.RespHeader().Get("Access-Control-Expose-Headers")
 | 
				
			||||||
 | 
						if len(val) != 0 {
 | 
				
			||||||
 | 
							b.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							b.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetTotalCountHeader set "X-Total-Count" header
 | 
				
			||||||
 | 
					func (b *Base) SetTotalCountHeader(total int64) {
 | 
				
			||||||
 | 
						b.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
 | 
				
			||||||
 | 
						b.AppendAccessControlExposeHeaders("X-Total-Count")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Written returns true if there are something sent to web browser
 | 
				
			||||||
 | 
					func (b *Base) Written() bool {
 | 
				
			||||||
 | 
						return b.Resp.Status() > 0
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Status writes status code
 | 
				
			||||||
 | 
					func (b *Base) Status(status int) {
 | 
				
			||||||
 | 
						b.Resp.WriteHeader(status)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Write writes data to web browser
 | 
				
			||||||
 | 
					func (b *Base) Write(bs []byte) (int, error) {
 | 
				
			||||||
 | 
						return b.Resp.Write(bs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RespHeader returns the response header
 | 
				
			||||||
 | 
					func (b *Base) RespHeader() http.Header {
 | 
				
			||||||
 | 
						return b.Resp.Header()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Error returned an error to web browser
 | 
				
			||||||
 | 
					func (b *Base) Error(status int, contents ...string) {
 | 
				
			||||||
 | 
						v := http.StatusText(status)
 | 
				
			||||||
 | 
						if len(contents) > 0 {
 | 
				
			||||||
 | 
							v = contents[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						http.Error(b.Resp, v, status)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// JSON render content as JSON
 | 
				
			||||||
 | 
					func (b *Base) JSON(status int, content interface{}) {
 | 
				
			||||||
 | 
						b.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
 | 
				
			||||||
 | 
						b.Resp.WriteHeader(status)
 | 
				
			||||||
 | 
						if err := json.NewEncoder(b.Resp).Encode(content); err != nil {
 | 
				
			||||||
 | 
							log.Error("Render JSON failed: %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// RemoteAddr returns the client machine ip address
 | 
				
			||||||
 | 
					func (b *Base) RemoteAddr() string {
 | 
				
			||||||
 | 
						return b.Req.RemoteAddr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Params returns the param on route
 | 
				
			||||||
 | 
					func (b *Base) Params(p string) string {
 | 
				
			||||||
 | 
						s, _ := url.PathUnescape(chi.URLParam(b.Req, strings.TrimPrefix(p, ":")))
 | 
				
			||||||
 | 
						return s
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ParamsInt64 returns the param on route as int64
 | 
				
			||||||
 | 
					func (b *Base) ParamsInt64(p string) int64 {
 | 
				
			||||||
 | 
						v, _ := strconv.ParseInt(b.Params(p), 10, 64)
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SetParams set params into routes
 | 
				
			||||||
 | 
					func (b *Base) SetParams(k, v string) {
 | 
				
			||||||
 | 
						chiCtx := chi.RouteContext(b)
 | 
				
			||||||
 | 
						chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormString returns the first value matching the provided key in the form as a string
 | 
				
			||||||
 | 
					func (b *Base) FormString(key string) string {
 | 
				
			||||||
 | 
						return b.Req.FormValue(key)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormStrings returns a string slice for the provided key from the form
 | 
				
			||||||
 | 
					func (b *Base) FormStrings(key string) []string {
 | 
				
			||||||
 | 
						if b.Req.Form == nil {
 | 
				
			||||||
 | 
							if err := b.Req.ParseMultipartForm(32 << 20); err != nil {
 | 
				
			||||||
 | 
								return nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if v, ok := b.Req.Form[key]; ok {
 | 
				
			||||||
 | 
							return v
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormTrim returns the first value for the provided key in the form as a space trimmed string
 | 
				
			||||||
 | 
					func (b *Base) FormTrim(key string) string {
 | 
				
			||||||
 | 
						return strings.TrimSpace(b.Req.FormValue(key))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormInt returns the first value for the provided key in the form as an int
 | 
				
			||||||
 | 
					func (b *Base) FormInt(key string) int {
 | 
				
			||||||
 | 
						v, _ := strconv.Atoi(b.Req.FormValue(key))
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormInt64 returns the first value for the provided key in the form as an int64
 | 
				
			||||||
 | 
					func (b *Base) FormInt64(key string) int64 {
 | 
				
			||||||
 | 
						v, _ := strconv.ParseInt(b.Req.FormValue(key), 10, 64)
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
 | 
				
			||||||
 | 
					func (b *Base) FormBool(key string) bool {
 | 
				
			||||||
 | 
						s := b.Req.FormValue(key)
 | 
				
			||||||
 | 
						v, _ := strconv.ParseBool(s)
 | 
				
			||||||
 | 
						v = v || strings.EqualFold(s, "on")
 | 
				
			||||||
 | 
						return v
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
 | 
				
			||||||
 | 
					// for the provided key exists in the form else it returns OptionalBoolNone
 | 
				
			||||||
 | 
					func (b *Base) FormOptionalBool(key string) util.OptionalBool {
 | 
				
			||||||
 | 
						value := b.Req.FormValue(key)
 | 
				
			||||||
 | 
						if len(value) == 0 {
 | 
				
			||||||
 | 
							return util.OptionalBoolNone
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						s := b.Req.FormValue(key)
 | 
				
			||||||
 | 
						v, _ := strconv.ParseBool(s)
 | 
				
			||||||
 | 
						v = v || strings.EqualFold(s, "on")
 | 
				
			||||||
 | 
						return util.OptionalBoolOf(v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) SetFormString(key, value string) {
 | 
				
			||||||
 | 
						_ = b.Req.FormValue(key) // force parse form
 | 
				
			||||||
 | 
						b.Req.Form.Set(key, value)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PlainTextBytes renders bytes as plain text
 | 
				
			||||||
 | 
					func (b *Base) plainTextInternal(skip, status int, bs []byte) {
 | 
				
			||||||
 | 
						statusPrefix := status / 100
 | 
				
			||||||
 | 
						if statusPrefix == 4 || statusPrefix == 5 {
 | 
				
			||||||
 | 
							log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
 | 
				
			||||||
 | 
						b.Resp.Header().Set("X-Content-Type-Options", "nosniff")
 | 
				
			||||||
 | 
						b.Resp.WriteHeader(status)
 | 
				
			||||||
 | 
						if _, err := b.Resp.Write(bs); err != nil {
 | 
				
			||||||
 | 
							log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PlainTextBytes renders bytes as plain text
 | 
				
			||||||
 | 
					func (b *Base) PlainTextBytes(status int, bs []byte) {
 | 
				
			||||||
 | 
						b.plainTextInternal(2, status, bs)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// PlainText renders content as plain text
 | 
				
			||||||
 | 
					func (b *Base) PlainText(status int, text string) {
 | 
				
			||||||
 | 
						b.plainTextInternal(2, status, []byte(text))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Redirect redirects the request
 | 
				
			||||||
 | 
					func (b *Base) Redirect(location string, status ...int) {
 | 
				
			||||||
 | 
						code := http.StatusSeeOther
 | 
				
			||||||
 | 
						if len(status) == 1 {
 | 
				
			||||||
 | 
							code = status[0]
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
 | 
				
			||||||
 | 
							// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
 | 
				
			||||||
 | 
							// 1. the first request to "/my-path" contains cookie
 | 
				
			||||||
 | 
							// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
 | 
				
			||||||
 | 
							// 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
 | 
				
			||||||
 | 
							// 4. then the browser accepts the empty session, then the user is logged out
 | 
				
			||||||
 | 
							// So in this case, we should remove the session cookie from the response header
 | 
				
			||||||
 | 
							removeSessionCookieHeader(b.Resp)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						http.Redirect(b.Resp, b.Req, location, code)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ServeHeaderOptions httplib.ServeHeaderOptions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) SetServeHeaders(opt *ServeHeaderOptions) {
 | 
				
			||||||
 | 
						httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opt))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// ServeContent serves content to http request
 | 
				
			||||||
 | 
					func (b *Base) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
 | 
				
			||||||
 | 
						httplib.ServeSetHeaders(b.Resp, (*httplib.ServeHeaderOptions)(opts))
 | 
				
			||||||
 | 
						http.ServeContent(b.Resp, b.Req, opts.Filename, opts.LastModified, r)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Close frees all resources hold by Context
 | 
				
			||||||
 | 
					func (b *Base) cleanUp() {
 | 
				
			||||||
 | 
						if b.Req != nil && b.Req.MultipartForm != nil {
 | 
				
			||||||
 | 
							_ = b.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) Tr(msg string, args ...any) string {
 | 
				
			||||||
 | 
						return b.Locale.Tr(msg, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (b *Base) TrN(cnt any, key1, keyN string, args ...any) string {
 | 
				
			||||||
 | 
						return b.Locale.TrN(cnt, key1, keyN, args...)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewBaseContext(resp http.ResponseWriter, req *http.Request) (b *Base, closeFunc func()) {
 | 
				
			||||||
 | 
						b = &Base{
 | 
				
			||||||
 | 
							originCtx: req.Context(),
 | 
				
			||||||
 | 
							Req:       req,
 | 
				
			||||||
 | 
							Resp:      WrapResponseWriter(resp),
 | 
				
			||||||
 | 
							Locale:    middleware.Locale(resp, req),
 | 
				
			||||||
 | 
							Data:      middleware.GetContextData(req.Context()),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						b.AppendContextValue(translation.ContextKey, b.Locale)
 | 
				
			||||||
 | 
						b.Req = b.Req.WithContext(b)
 | 
				
			||||||
 | 
						return b, b.cleanUp
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@
 | 
				
			||||||
package context
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
					 | 
				
			||||||
	"html"
 | 
						"html"
 | 
				
			||||||
	"html/template"
 | 
						"html/template"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
| 
						 | 
					@ -36,38 +35,27 @@ type Render interface {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Context represents context of a request.
 | 
					// Context represents context of a request.
 | 
				
			||||||
type Context struct {
 | 
					type Context struct {
 | 
				
			||||||
	Resp   ResponseWriter
 | 
						*Base
 | 
				
			||||||
	Req    *http.Request
 | 
					 | 
				
			||||||
	Render Render
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Data     middleware.ContextData // data used by MVC templates
 | 
						Render   Render
 | 
				
			||||||
	PageData map[string]any         // data used by JavaScript modules in one page, it's `window.config.pageData`
 | 
						PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Locale  translation.Locale
 | 
					 | 
				
			||||||
	Cache   cache.Cache
 | 
						Cache   cache.Cache
 | 
				
			||||||
	Csrf    CSRFProtector
 | 
						Csrf    CSRFProtector
 | 
				
			||||||
	Flash   *middleware.Flash
 | 
						Flash   *middleware.Flash
 | 
				
			||||||
	Session session.Store
 | 
						Session session.Store
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Link        string // current request URL (without query string)
 | 
						Link string // current request URL (without query string)
 | 
				
			||||||
	Doer        *user_model.User
 | 
					
 | 
				
			||||||
 | 
						Doer        *user_model.User // current signed-in user
 | 
				
			||||||
	IsSigned    bool
 | 
						IsSigned    bool
 | 
				
			||||||
	IsBasicAuth bool
 | 
						IsBasicAuth bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ContextUser *user_model.User
 | 
						ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer
 | 
				
			||||||
	Repo        *Repository
 | 
					 | 
				
			||||||
	Org         *Organization
 | 
					 | 
				
			||||||
	Package     *Package
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Close frees all resources hold by Context
 | 
						Repo    *Repository
 | 
				
			||||||
func (ctx *Context) Close() error {
 | 
						Org     *Organization
 | 
				
			||||||
	var err error
 | 
						Package *Package
 | 
				
			||||||
	if ctx.Req != nil && ctx.Req.MultipartForm != nil {
 | 
					 | 
				
			||||||
		err = ctx.Req.MultipartForm.RemoveAll() // remove the temp files buffered to tmp directory
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// TODO: close opened repo, and more
 | 
					 | 
				
			||||||
	return err
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
 | 
					// TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
 | 
				
			||||||
| 
						 | 
					@ -80,55 +68,30 @@ func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
 | 
				
			||||||
	return ctx.Locale.Tr(msg, trArgs...)
 | 
						return ctx.Locale.Tr(msg, trArgs...)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ctx *Context) Tr(msg string, args ...any) string {
 | 
					 | 
				
			||||||
	return ctx.Locale.Tr(msg, args...)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (ctx *Context) TrN(cnt any, key1, keyN string, args ...any) string {
 | 
					 | 
				
			||||||
	return ctx.Locale.TrN(cnt, key1, keyN, args...)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Deadline is part of the interface for context.Context and we pass this to the request context
 | 
					 | 
				
			||||||
func (ctx *Context) Deadline() (deadline time.Time, ok bool) {
 | 
					 | 
				
			||||||
	return ctx.Req.Context().Deadline()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Done is part of the interface for context.Context and we pass this to the request context
 | 
					 | 
				
			||||||
func (ctx *Context) Done() <-chan struct{} {
 | 
					 | 
				
			||||||
	return ctx.Req.Context().Done()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Err is part of the interface for context.Context and we pass this to the request context
 | 
					 | 
				
			||||||
func (ctx *Context) Err() error {
 | 
					 | 
				
			||||||
	return ctx.Req.Context().Err()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Value is part of the interface for context.Context and we pass this to the request context
 | 
					 | 
				
			||||||
func (ctx *Context) Value(key interface{}) interface{} {
 | 
					 | 
				
			||||||
	if key == git.RepositoryContextKey && ctx.Repo != nil {
 | 
					 | 
				
			||||||
		return ctx.Repo.GitRepo
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if key == translation.ContextKey && ctx.Locale != nil {
 | 
					 | 
				
			||||||
		return ctx.Locale
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return ctx.Req.Context().Value(key)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type contextKeyType struct{}
 | 
					type contextKeyType struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var contextKey interface{} = contextKeyType{}
 | 
					var contextKey interface{} = contextKeyType{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithContext set up install context in request
 | 
					func GetContext(req *http.Request) *Context {
 | 
				
			||||||
func WithContext(req *http.Request, ctx *Context) *http.Request {
 | 
						ctx, _ := req.Context().Value(contextKey).(*Context)
 | 
				
			||||||
	return req.WithContext(context.WithValue(req.Context(), contextKey, ctx))
 | 
						return ctx
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetContext retrieves install context from request
 | 
					// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
 | 
				
			||||||
func GetContext(req *http.Request) *Context {
 | 
					type ValidateContext struct {
 | 
				
			||||||
	if ctx, ok := req.Context().Value(contextKey).(*Context); ok {
 | 
						*Base
 | 
				
			||||||
		return ctx
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetValidateContext gets a context for middleware form validation
 | 
				
			||||||
 | 
					func GetValidateContext(req *http.Request) (ctx *ValidateContext) {
 | 
				
			||||||
 | 
						if ctxAPI, ok := req.Context().Value(apiContextKey).(*APIContext); ok {
 | 
				
			||||||
 | 
							ctx = &ValidateContext{Base: ctxAPI.Base}
 | 
				
			||||||
 | 
						} else if ctxWeb, ok := req.Context().Value(contextKey).(*Context); ok {
 | 
				
			||||||
 | 
							ctx = &ValidateContext{Base: ctxWeb.Base}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							panic("invalid context, expect either APIContext or Context")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return ctx
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Contexter initializes a classic context for a request.
 | 
					// Contexter initializes a classic context for a request.
 | 
				
			||||||
| 
						 | 
					@ -150,20 +113,17 @@ func Contexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(next http.Handler) http.Handler {
 | 
				
			||||||
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
			ctx := Context{
 | 
								base, baseCleanUp := NewBaseContext(resp, req)
 | 
				
			||||||
				Resp:    NewResponse(resp),
 | 
								ctx := &Context{
 | 
				
			||||||
 | 
									Base:    base,
 | 
				
			||||||
				Cache:   mc.GetCache(),
 | 
									Cache:   mc.GetCache(),
 | 
				
			||||||
				Locale:  middleware.Locale(resp, req),
 | 
					 | 
				
			||||||
				Link:    setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"),
 | 
									Link:    setting.AppSubURL + strings.TrimSuffix(req.URL.EscapedPath(), "/"),
 | 
				
			||||||
				Render:  rnd,
 | 
									Render:  rnd,
 | 
				
			||||||
				Session: session.GetSession(req),
 | 
									Session: session.GetSession(req),
 | 
				
			||||||
				Repo: &Repository{
 | 
									Repo:    &Repository{PullRequest: &PullRequest{}},
 | 
				
			||||||
					PullRequest: &PullRequest{},
 | 
									Org:     &Organization{},
 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
				Org:  &Organization{},
 | 
					 | 
				
			||||||
				Data: middleware.GetContextData(req.Context()),
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			defer ctx.Close()
 | 
								defer baseCleanUp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
 | 
								ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
 | 
				
			||||||
			ctx.Data["Context"] = &ctx
 | 
								ctx.Data["Context"] = &ctx
 | 
				
			||||||
| 
						 | 
					@ -175,15 +135,17 @@ func Contexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
			ctx.PageData = map[string]any{}
 | 
								ctx.PageData = map[string]any{}
 | 
				
			||||||
			ctx.Data["PageData"] = ctx.PageData
 | 
								ctx.Data["PageData"] = ctx.PageData
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Req = WithContext(req, &ctx)
 | 
								ctx.Base.AppendContextValue(contextKey, ctx)
 | 
				
			||||||
			ctx.Csrf = PrepareCSRFProtector(csrfOpts, &ctx)
 | 
								ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// Get the last flash message from cookie
 | 
								// Get the last flash message from cookie
 | 
				
			||||||
			lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
 | 
								lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
 | 
				
			||||||
			if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
 | 
								if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
 | 
				
			||||||
				// store last Flash message into the template data, to render it
 | 
									// store last Flash message into the template data, to render it
 | 
				
			||||||
				ctx.Data["Flash"] = &middleware.Flash{
 | 
									ctx.Data["Flash"] = &middleware.Flash{
 | 
				
			||||||
					DataStore:  &ctx,
 | 
										DataStore:  ctx,
 | 
				
			||||||
					Values:     vals,
 | 
										Values:     vals,
 | 
				
			||||||
					ErrorMsg:   vals.Get("error"),
 | 
										ErrorMsg:   vals.Get("error"),
 | 
				
			||||||
					SuccessMsg: vals.Get("success"),
 | 
										SuccessMsg: vals.Get("success"),
 | 
				
			||||||
| 
						 | 
					@ -193,7 +155,7 @@ func Contexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// prepare an empty Flash message for current request
 | 
								// prepare an empty Flash message for current request
 | 
				
			||||||
			ctx.Flash = &middleware.Flash{DataStore: &ctx, Values: url.Values{}}
 | 
								ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
 | 
				
			||||||
			ctx.Resp.Before(func(resp ResponseWriter) {
 | 
								ctx.Resp.Before(func(resp ResponseWriter) {
 | 
				
			||||||
				if val := ctx.Flash.Encode(); val != "" {
 | 
									if val := ctx.Flash.Encode(); val != "" {
 | 
				
			||||||
					middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
 | 
										middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
 | 
				
			||||||
| 
						 | 
					@ -235,3 +197,24 @@ func Contexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// HasError returns true if error occurs in form validation.
 | 
				
			||||||
 | 
					// Attention: this function changes ctx.Data and ctx.Flash
 | 
				
			||||||
 | 
					func (ctx *Context) HasError() bool {
 | 
				
			||||||
 | 
						hasErr, ok := ctx.Data["HasError"]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ctx.Flash.ErrorMsg = ctx.GetErrMsg()
 | 
				
			||||||
 | 
						ctx.Data["Flash"] = ctx.Flash
 | 
				
			||||||
 | 
						return hasErr.(bool)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// GetErrMsg returns error message in form validation.
 | 
				
			||||||
 | 
					func (ctx *Context) GetErrMsg() string {
 | 
				
			||||||
 | 
						msg, _ := ctx.Data["ErrorMsg"].(string)
 | 
				
			||||||
 | 
						if msg == "" {
 | 
				
			||||||
 | 
							msg = "invalid form data"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return msg
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,43 +0,0 @@
 | 
				
			||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MIT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import "code.gitea.io/gitea/modules/web/middleware"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetData returns the data
 | 
					 | 
				
			||||||
func (ctx *Context) GetData() middleware.ContextData {
 | 
					 | 
				
			||||||
	return ctx.Data
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HasAPIError returns true if error occurs in form validation.
 | 
					 | 
				
			||||||
func (ctx *Context) HasAPIError() bool {
 | 
					 | 
				
			||||||
	hasErr, ok := ctx.Data["HasError"]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return hasErr.(bool)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetErrMsg returns error message
 | 
					 | 
				
			||||||
func (ctx *Context) GetErrMsg() string {
 | 
					 | 
				
			||||||
	return ctx.Data["ErrorMsg"].(string)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HasError returns true if error occurs in form validation.
 | 
					 | 
				
			||||||
// Attention: this function changes ctx.Data and ctx.Flash
 | 
					 | 
				
			||||||
func (ctx *Context) HasError() bool {
 | 
					 | 
				
			||||||
	hasErr, ok := ctx.Data["HasError"]
 | 
					 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Flash.ErrorMsg = ctx.Data["ErrorMsg"].(string)
 | 
					 | 
				
			||||||
	ctx.Data["Flash"] = ctx.Flash
 | 
					 | 
				
			||||||
	return hasErr.(bool)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// HasValue returns true if value of given name exists.
 | 
					 | 
				
			||||||
func (ctx *Context) HasValue(name string) bool {
 | 
					 | 
				
			||||||
	_, ok := ctx.Data[name]
 | 
					 | 
				
			||||||
	return ok
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -1,72 +0,0 @@
 | 
				
			||||||
// Copyright 2021 The Gitea Authors. All rights reserved.
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MIT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormString returns the first value matching the provided key in the form as a string
 | 
					 | 
				
			||||||
func (ctx *Context) FormString(key string) string {
 | 
					 | 
				
			||||||
	return ctx.Req.FormValue(key)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormStrings returns a string slice for the provided key from the form
 | 
					 | 
				
			||||||
func (ctx *Context) FormStrings(key string) []string {
 | 
					 | 
				
			||||||
	if ctx.Req.Form == nil {
 | 
					 | 
				
			||||||
		if err := ctx.Req.ParseMultipartForm(32 << 20); err != nil {
 | 
					 | 
				
			||||||
			return nil
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if v, ok := ctx.Req.Form[key]; ok {
 | 
					 | 
				
			||||||
		return v
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormTrim returns the first value for the provided key in the form as a space trimmed string
 | 
					 | 
				
			||||||
func (ctx *Context) FormTrim(key string) string {
 | 
					 | 
				
			||||||
	return strings.TrimSpace(ctx.Req.FormValue(key))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormInt returns the first value for the provided key in the form as an int
 | 
					 | 
				
			||||||
func (ctx *Context) FormInt(key string) int {
 | 
					 | 
				
			||||||
	v, _ := strconv.Atoi(ctx.Req.FormValue(key))
 | 
					 | 
				
			||||||
	return v
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormInt64 returns the first value for the provided key in the form as an int64
 | 
					 | 
				
			||||||
func (ctx *Context) FormInt64(key string) int64 {
 | 
					 | 
				
			||||||
	v, _ := strconv.ParseInt(ctx.Req.FormValue(key), 10, 64)
 | 
					 | 
				
			||||||
	return v
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormBool returns true if the value for the provided key in the form is "1", "true" or "on"
 | 
					 | 
				
			||||||
func (ctx *Context) FormBool(key string) bool {
 | 
					 | 
				
			||||||
	s := ctx.Req.FormValue(key)
 | 
					 | 
				
			||||||
	v, _ := strconv.ParseBool(s)
 | 
					 | 
				
			||||||
	v = v || strings.EqualFold(s, "on")
 | 
					 | 
				
			||||||
	return v
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// FormOptionalBool returns an OptionalBoolTrue or OptionalBoolFalse if the value
 | 
					 | 
				
			||||||
// for the provided key exists in the form else it returns OptionalBoolNone
 | 
					 | 
				
			||||||
func (ctx *Context) FormOptionalBool(key string) util.OptionalBool {
 | 
					 | 
				
			||||||
	value := ctx.Req.FormValue(key)
 | 
					 | 
				
			||||||
	if len(value) == 0 {
 | 
					 | 
				
			||||||
		return util.OptionalBoolNone
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	s := ctx.Req.FormValue(key)
 | 
					 | 
				
			||||||
	v, _ := strconv.ParseBool(s)
 | 
					 | 
				
			||||||
	v = v || strings.EqualFold(s, "on")
 | 
					 | 
				
			||||||
	return util.OptionalBoolOf(v)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (ctx *Context) SetFormString(key, value string) {
 | 
					 | 
				
			||||||
	_ = ctx.Req.FormValue(key) // force parse form
 | 
					 | 
				
			||||||
	ctx.Req.Form.Set(key, value)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -6,36 +6,9 @@ package context
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/url"
 | 
					 | 
				
			||||||
	"strconv"
 | 
					 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					 | 
				
			||||||
	"github.com/go-chi/chi/v5"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RemoteAddr returns the client machine ip address
 | 
					 | 
				
			||||||
func (ctx *Context) RemoteAddr() string {
 | 
					 | 
				
			||||||
	return ctx.Req.RemoteAddr
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Params returns the param on route
 | 
					 | 
				
			||||||
func (ctx *Context) Params(p string) string {
 | 
					 | 
				
			||||||
	s, _ := url.PathUnescape(chi.URLParam(ctx.Req, strings.TrimPrefix(p, ":")))
 | 
					 | 
				
			||||||
	return s
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ParamsInt64 returns the param on route as int64
 | 
					 | 
				
			||||||
func (ctx *Context) ParamsInt64(p string) int64 {
 | 
					 | 
				
			||||||
	v, _ := strconv.ParseInt(ctx.Params(p), 10, 64)
 | 
					 | 
				
			||||||
	return v
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// SetParams set params into routes
 | 
					 | 
				
			||||||
func (ctx *Context) SetParams(k, v string) {
 | 
					 | 
				
			||||||
	chiCtx := chi.RouteContext(ctx)
 | 
					 | 
				
			||||||
	chiCtx.URLParams.Add(strings.TrimPrefix(k, ":"), url.PathEscape(v))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// UploadStream returns the request body or the first form file
 | 
					// UploadStream returns the request body or the first form file
 | 
				
			||||||
// Only form files need to get closed.
 | 
					// Only form files need to get closed.
 | 
				
			||||||
func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {
 | 
					func (ctx *Context) UploadStream() (rd io.ReadCloser, needToClose bool, err error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,49 +16,17 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	user_model "code.gitea.io/gitea/models/user"
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/base"
 | 
						"code.gitea.io/gitea/modules/base"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/json"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/templates"
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetTotalCountHeader set "X-Total-Count" header
 | 
					 | 
				
			||||||
func (ctx *Context) SetTotalCountHeader(total int64) {
 | 
					 | 
				
			||||||
	ctx.RespHeader().Set("X-Total-Count", fmt.Sprint(total))
 | 
					 | 
				
			||||||
	ctx.AppendAccessControlExposeHeaders("X-Total-Count")
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// AppendAccessControlExposeHeaders append headers by name to "Access-Control-Expose-Headers" header
 | 
					 | 
				
			||||||
func (ctx *Context) AppendAccessControlExposeHeaders(names ...string) {
 | 
					 | 
				
			||||||
	val := ctx.RespHeader().Get("Access-Control-Expose-Headers")
 | 
					 | 
				
			||||||
	if len(val) != 0 {
 | 
					 | 
				
			||||||
		ctx.RespHeader().Set("Access-Control-Expose-Headers", fmt.Sprintf("%s, %s", val, strings.Join(names, ", ")))
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		ctx.RespHeader().Set("Access-Control-Expose-Headers", strings.Join(names, ", "))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Written returns true if there are something sent to web browser
 | 
					 | 
				
			||||||
func (ctx *Context) Written() bool {
 | 
					 | 
				
			||||||
	return ctx.Resp.Status() > 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Status writes status code
 | 
					 | 
				
			||||||
func (ctx *Context) Status(status int) {
 | 
					 | 
				
			||||||
	ctx.Resp.WriteHeader(status)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Write writes data to web browser
 | 
					 | 
				
			||||||
func (ctx *Context) Write(bs []byte) (int, error) {
 | 
					 | 
				
			||||||
	return ctx.Resp.Write(bs)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RedirectToUser redirect to a differently-named user
 | 
					// RedirectToUser redirect to a differently-named user
 | 
				
			||||||
func RedirectToUser(ctx *Context, userName string, redirectUserID int64) {
 | 
					func RedirectToUser(ctx *Base, userName string, redirectUserID int64) {
 | 
				
			||||||
	user, err := user_model.GetUserByID(ctx, redirectUserID)
 | 
						user, err := user_model.GetUserByID(ctx, redirectUserID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetUserByID", err)
 | 
							ctx.Error(http.StatusInternalServerError, "unable to get user")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -211,69 +179,3 @@ func (ctx *Context) NotFoundOrServerError(logMsg string, errCheck func(error) bo
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ctx.serverErrorInternal(logMsg, logErr)
 | 
						ctx.serverErrorInternal(logMsg, logErr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					 | 
				
			||||||
// PlainTextBytes renders bytes as plain text
 | 
					 | 
				
			||||||
func (ctx *Context) plainTextInternal(skip, status int, bs []byte) {
 | 
					 | 
				
			||||||
	statusPrefix := status / 100
 | 
					 | 
				
			||||||
	if statusPrefix == 4 || statusPrefix == 5 {
 | 
					 | 
				
			||||||
		log.Log(skip, log.TRACE, "plainTextInternal (status=%d): %s", status, string(bs))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	ctx.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8")
 | 
					 | 
				
			||||||
	ctx.Resp.Header().Set("X-Content-Type-Options", "nosniff")
 | 
					 | 
				
			||||||
	ctx.Resp.WriteHeader(status)
 | 
					 | 
				
			||||||
	if _, err := ctx.Resp.Write(bs); err != nil {
 | 
					 | 
				
			||||||
		log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PlainTextBytes renders bytes as plain text
 | 
					 | 
				
			||||||
func (ctx *Context) PlainTextBytes(status int, bs []byte) {
 | 
					 | 
				
			||||||
	ctx.plainTextInternal(2, status, bs)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// PlainText renders content as plain text
 | 
					 | 
				
			||||||
func (ctx *Context) PlainText(status int, text string) {
 | 
					 | 
				
			||||||
	ctx.plainTextInternal(2, status, []byte(text))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// RespHeader returns the response header
 | 
					 | 
				
			||||||
func (ctx *Context) RespHeader() http.Header {
 | 
					 | 
				
			||||||
	return ctx.Resp.Header()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Error returned an error to web browser
 | 
					 | 
				
			||||||
func (ctx *Context) Error(status int, contents ...string) {
 | 
					 | 
				
			||||||
	v := http.StatusText(status)
 | 
					 | 
				
			||||||
	if len(contents) > 0 {
 | 
					 | 
				
			||||||
		v = contents[0]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	http.Error(ctx.Resp, v, status)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// JSON render content as JSON
 | 
					 | 
				
			||||||
func (ctx *Context) JSON(status int, content interface{}) {
 | 
					 | 
				
			||||||
	ctx.Resp.Header().Set("Content-Type", "application/json;charset=utf-8")
 | 
					 | 
				
			||||||
	ctx.Resp.WriteHeader(status)
 | 
					 | 
				
			||||||
	if err := json.NewEncoder(ctx.Resp).Encode(content); err != nil {
 | 
					 | 
				
			||||||
		ctx.ServerError("Render JSON failed", err)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Redirect redirects the request
 | 
					 | 
				
			||||||
func (ctx *Context) Redirect(location string, status ...int) {
 | 
					 | 
				
			||||||
	code := http.StatusSeeOther
 | 
					 | 
				
			||||||
	if len(status) == 1 {
 | 
					 | 
				
			||||||
		code = status[0]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if strings.Contains(location, "://") || strings.HasPrefix(location, "//") {
 | 
					 | 
				
			||||||
		// Some browsers (Safari) have buggy behavior for Cookie + Cache + External Redirection, eg: /my-path => https://other/path
 | 
					 | 
				
			||||||
		// 1. the first request to "/my-path" contains cookie
 | 
					 | 
				
			||||||
		// 2. some time later, the request to "/my-path" doesn't contain cookie (caused by Prevent web tracking)
 | 
					 | 
				
			||||||
		// 3. Gitea's Sessioner doesn't see the session cookie, so it generates a new session id, and returns it to browser
 | 
					 | 
				
			||||||
		// 4. then the browser accepts the empty session, then the user is logged out
 | 
					 | 
				
			||||||
		// So in this case, we should remove the session cookie from the response header
 | 
					 | 
				
			||||||
		removeSessionCookieHeader(ctx.Resp)
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	http.Redirect(ctx.Resp, ctx.Req, location, code)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,23 +0,0 @@
 | 
				
			||||||
// Copyright 2023 The Gitea Authors. All rights reserved.
 | 
					 | 
				
			||||||
// SPDX-License-Identifier: MIT
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
package context
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import (
 | 
					 | 
				
			||||||
	"io"
 | 
					 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/httplib"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type ServeHeaderOptions httplib.ServeHeaderOptions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (ctx *Context) SetServeHeaders(opt *ServeHeaderOptions) {
 | 
					 | 
				
			||||||
	httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opt))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// ServeContent serves content to http request
 | 
					 | 
				
			||||||
func (ctx *Context) ServeContent(r io.ReadSeeker, opts *ServeHeaderOptions) {
 | 
					 | 
				
			||||||
	httplib.ServeSetHeaders(ctx.Resp, (*httplib.ServeHeaderOptions)(opts))
 | 
					 | 
				
			||||||
	http.ServeContent(ctx.Resp, ctx.Req, opts.Filename, opts.LastModified, r)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ func GetOrganizationByParams(ctx *Context) {
 | 
				
			||||||
		if organization.IsErrOrgNotExist(err) {
 | 
							if organization.IsErrOrgNotExist(err) {
 | 
				
			||||||
			redirectUserID, err := user_model.LookupUserRedirect(orgName)
 | 
								redirectUserID, err := user_model.LookupUserRedirect(orgName)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				RedirectToUser(ctx, orgName, redirectUserID)
 | 
									RedirectToUser(ctx.Base, orgName, redirectUserID)
 | 
				
			||||||
			} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
								} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
				
			||||||
				ctx.NotFound("GetUserByName", err)
 | 
									ctx.NotFound("GetUserByName", err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,6 @@
 | 
				
			||||||
package context
 | 
					package context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	gocontext "context"
 | 
					 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +15,6 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/structs"
 | 
						"code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/templates"
 | 
						"code.gitea.io/gitea/modules/templates"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Package contains owner, access mode and optional the package descriptor
 | 
					// Package contains owner, access mode and optional the package descriptor
 | 
				
			||||||
| 
						 | 
					@ -26,10 +24,16 @@ type Package struct {
 | 
				
			||||||
	Descriptor *packages_model.PackageDescriptor
 | 
						Descriptor *packages_model.PackageDescriptor
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type packageAssignmentCtx struct {
 | 
				
			||||||
 | 
						*Base
 | 
				
			||||||
 | 
						Doer        *user_model.User
 | 
				
			||||||
 | 
						ContextUser *user_model.User
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PackageAssignment returns a middleware to handle Context.Package assignment
 | 
					// PackageAssignment returns a middleware to handle Context.Package assignment
 | 
				
			||||||
func PackageAssignment() func(ctx *Context) {
 | 
					func PackageAssignment() func(ctx *Context) {
 | 
				
			||||||
	return func(ctx *Context) {
 | 
						return func(ctx *Context) {
 | 
				
			||||||
		packageAssignment(ctx, func(status int, title string, obj interface{}) {
 | 
							errorFn := func(status int, title string, obj interface{}) {
 | 
				
			||||||
			err, ok := obj.(error)
 | 
								err, ok := obj.(error)
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				err = fmt.Errorf("%s", obj)
 | 
									err = fmt.Errorf("%s", obj)
 | 
				
			||||||
| 
						 | 
					@ -39,68 +43,72 @@ func PackageAssignment() func(ctx *Context) {
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ctx.ServerError(title, err)
 | 
									ctx.ServerError(title, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							}
 | 
				
			||||||
 | 
							paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
 | 
				
			||||||
 | 
							ctx.Package = packageAssignment(paCtx, errorFn)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PackageAssignmentAPI returns a middleware to handle Context.Package assignment
 | 
					// PackageAssignmentAPI returns a middleware to handle Context.Package assignment
 | 
				
			||||||
func PackageAssignmentAPI() func(ctx *APIContext) {
 | 
					func PackageAssignmentAPI() func(ctx *APIContext) {
 | 
				
			||||||
	return func(ctx *APIContext) {
 | 
						return func(ctx *APIContext) {
 | 
				
			||||||
		packageAssignment(ctx.Context, ctx.Error)
 | 
							paCtx := &packageAssignmentCtx{Base: ctx.Base, Doer: ctx.Doer, ContextUser: ctx.ContextUser}
 | 
				
			||||||
 | 
							ctx.Package = packageAssignment(paCtx, ctx.Error)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func packageAssignment(ctx *Context, errCb func(int, string, interface{})) {
 | 
					func packageAssignment(ctx *packageAssignmentCtx, errCb func(int, string, interface{})) *Package {
 | 
				
			||||||
	ctx.Package = &Package{
 | 
						pkg := &Package{
 | 
				
			||||||
		Owner: ctx.ContextUser,
 | 
							Owner: ctx.ContextUser,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	ctx.Package.AccessMode, err = determineAccessMode(ctx)
 | 
						pkg.AccessMode, err = determineAccessMode(ctx.Base, pkg, ctx.Doer)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		errCb(http.StatusInternalServerError, "determineAccessMode", err)
 | 
							errCb(http.StatusInternalServerError, "determineAccessMode", err)
 | 
				
			||||||
		return
 | 
							return pkg
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	packageType := ctx.Params("type")
 | 
						packageType := ctx.Params("type")
 | 
				
			||||||
	name := ctx.Params("name")
 | 
						name := ctx.Params("name")
 | 
				
			||||||
	version := ctx.Params("version")
 | 
						version := ctx.Params("version")
 | 
				
			||||||
	if packageType != "" && name != "" && version != "" {
 | 
						if packageType != "" && name != "" && version != "" {
 | 
				
			||||||
		pv, err := packages_model.GetVersionByNameAndVersion(ctx, ctx.Package.Owner.ID, packages_model.Type(packageType), name, version)
 | 
							pv, err := packages_model.GetVersionByNameAndVersion(ctx, pkg.Owner.ID, packages_model.Type(packageType), name, version)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if err == packages_model.ErrPackageNotExist {
 | 
								if err == packages_model.ErrPackageNotExist {
 | 
				
			||||||
				errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
 | 
									errCb(http.StatusNotFound, "GetVersionByNameAndVersion", err)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
 | 
									errCb(http.StatusInternalServerError, "GetVersionByNameAndVersion", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return
 | 
								return pkg
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		ctx.Package.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
 | 
							pkg.Descriptor, err = packages_model.GetPackageDescriptor(ctx, pv)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
 | 
								errCb(http.StatusInternalServerError, "GetPackageDescriptor", err)
 | 
				
			||||||
			return
 | 
								return pkg
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pkg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
 | 
					func determineAccessMode(ctx *Base, pkg *Package, doer *user_model.User) (perm.AccessMode, error) {
 | 
				
			||||||
	if setting.Service.RequireSignInView && ctx.Doer == nil {
 | 
						if setting.Service.RequireSignInView && doer == nil {
 | 
				
			||||||
		return perm.AccessModeNone, nil
 | 
							return perm.AccessModeNone, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.Doer != nil && !ctx.Doer.IsGhost() && (!ctx.Doer.IsActive || ctx.Doer.ProhibitLogin) {
 | 
						if doer != nil && !doer.IsGhost() && (!doer.IsActive || doer.ProhibitLogin) {
 | 
				
			||||||
		return perm.AccessModeNone, nil
 | 
							return perm.AccessModeNone, nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// TODO: ActionUser permission check
 | 
						// TODO: ActionUser permission check
 | 
				
			||||||
	accessMode := perm.AccessModeNone
 | 
						accessMode := perm.AccessModeNone
 | 
				
			||||||
	if ctx.Package.Owner.IsOrganization() {
 | 
						if pkg.Owner.IsOrganization() {
 | 
				
			||||||
		org := organization.OrgFromUser(ctx.Package.Owner)
 | 
							org := organization.OrgFromUser(pkg.Owner)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ctx.Doer != nil && !ctx.Doer.IsGhost() {
 | 
							if doer != nil && !doer.IsGhost() {
 | 
				
			||||||
			// 1. If user is logged in, check all team packages permissions
 | 
								// 1. If user is logged in, check all team packages permissions
 | 
				
			||||||
			teams, err := organization.GetUserOrgTeams(ctx, org.ID, ctx.Doer.ID)
 | 
								teams, err := organization.GetUserOrgTeams(ctx, org.ID, doer.ID)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return accessMode, err
 | 
									return accessMode, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					@ -110,19 +118,19 @@ func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
 | 
				
			||||||
					accessMode = perm
 | 
										accessMode = perm
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if organization.HasOrgOrUserVisible(ctx, ctx.Package.Owner, ctx.Doer) {
 | 
							} else if organization.HasOrgOrUserVisible(ctx, pkg.Owner, doer) {
 | 
				
			||||||
			// 2. If user is non-login, check if org is visible to non-login user
 | 
								// 2. If user is non-login, check if org is visible to non-login user
 | 
				
			||||||
			accessMode = perm.AccessModeRead
 | 
								accessMode = perm.AccessModeRead
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if ctx.Doer != nil && !ctx.Doer.IsGhost() {
 | 
							if doer != nil && !doer.IsGhost() {
 | 
				
			||||||
			// 1. Check if user is package owner
 | 
								// 1. Check if user is package owner
 | 
				
			||||||
			if ctx.Doer.ID == ctx.Package.Owner.ID {
 | 
								if doer.ID == pkg.Owner.ID {
 | 
				
			||||||
				accessMode = perm.AccessModeOwner
 | 
									accessMode = perm.AccessModeOwner
 | 
				
			||||||
			} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic || ctx.Package.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
 | 
								} else if pkg.Owner.Visibility == structs.VisibleTypePublic || pkg.Owner.Visibility == structs.VisibleTypeLimited { // 2. Check if package owner is public or limited
 | 
				
			||||||
				accessMode = perm.AccessModeRead
 | 
									accessMode = perm.AccessModeRead
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		} else if ctx.Package.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
 | 
							} else if pkg.Owner.Visibility == structs.VisibleTypePublic { // 3. Check if package owner is public
 | 
				
			||||||
			accessMode = perm.AccessModeRead
 | 
								accessMode = perm.AccessModeRead
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -131,19 +139,18 @@ func determineAccessMode(ctx *Context) (perm.AccessMode, error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PackageContexter initializes a package context for a request.
 | 
					// PackageContexter initializes a package context for a request.
 | 
				
			||||||
func PackageContexter(ctx gocontext.Context) func(next http.Handler) http.Handler {
 | 
					func PackageContexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
	rnd := templates.HTMLRenderer()
 | 
						renderer := templates.HTMLRenderer()
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(next http.Handler) http.Handler {
 | 
				
			||||||
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
			ctx := Context{
 | 
								base, baseCleanUp := NewBaseContext(resp, req)
 | 
				
			||||||
				Resp:   NewResponse(resp),
 | 
								ctx := &Context{
 | 
				
			||||||
				Data:   middleware.GetContextData(req.Context()),
 | 
									Base:   base,
 | 
				
			||||||
				Render: rnd,
 | 
									Render: renderer, // it is still needed when rendering 500 page in a package handler
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			defer ctx.Close()
 | 
								defer baseCleanUp()
 | 
				
			||||||
 | 
					 | 
				
			||||||
			ctx.Req = WithContext(req, &ctx)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								ctx.Base.AppendContextValue(contextKey, ctx)
 | 
				
			||||||
			next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
								next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,13 +11,14 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/graceful"
 | 
						"code.gitea.io/gitea/modules/graceful"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/process"
 | 
						"code.gitea.io/gitea/modules/process"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PrivateContext represents a context for private routes
 | 
					// PrivateContext represents a context for private routes
 | 
				
			||||||
type PrivateContext struct {
 | 
					type PrivateContext struct {
 | 
				
			||||||
	*Context
 | 
						*Base
 | 
				
			||||||
	Override context.Context
 | 
						Override context.Context
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Repo *Repository
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Deadline is part of the interface for context.Context and we pass this to the request context
 | 
					// Deadline is part of the interface for context.Context and we pass this to the request context
 | 
				
			||||||
| 
						 | 
					@ -25,7 +26,7 @@ func (ctx *PrivateContext) Deadline() (deadline time.Time, ok bool) {
 | 
				
			||||||
	if ctx.Override != nil {
 | 
						if ctx.Override != nil {
 | 
				
			||||||
		return ctx.Override.Deadline()
 | 
							return ctx.Override.Deadline()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ctx.Req.Context().Deadline()
 | 
						return ctx.Base.Deadline()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Done is part of the interface for context.Context and we pass this to the request context
 | 
					// Done is part of the interface for context.Context and we pass this to the request context
 | 
				
			||||||
| 
						 | 
					@ -33,7 +34,7 @@ func (ctx *PrivateContext) Done() <-chan struct{} {
 | 
				
			||||||
	if ctx.Override != nil {
 | 
						if ctx.Override != nil {
 | 
				
			||||||
		return ctx.Override.Done()
 | 
							return ctx.Override.Done()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ctx.Req.Context().Done()
 | 
						return ctx.Base.Done()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Err is part of the interface for context.Context and we pass this to the request context
 | 
					// Err is part of the interface for context.Context and we pass this to the request context
 | 
				
			||||||
| 
						 | 
					@ -41,16 +42,11 @@ func (ctx *PrivateContext) Err() error {
 | 
				
			||||||
	if ctx.Override != nil {
 | 
						if ctx.Override != nil {
 | 
				
			||||||
		return ctx.Override.Err()
 | 
							return ctx.Override.Err()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ctx.Req.Context().Err()
 | 
						return ctx.Base.Err()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var privateContextKey interface{} = "default_private_context"
 | 
					var privateContextKey interface{} = "default_private_context"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// WithPrivateContext set up private context in request
 | 
					 | 
				
			||||||
func WithPrivateContext(req *http.Request, ctx *PrivateContext) *http.Request {
 | 
					 | 
				
			||||||
	return req.WithContext(context.WithValue(req.Context(), privateContextKey, ctx))
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// GetPrivateContext returns a context for Private routes
 | 
					// GetPrivateContext returns a context for Private routes
 | 
				
			||||||
func GetPrivateContext(req *http.Request) *PrivateContext {
 | 
					func GetPrivateContext(req *http.Request) *PrivateContext {
 | 
				
			||||||
	return req.Context().Value(privateContextKey).(*PrivateContext)
 | 
						return req.Context().Value(privateContextKey).(*PrivateContext)
 | 
				
			||||||
| 
						 | 
					@ -60,16 +56,11 @@ func GetPrivateContext(req *http.Request) *PrivateContext {
 | 
				
			||||||
func PrivateContexter() func(http.Handler) http.Handler {
 | 
					func PrivateContexter() func(http.Handler) http.Handler {
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(next http.Handler) http.Handler {
 | 
				
			||||||
		return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
			ctx := &PrivateContext{
 | 
								base, baseCleanUp := NewBaseContext(w, req)
 | 
				
			||||||
				Context: &Context{
 | 
								ctx := &PrivateContext{Base: base}
 | 
				
			||||||
					Resp: NewResponse(w),
 | 
								defer baseCleanUp()
 | 
				
			||||||
					Data: middleware.GetContextData(req.Context()),
 | 
								ctx.Base.AppendContextValue(privateContextKey, ctx)
 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			defer ctx.Close()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Req = WithPrivateContext(req, ctx)
 | 
					 | 
				
			||||||
			ctx.Data["Context"] = ctx
 | 
					 | 
				
			||||||
			next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
								next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -331,13 +331,14 @@ func EarlyResponseForGoGetMeta(ctx *Context) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RedirectToRepo redirect to a differently-named repository
 | 
					// RedirectToRepo redirect to a differently-named repository
 | 
				
			||||||
func RedirectToRepo(ctx *Context, redirectRepoID int64) {
 | 
					func RedirectToRepo(ctx *Base, redirectRepoID int64) {
 | 
				
			||||||
	ownerName := ctx.Params(":username")
 | 
						ownerName := ctx.Params(":username")
 | 
				
			||||||
	previousRepoName := ctx.Params(":reponame")
 | 
						previousRepoName := ctx.Params(":reponame")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
 | 
						repo, err := repo_model.GetRepositoryByID(ctx, redirectRepoID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetRepositoryByID", err)
 | 
							log.Error("GetRepositoryByID: %v", err)
 | 
				
			||||||
 | 
							ctx.Error(http.StatusInternalServerError, "GetRepositoryByID")
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -456,7 +457,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
 | 
									if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
 | 
				
			||||||
					RedirectToUser(ctx, userName, redirectUserID)
 | 
										RedirectToUser(ctx.Base, userName, redirectUserID)
 | 
				
			||||||
				} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
									} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
				
			||||||
					ctx.NotFound("GetUserByName", nil)
 | 
										ctx.NotFound("GetUserByName", nil)
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
| 
						 | 
					@ -498,7 +499,7 @@ func RepoAssignment(ctx *Context) (cancel context.CancelFunc) {
 | 
				
			||||||
		if repo_model.IsErrRepoNotExist(err) {
 | 
							if repo_model.IsErrRepoNotExist(err) {
 | 
				
			||||||
			redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
 | 
								redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
 | 
				
			||||||
			if err == nil {
 | 
								if err == nil {
 | 
				
			||||||
				RedirectToRepo(ctx, redirectRepoID)
 | 
									RedirectToRepo(ctx.Base, redirectRepoID)
 | 
				
			||||||
			} else if repo_model.IsErrRedirectNotExist(err) {
 | 
								} else if repo_model.IsErrRedirectNotExist(err) {
 | 
				
			||||||
				if ctx.FormString("go-get") == "1" {
 | 
									if ctx.FormString("go-get") == "1" {
 | 
				
			||||||
					EarlyResponseForGoGetMeta(ctx)
 | 
										EarlyResponseForGoGetMeta(ctx)
 | 
				
			||||||
| 
						 | 
					@ -781,46 +782,46 @@ func (rt RepoRefType) RefTypeIncludesTags() bool {
 | 
				
			||||||
	return false
 | 
						return false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getRefNameFromPath(ctx *Context, path string, isExist func(string) bool) string {
 | 
					func getRefNameFromPath(ctx *Base, repo *Repository, path string, isExist func(string) bool) string {
 | 
				
			||||||
	refName := ""
 | 
						refName := ""
 | 
				
			||||||
	parts := strings.Split(path, "/")
 | 
						parts := strings.Split(path, "/")
 | 
				
			||||||
	for i, part := range parts {
 | 
						for i, part := range parts {
 | 
				
			||||||
		refName = strings.TrimPrefix(refName+"/"+part, "/")
 | 
							refName = strings.TrimPrefix(refName+"/"+part, "/")
 | 
				
			||||||
		if isExist(refName) {
 | 
							if isExist(refName) {
 | 
				
			||||||
			ctx.Repo.TreePath = strings.Join(parts[i+1:], "/")
 | 
								repo.TreePath = strings.Join(parts[i+1:], "/")
 | 
				
			||||||
			return refName
 | 
								return refName
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return ""
 | 
						return ""
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getRefName(ctx *Context, pathType RepoRefType) string {
 | 
					func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string {
 | 
				
			||||||
	path := ctx.Params("*")
 | 
						path := ctx.Params("*")
 | 
				
			||||||
	switch pathType {
 | 
						switch pathType {
 | 
				
			||||||
	case RepoRefLegacy, RepoRefAny:
 | 
						case RepoRefLegacy, RepoRefAny:
 | 
				
			||||||
		if refName := getRefName(ctx, RepoRefBranch); len(refName) > 0 {
 | 
							if refName := getRefName(ctx, repo, RepoRefBranch); len(refName) > 0 {
 | 
				
			||||||
			return refName
 | 
								return refName
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if refName := getRefName(ctx, RepoRefTag); len(refName) > 0 {
 | 
							if refName := getRefName(ctx, repo, RepoRefTag); len(refName) > 0 {
 | 
				
			||||||
			return refName
 | 
								return refName
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		// For legacy and API support only full commit sha
 | 
							// For legacy and API support only full commit sha
 | 
				
			||||||
		parts := strings.Split(path, "/")
 | 
							parts := strings.Split(path, "/")
 | 
				
			||||||
		if len(parts) > 0 && len(parts[0]) == git.SHAFullLength {
 | 
							if len(parts) > 0 && len(parts[0]) == git.SHAFullLength {
 | 
				
			||||||
			ctx.Repo.TreePath = strings.Join(parts[1:], "/")
 | 
								repo.TreePath = strings.Join(parts[1:], "/")
 | 
				
			||||||
			return parts[0]
 | 
								return parts[0]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if refName := getRefName(ctx, RepoRefBlob); len(refName) > 0 {
 | 
							if refName := getRefName(ctx, repo, RepoRefBlob); len(refName) > 0 {
 | 
				
			||||||
			return refName
 | 
								return refName
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.Repo.TreePath = path
 | 
							repo.TreePath = path
 | 
				
			||||||
		return ctx.Repo.Repository.DefaultBranch
 | 
							return repo.Repository.DefaultBranch
 | 
				
			||||||
	case RepoRefBranch:
 | 
						case RepoRefBranch:
 | 
				
			||||||
		ref := getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsBranchExist)
 | 
							ref := getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsBranchExist)
 | 
				
			||||||
		if len(ref) == 0 {
 | 
							if len(ref) == 0 {
 | 
				
			||||||
			// maybe it's a renamed branch
 | 
								// maybe it's a renamed branch
 | 
				
			||||||
			return getRefNameFromPath(ctx, path, func(s string) bool {
 | 
								return getRefNameFromPath(ctx, repo, path, func(s string) bool {
 | 
				
			||||||
				b, exist, err := git_model.FindRenamedBranch(ctx, ctx.Repo.Repository.ID, s)
 | 
									b, exist, err := git_model.FindRenamedBranch(ctx, repo.Repository.ID, s)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					log.Error("FindRenamedBranch", err)
 | 
										log.Error("FindRenamedBranch", err)
 | 
				
			||||||
					return false
 | 
										return false
 | 
				
			||||||
| 
						 | 
					@ -839,15 +840,15 @@ func getRefName(ctx *Context, pathType RepoRefType) string {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return ref
 | 
							return ref
 | 
				
			||||||
	case RepoRefTag:
 | 
						case RepoRefTag:
 | 
				
			||||||
		return getRefNameFromPath(ctx, path, ctx.Repo.GitRepo.IsTagExist)
 | 
							return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist)
 | 
				
			||||||
	case RepoRefCommit:
 | 
						case RepoRefCommit:
 | 
				
			||||||
		parts := strings.Split(path, "/")
 | 
							parts := strings.Split(path, "/")
 | 
				
			||||||
		if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength {
 | 
							if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= git.SHAFullLength {
 | 
				
			||||||
			ctx.Repo.TreePath = strings.Join(parts[1:], "/")
 | 
								repo.TreePath = strings.Join(parts[1:], "/")
 | 
				
			||||||
			return parts[0]
 | 
								return parts[0]
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case RepoRefBlob:
 | 
						case RepoRefBlob:
 | 
				
			||||||
		_, err := ctx.Repo.GitRepo.GetBlob(path)
 | 
							_, err := repo.GitRepo.GetBlob(path)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return ""
 | 
								return ""
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -922,7 +923,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Repo.IsViewBranch = true
 | 
								ctx.Repo.IsViewBranch = true
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			refName = getRefName(ctx, refType)
 | 
								refName = getRefName(ctx.Base, ctx.Repo, refType)
 | 
				
			||||||
			ctx.Repo.RefName = refName
 | 
								ctx.Repo.RefName = refName
 | 
				
			||||||
			isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
 | 
								isRenamedBranch, has := ctx.Data["IsRenamedBranch"].(bool)
 | 
				
			||||||
			if isRenamedBranch && has {
 | 
								if isRenamedBranch && has {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,10 +10,9 @@ import (
 | 
				
			||||||
// ResponseWriter represents a response writer for HTTP
 | 
					// ResponseWriter represents a response writer for HTTP
 | 
				
			||||||
type ResponseWriter interface {
 | 
					type ResponseWriter interface {
 | 
				
			||||||
	http.ResponseWriter
 | 
						http.ResponseWriter
 | 
				
			||||||
	Flush()
 | 
						http.Flusher
 | 
				
			||||||
	Status() int
 | 
						Status() int
 | 
				
			||||||
	Before(func(ResponseWriter))
 | 
						Before(func(ResponseWriter))
 | 
				
			||||||
	Size() int
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var _ ResponseWriter = &Response{}
 | 
					var _ ResponseWriter = &Response{}
 | 
				
			||||||
| 
						 | 
					@ -27,11 +26,6 @@ type Response struct {
 | 
				
			||||||
	beforeExecuted bool
 | 
						beforeExecuted bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Size return written size
 | 
					 | 
				
			||||||
func (r *Response) Size() int {
 | 
					 | 
				
			||||||
	return r.written
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Write writes bytes to HTTP endpoint
 | 
					// Write writes bytes to HTTP endpoint
 | 
				
			||||||
func (r *Response) Write(bs []byte) (int, error) {
 | 
					func (r *Response) Write(bs []byte) (int, error) {
 | 
				
			||||||
	if !r.beforeExecuted {
 | 
						if !r.beforeExecuted {
 | 
				
			||||||
| 
						 | 
					@ -65,7 +59,7 @@ func (r *Response) WriteHeader(statusCode int) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Flush flush cached data
 | 
					// Flush flushes cached data
 | 
				
			||||||
func (r *Response) Flush() {
 | 
					func (r *Response) Flush() {
 | 
				
			||||||
	if f, ok := r.ResponseWriter.(http.Flusher); ok {
 | 
						if f, ok := r.ResponseWriter.(http.Flusher); ok {
 | 
				
			||||||
		f.Flush()
 | 
							f.Flush()
 | 
				
			||||||
| 
						 | 
					@ -83,8 +77,7 @@ func (r *Response) Before(f func(ResponseWriter)) {
 | 
				
			||||||
	r.befores = append(r.befores, f)
 | 
						r.befores = append(r.befores, f)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// NewResponse creates a response
 | 
					func WrapResponseWriter(resp http.ResponseWriter) *Response {
 | 
				
			||||||
func NewResponse(resp http.ResponseWriter) *Response {
 | 
					 | 
				
			||||||
	if v, ok := resp.(*Response); ok {
 | 
						if v, ok := resp.(*Response); ok {
 | 
				
			||||||
		return v
 | 
							return v
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,7 +10,7 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 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
 | 
				
			||||||
func GetQueryBeforeSince(ctx *Context) (before, since int64, err error) {
 | 
					func GetQueryBeforeSince(ctx *Base) (before, since int64, err error) {
 | 
				
			||||||
	qCreatedBefore, err := prepareQueryArg(ctx, "before")
 | 
						qCreatedBefore, err := prepareQueryArg(ctx, "before")
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, 0, err
 | 
							return 0, 0, err
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ func parseTime(value string) (int64, error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// prepareQueryArg unescape and trim a query arg
 | 
					// prepareQueryArg unescape and trim a query arg
 | 
				
			||||||
func prepareQueryArg(ctx *Context, name string) (value string, err error) {
 | 
					func prepareQueryArg(ctx *Base, name string) (value string, err error) {
 | 
				
			||||||
	value, err = url.PathUnescape(ctx.FormString(name))
 | 
						value, err = url.PathUnescape(ctx.FormString(name))
 | 
				
			||||||
	value = strings.TrimSpace(value)
 | 
						value = strings.TrimSpace(value)
 | 
				
			||||||
	return value, err
 | 
						return value, err
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,7 @@
 | 
				
			||||||
package test
 | 
					package test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	scontext "context"
 | 
						gocontext "context"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
	"net/http/httptest"
 | 
						"net/http/httptest"
 | 
				
			||||||
| 
						 | 
					@ -28,18 +28,7 @@ import (
 | 
				
			||||||
// MockContext mock context for unit tests
 | 
					// MockContext mock context for unit tests
 | 
				
			||||||
// TODO: move this function to other packages, because it depends on "models" package
 | 
					// TODO: move this function to other packages, because it depends on "models" package
 | 
				
			||||||
func MockContext(t *testing.T, path string) *context.Context {
 | 
					func MockContext(t *testing.T, path string) *context.Context {
 | 
				
			||||||
	resp := &mockResponseWriter{}
 | 
						resp := httptest.NewRecorder()
 | 
				
			||||||
	ctx := context.Context{
 | 
					 | 
				
			||||||
		Render: &mockRender{},
 | 
					 | 
				
			||||||
		Data:   make(middleware.ContextData),
 | 
					 | 
				
			||||||
		Flash: &middleware.Flash{
 | 
					 | 
				
			||||||
			Values: make(url.Values),
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		Resp:   context.NewResponse(resp),
 | 
					 | 
				
			||||||
		Locale: &translation.MockLocale{},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer ctx.Close()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	requestURL, err := url.Parse(path)
 | 
						requestURL, err := url.Parse(path)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	req := &http.Request{
 | 
						req := &http.Request{
 | 
				
			||||||
| 
						 | 
					@ -47,41 +36,105 @@ func MockContext(t *testing.T, path string) *context.Context {
 | 
				
			||||||
		Form: url.Values{},
 | 
							Form: url.Values{},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						base, baseCleanUp := context.NewBaseContext(resp, req)
 | 
				
			||||||
 | 
						base.Data = middleware.ContextData{}
 | 
				
			||||||
 | 
						base.Locale = &translation.MockLocale{}
 | 
				
			||||||
 | 
						ctx := &context.Context{
 | 
				
			||||||
 | 
							Base:   base,
 | 
				
			||||||
 | 
							Render: &mockRender{},
 | 
				
			||||||
 | 
							Flash:  &middleware.Flash{Values: url.Values{}},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	chiCtx := chi.NewRouteContext()
 | 
						chiCtx := chi.NewRouteContext()
 | 
				
			||||||
	req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx))
 | 
						ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
 | 
				
			||||||
	ctx.Req = context.WithContext(req, &ctx)
 | 
						return ctx
 | 
				
			||||||
	return &ctx
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// MockAPIContext mock context for unit tests
 | 
				
			||||||
 | 
					// TODO: move this function to other packages, because it depends on "models" package
 | 
				
			||||||
 | 
					func MockAPIContext(t *testing.T, path string) *context.APIContext {
 | 
				
			||||||
 | 
						resp := httptest.NewRecorder()
 | 
				
			||||||
 | 
						requestURL, err := url.Parse(path)
 | 
				
			||||||
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						req := &http.Request{
 | 
				
			||||||
 | 
							URL:  requestURL,
 | 
				
			||||||
 | 
							Form: url.Values{},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						base, baseCleanUp := context.NewBaseContext(resp, req)
 | 
				
			||||||
 | 
						base.Data = middleware.ContextData{}
 | 
				
			||||||
 | 
						base.Locale = &translation.MockLocale{}
 | 
				
			||||||
 | 
						ctx := &context.APIContext{Base: base}
 | 
				
			||||||
 | 
						_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chiCtx := chi.NewRouteContext()
 | 
				
			||||||
 | 
						ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
 | 
				
			||||||
 | 
						return ctx
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadRepo load a repo into a test context.
 | 
					// LoadRepo load a repo into a test context.
 | 
				
			||||||
func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
 | 
					func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
 | 
				
			||||||
	ctx.Repo = &context.Repository{}
 | 
						var doer *user_model.User
 | 
				
			||||||
	ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
 | 
						repo := &context.Repository{}
 | 
				
			||||||
 | 
						switch ctx := ctx.(type) {
 | 
				
			||||||
 | 
						case *context.Context:
 | 
				
			||||||
 | 
							ctx.Repo = repo
 | 
				
			||||||
 | 
							doer = ctx.Doer
 | 
				
			||||||
 | 
						case *context.APIContext:
 | 
				
			||||||
 | 
							ctx.Repo = repo
 | 
				
			||||||
 | 
							doer = ctx.Doer
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							assert.Fail(t, "context is not *context.Context or *context.APIContext")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	ctx.Repo.Owner, err = user_model.GetUserByID(ctx, ctx.Repo.Repository.OwnerID)
 | 
						repo.Owner, err = user_model.GetUserByID(ctx, repo.Repository.OwnerID)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
 | 
						repo.RepoLink = repo.Repository.Link()
 | 
				
			||||||
	ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, ctx.Doer)
 | 
						repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo.Repository, doer)
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadRepoCommit loads a repo's commit into a test context.
 | 
					// LoadRepoCommit loads a repo's commit into a test context.
 | 
				
			||||||
func LoadRepoCommit(t *testing.T, ctx *context.Context) {
 | 
					func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
 | 
				
			||||||
	gitRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
 | 
						var repo *context.Repository
 | 
				
			||||||
 | 
						switch ctx := ctx.(type) {
 | 
				
			||||||
 | 
						case *context.Context:
 | 
				
			||||||
 | 
							repo = ctx.Repo
 | 
				
			||||||
 | 
						case *context.APIContext:
 | 
				
			||||||
 | 
							repo = ctx.Repo
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							assert.Fail(t, "context is not *context.Context or *context.APIContext")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gitRepo, err := git.OpenRepository(ctx, repo.Repository.RepoPath())
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	defer gitRepo.Close()
 | 
						defer gitRepo.Close()
 | 
				
			||||||
	branch, err := gitRepo.GetHEADBranch()
 | 
						branch, err := gitRepo.GetHEADBranch()
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	assert.NotNil(t, branch)
 | 
						assert.NotNil(t, branch)
 | 
				
			||||||
	if branch != nil {
 | 
						if branch != nil {
 | 
				
			||||||
		ctx.Repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
 | 
							repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
 | 
				
			||||||
		assert.NoError(t, err)
 | 
							assert.NoError(t, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadUser load a user into a test context.
 | 
					// LoadUser load a user into a test context.
 | 
				
			||||||
func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
 | 
					func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
 | 
				
			||||||
	ctx.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
 | 
						doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
 | 
				
			||||||
 | 
						switch ctx := ctx.(type) {
 | 
				
			||||||
 | 
						case *context.Context:
 | 
				
			||||||
 | 
							ctx.Doer = doer
 | 
				
			||||||
 | 
						case *context.APIContext:
 | 
				
			||||||
 | 
							ctx.Doer = doer
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							assert.Fail(t, "context is not *context.Context or *context.APIContext")
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
 | 
					// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
 | 
				
			||||||
| 
						 | 
					@ -93,32 +146,6 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) {
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type mockResponseWriter struct {
 | 
					 | 
				
			||||||
	httptest.ResponseRecorder
 | 
					 | 
				
			||||||
	size int
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (rw *mockResponseWriter) Write(b []byte) (int, error) {
 | 
					 | 
				
			||||||
	rw.size += len(b)
 | 
					 | 
				
			||||||
	return rw.ResponseRecorder.Write(b)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (rw *mockResponseWriter) Status() int {
 | 
					 | 
				
			||||||
	return rw.ResponseRecorder.Code
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (rw *mockResponseWriter) Written() bool {
 | 
					 | 
				
			||||||
	return rw.ResponseRecorder.Code > 0
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (rw *mockResponseWriter) Size() int {
 | 
					 | 
				
			||||||
	return rw.size
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error {
 | 
					 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
type mockRender struct{}
 | 
					type mockRender struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) {
 | 
					func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -38,10 +38,12 @@ type LangType struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	lock          *sync.RWMutex
 | 
						lock *sync.RWMutex
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						allLangs   []*LangType
 | 
				
			||||||
 | 
						allLangMap map[string]*LangType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	matcher       language.Matcher
 | 
						matcher       language.Matcher
 | 
				
			||||||
	allLangs      []*LangType
 | 
					 | 
				
			||||||
	allLangMap    map[string]*LangType
 | 
					 | 
				
			||||||
	supportedTags []language.Tag
 | 
						supportedTags []language.Tag
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -251,3 +253,9 @@ func (l *locale) PrettyNumber(v any) string {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return l.msgPrinter.Sprintf("%v", number.Decimal(v))
 | 
						return l.msgPrinter.Sprintf("%v", number.Decimal(v))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						// prepare a default matcher, especially for tests
 | 
				
			||||||
 | 
						supportedTags = []language.Tag{language.English}
 | 
				
			||||||
 | 
						matcher = language.NewMatcher(supportedTags)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,6 +10,7 @@ import (
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/routing"
 | 
						"code.gitea.io/gitea/modules/web/routing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +26,10 @@ var argTypeProvider = map[reflect.Type]func(req *http.Request) ResponseStatusPro
 | 
				
			||||||
	reflect.TypeOf(&context.PrivateContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetPrivateContext(req) },
 | 
						reflect.TypeOf(&context.PrivateContext{}): func(req *http.Request) ResponseStatusProvider { return context.GetPrivateContext(req) },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RegisterHandleTypeProvider[T any](fn func(req *http.Request) ResponseStatusProvider) {
 | 
				
			||||||
 | 
						argTypeProvider[reflect.TypeOf((*T)(nil)).Elem()] = fn
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written
 | 
					// responseWriter is a wrapper of http.ResponseWriter, to check whether the response has been written
 | 
				
			||||||
type responseWriter struct {
 | 
					type responseWriter struct {
 | 
				
			||||||
	respWriter http.ResponseWriter
 | 
						respWriter http.ResponseWriter
 | 
				
			||||||
| 
						 | 
					@ -78,7 +83,13 @@ func preCheckHandler(fn reflect.Value, argsIn []reflect.Value) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value) []reflect.Value {
 | 
					func prepareHandleArgsIn(resp http.ResponseWriter, req *http.Request, fn reflect.Value, fnInfo *routing.FuncInfo) []reflect.Value {
 | 
				
			||||||
 | 
						defer func() {
 | 
				
			||||||
 | 
							if err := recover(); err != nil {
 | 
				
			||||||
 | 
								log.Error("unable to prepare handler arguments for %s: %v", fnInfo.String(), err)
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}()
 | 
				
			||||||
	isPreCheck := req == nil
 | 
						isPreCheck := req == nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	argsIn := make([]reflect.Value, fn.Type().NumIn())
 | 
						argsIn := make([]reflect.Value, fn.Type().NumIn())
 | 
				
			||||||
| 
						 | 
					@ -155,7 +166,7 @@ func toHandlerProvider(handler any) func(next http.Handler) http.Handler {
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// prepare the arguments for the handler and do pre-check
 | 
								// prepare the arguments for the handler and do pre-check
 | 
				
			||||||
			argsIn := prepareHandleArgsIn(resp, req, fn)
 | 
								argsIn := prepareHandleArgsIn(resp, req, fn, funcInfo)
 | 
				
			||||||
			if req == nil {
 | 
								if req == nil {
 | 
				
			||||||
				preCheckHandler(fn, argsIn)
 | 
									preCheckHandler(fn, argsIn)
 | 
				
			||||||
				return // it's doing pre-check, just return
 | 
									return // it's doing pre-check, just return
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
package actions
 | 
					package actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Github Actions Artifacts API Simple Description
 | 
					// GitHub Actions Artifacts API Simple Description
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
// 1. Upload artifact
 | 
					// 1. Upload artifact
 | 
				
			||||||
// 1.1. Post upload url
 | 
					// 1.1. Post upload url
 | 
				
			||||||
| 
						 | 
					@ -63,7 +63,6 @@ package actions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"compress/gzip"
 | 
						"compress/gzip"
 | 
				
			||||||
	gocontext "context"
 | 
					 | 
				
			||||||
	"crypto/md5"
 | 
						"crypto/md5"
 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
| 
						 | 
					@ -92,9 +91,25 @@ const (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"
 | 
					const artifactRouteBase = "/_apis/pipelines/workflows/{run_id}/artifacts"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ArtifactsRoutes(goctx gocontext.Context, prefix string) *web.Route {
 | 
					type artifactContextKeyType struct{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var artifactContextKey = artifactContextKeyType{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ArtifactContext struct {
 | 
				
			||||||
 | 
						*context.Base
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ActionTask *actions.ActionTask
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						web.RegisterHandleTypeProvider[*ArtifactContext](func(req *http.Request) web.ResponseStatusProvider {
 | 
				
			||||||
 | 
							return req.Context().Value(artifactContextKey).(*ArtifactContext)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ArtifactsRoutes(prefix string) *web.Route {
 | 
				
			||||||
	m := web.NewRoute()
 | 
						m := web.NewRoute()
 | 
				
			||||||
	m.Use(withContexter(goctx))
 | 
						m.Use(ArtifactContexter())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r := artifactRoutes{
 | 
						r := artifactRoutes{
 | 
				
			||||||
		prefix: prefix,
 | 
							prefix: prefix,
 | 
				
			||||||
| 
						 | 
					@ -115,15 +130,14 @@ func ArtifactsRoutes(goctx gocontext.Context, prefix string) *web.Route {
 | 
				
			||||||
	return m
 | 
						return m
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// withContexter initializes a package context for a request.
 | 
					func ArtifactContexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler {
 | 
					 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(next http.Handler) http.Handler {
 | 
				
			||||||
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
			ctx := context.Context{
 | 
								base, baseCleanUp := context.NewBaseContext(resp, req)
 | 
				
			||||||
				Resp: context.NewResponse(resp),
 | 
								defer baseCleanUp()
 | 
				
			||||||
				Data: map[string]interface{}{},
 | 
					
 | 
				
			||||||
			}
 | 
								ctx := &ArtifactContext{Base: base}
 | 
				
			||||||
			defer ctx.Close()
 | 
								ctx.AppendContextValue(artifactContextKey, ctx)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
 | 
								// action task call server api with Bearer ACTIONS_RUNTIME_TOKEN
 | 
				
			||||||
			// we should verify the ACTIONS_RUNTIME_TOKEN
 | 
								// we should verify the ACTIONS_RUNTIME_TOKEN
 | 
				
			||||||
| 
						 | 
					@ -132,6 +146,7 @@ func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler
 | 
				
			||||||
				ctx.Error(http.StatusUnauthorized, "Bad authorization header")
 | 
									ctx.Error(http.StatusUnauthorized, "Bad authorization header")
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			authToken := strings.TrimPrefix(authHeader, "Bearer ")
 | 
								authToken := strings.TrimPrefix(authHeader, "Bearer ")
 | 
				
			||||||
			task, err := actions.GetRunningTaskByToken(req.Context(), authToken)
 | 
								task, err := actions.GetRunningTaskByToken(req.Context(), authToken)
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
| 
						 | 
					@ -139,16 +154,14 @@ func withContexter(goctx gocontext.Context) func(next http.Handler) http.Handler
 | 
				
			||||||
				ctx.Error(http.StatusInternalServerError, "Error runner api getting task")
 | 
									ctx.Error(http.StatusInternalServerError, "Error runner api getting task")
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			ctx.Data["task"] = task
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if err := task.LoadJob(goctx); err != nil {
 | 
								if err := task.LoadJob(req.Context()); err != nil {
 | 
				
			||||||
				log.Error("Error runner api getting job: %v", err)
 | 
									log.Error("Error runner api getting job: %v", err)
 | 
				
			||||||
				ctx.Error(http.StatusInternalServerError, "Error runner api getting job")
 | 
									ctx.Error(http.StatusInternalServerError, "Error runner api getting job")
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Req = context.WithContext(req, &ctx)
 | 
								ctx.ActionTask = task
 | 
				
			||||||
 | 
					 | 
				
			||||||
			next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
								next.ServeHTTP(ctx.Resp, ctx.Req)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -175,13 +188,8 @@ type getUploadArtifactResponse struct {
 | 
				
			||||||
	FileContainerResourceURL string `json:"fileContainerResourceUrl"`
 | 
						FileContainerResourceURL string `json:"fileContainerResourceUrl"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) validateRunID(ctx *context.Context) (*actions.ActionTask, int64, bool) {
 | 
					func (ar artifactRoutes) validateRunID(ctx *ArtifactContext) (*actions.ActionTask, int64, bool) {
 | 
				
			||||||
	task, ok := ctx.Data["task"].(*actions.ActionTask)
 | 
						task := ctx.ActionTask
 | 
				
			||||||
	if !ok {
 | 
					 | 
				
			||||||
		log.Error("Error getting task in context")
 | 
					 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "Error getting task in context")
 | 
					 | 
				
			||||||
		return nil, 0, false
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	runID := ctx.ParamsInt64("run_id")
 | 
						runID := ctx.ParamsInt64("run_id")
 | 
				
			||||||
	if task.Job.RunID != runID {
 | 
						if task.Job.RunID != runID {
 | 
				
			||||||
		log.Error("Error runID not match")
 | 
							log.Error("Error runID not match")
 | 
				
			||||||
| 
						 | 
					@ -192,7 +200,7 @@ func (ar artifactRoutes) validateRunID(ctx *context.Context) (*actions.ActionTas
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getUploadArtifactURL generates a URL for uploading an artifact
 | 
					// getUploadArtifactURL generates a URL for uploading an artifact
 | 
				
			||||||
func (ar artifactRoutes) getUploadArtifactURL(ctx *context.Context) {
 | 
					func (ar artifactRoutes) getUploadArtifactURL(ctx *ArtifactContext) {
 | 
				
			||||||
	task, runID, ok := ar.validateRunID(ctx)
 | 
						task, runID, ok := ar.validateRunID(ctx)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -220,7 +228,7 @@ func (ar artifactRoutes) getUploadArtifactURL(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// getUploadFileSize returns the size of the file to be uploaded.
 | 
					// getUploadFileSize returns the size of the file to be uploaded.
 | 
				
			||||||
// The raw size is the size of the file as reported by the header X-TFS-FileLength.
 | 
					// The raw size is the size of the file as reported by the header X-TFS-FileLength.
 | 
				
			||||||
func (ar artifactRoutes) getUploadFileSize(ctx *context.Context) (int64, int64, error) {
 | 
					func (ar artifactRoutes) getUploadFileSize(ctx *ArtifactContext) (int64, int64, error) {
 | 
				
			||||||
	contentLength := ctx.Req.ContentLength
 | 
						contentLength := ctx.Req.ContentLength
 | 
				
			||||||
	xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64)
 | 
						xTfsLength, _ := strconv.ParseInt(ctx.Req.Header.Get(artifactXTfsFileLengthHeader), 10, 64)
 | 
				
			||||||
	if xTfsLength > 0 {
 | 
						if xTfsLength > 0 {
 | 
				
			||||||
| 
						 | 
					@ -229,7 +237,7 @@ func (ar artifactRoutes) getUploadFileSize(ctx *context.Context) (int64, int64,
 | 
				
			||||||
	return contentLength, contentLength, nil
 | 
						return contentLength, contentLength, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) saveUploadChunk(ctx *context.Context,
 | 
					func (ar artifactRoutes) saveUploadChunk(ctx *ArtifactContext,
 | 
				
			||||||
	artifact *actions.ActionArtifact,
 | 
						artifact *actions.ActionArtifact,
 | 
				
			||||||
	contentSize, runID int64,
 | 
						contentSize, runID int64,
 | 
				
			||||||
) (int64, error) {
 | 
					) (int64, error) {
 | 
				
			||||||
| 
						 | 
					@ -273,7 +281,7 @@ func (ar artifactRoutes) saveUploadChunk(ctx *context.Context,
 | 
				
			||||||
// The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
 | 
					// The rules are from https://github.com/actions/toolkit/blob/main/packages/artifact/src/internal/path-and-artifact-name-validation.ts#L32
 | 
				
			||||||
var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "")
 | 
					var invalidArtifactNameChars = strings.Join([]string{"\\", "/", "\"", ":", "<", ">", "|", "*", "?", "\r", "\n"}, "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) uploadArtifact(ctx *context.Context) {
 | 
					func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
 | 
				
			||||||
	_, runID, ok := ar.validateRunID(ctx)
 | 
						_, runID, ok := ar.validateRunID(ctx)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -341,7 +349,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// comfirmUploadArtifact comfirm upload artifact.
 | 
					// comfirmUploadArtifact comfirm upload artifact.
 | 
				
			||||||
// if all chunks are uploaded, merge them to one file.
 | 
					// if all chunks are uploaded, merge them to one file.
 | 
				
			||||||
func (ar artifactRoutes) comfirmUploadArtifact(ctx *context.Context) {
 | 
					func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
 | 
				
			||||||
	_, runID, ok := ar.validateRunID(ctx)
 | 
						_, runID, ok := ar.validateRunID(ctx)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -364,7 +372,7 @@ type chunkItem struct {
 | 
				
			||||||
	Path       string
 | 
						Path       string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64) error {
 | 
					func (ar artifactRoutes) mergeArtifactChunks(ctx *ArtifactContext, runID int64) error {
 | 
				
			||||||
	storageDir := fmt.Sprintf("tmp%d", runID)
 | 
						storageDir := fmt.Sprintf("tmp%d", runID)
 | 
				
			||||||
	var chunks []*chunkItem
 | 
						var chunks []*chunkItem
 | 
				
			||||||
	if err := ar.fs.IterateObjects(storageDir, func(path string, obj storage.Object) error {
 | 
						if err := ar.fs.IterateObjects(storageDir, func(path string, obj storage.Object) error {
 | 
				
			||||||
| 
						 | 
					@ -415,14 +423,20 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// use multiReader
 | 
							// use multiReader
 | 
				
			||||||
		readers := make([]io.Reader, 0, len(allChunks))
 | 
							readers := make([]io.Reader, 0, len(allChunks))
 | 
				
			||||||
		readerClosers := make([]io.Closer, 0, len(allChunks))
 | 
							closeReaders := func() {
 | 
				
			||||||
 | 
								for _, r := range readers {
 | 
				
			||||||
 | 
									_ = r.(io.Closer).Close() // it guarantees to be io.Closer by the following loop's Open function
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								readers = nil
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							defer closeReaders()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, c := range allChunks {
 | 
							for _, c := range allChunks {
 | 
				
			||||||
			reader, err := ar.fs.Open(c.Path)
 | 
								var readCloser io.ReadCloser
 | 
				
			||||||
			if err != nil {
 | 
								if readCloser, err = ar.fs.Open(c.Path); err != nil {
 | 
				
			||||||
				return fmt.Errorf("open chunk error: %v, %s", err, c.Path)
 | 
									return fmt.Errorf("open chunk error: %v, %s", err, c.Path)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			readers = append(readers, reader)
 | 
								readers = append(readers, readCloser)
 | 
				
			||||||
			readerClosers = append(readerClosers, reader)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		mergedReader := io.MultiReader(readers...)
 | 
							mergedReader := io.MultiReader(readers...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -445,11 +459,6 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
 | 
				
			||||||
			return fmt.Errorf("merged file size is not equal to chunk length")
 | 
								return fmt.Errorf("merged file size is not equal to chunk length")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// close readers
 | 
					 | 
				
			||||||
		for _, r := range readerClosers {
 | 
					 | 
				
			||||||
			r.Close()
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		// save storage path to artifact
 | 
							// save storage path to artifact
 | 
				
			||||||
		log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
 | 
							log.Debug("[artifact] merge chunks to artifact: %d, %s", artifact.ID, storagePath)
 | 
				
			||||||
		artifact.StoragePath = storagePath
 | 
							artifact.StoragePath = storagePath
 | 
				
			||||||
| 
						 | 
					@ -458,6 +467,8 @@ func (ar artifactRoutes) mergeArtifactChunks(ctx *context.Context, runID int64)
 | 
				
			||||||
			return fmt.Errorf("update artifact error: %v", err)
 | 
								return fmt.Errorf("update artifact error: %v", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							closeReaders() // close before delete
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// drop chunks
 | 
							// drop chunks
 | 
				
			||||||
		for _, c := range cs {
 | 
							for _, c := range cs {
 | 
				
			||||||
			if err := ar.fs.Delete(c.Path); err != nil {
 | 
								if err := ar.fs.Delete(c.Path); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -479,21 +490,21 @@ type (
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) listArtifacts(ctx *context.Context) {
 | 
					func (ar artifactRoutes) listArtifacts(ctx *ArtifactContext) {
 | 
				
			||||||
	_, runID, ok := ar.validateRunID(ctx)
 | 
						_, runID, ok := ar.validateRunID(ctx)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	artficats, err := actions.ListArtifactsByRunID(ctx, runID)
 | 
						artifacts, err := actions.ListArtifactsByRunID(ctx, runID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Error("Error getting artifacts: %v", err)
 | 
							log.Error("Error getting artifacts: %v", err)
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, err.Error())
 | 
							ctx.Error(http.StatusInternalServerError, err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	artficatsData := make([]listArtifactsResponseItem, 0, len(artficats))
 | 
						artficatsData := make([]listArtifactsResponseItem, 0, len(artifacts))
 | 
				
			||||||
	for _, a := range artficats {
 | 
						for _, a := range artifacts {
 | 
				
			||||||
		artficatsData = append(artficatsData, listArtifactsResponseItem{
 | 
							artficatsData = append(artficatsData, listArtifactsResponseItem{
 | 
				
			||||||
			Name:                     a.ArtifactName,
 | 
								Name:                     a.ArtifactName,
 | 
				
			||||||
			FileContainerResourceURL: ar.buildArtifactURL(runID, a.ID, "path"),
 | 
								FileContainerResourceURL: ar.buildArtifactURL(runID, a.ID, "path"),
 | 
				
			||||||
| 
						 | 
					@ -517,7 +528,7 @@ type (
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) getDownloadArtifactURL(ctx *context.Context) {
 | 
					func (ar artifactRoutes) getDownloadArtifactURL(ctx *ArtifactContext) {
 | 
				
			||||||
	_, runID, ok := ar.validateRunID(ctx)
 | 
						_, runID, ok := ar.validateRunID(ctx)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -546,7 +557,7 @@ func (ar artifactRoutes) getDownloadArtifactURL(ctx *context.Context) {
 | 
				
			||||||
	ctx.JSON(http.StatusOK, respData)
 | 
						ctx.JSON(http.StatusOK, respData)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ar artifactRoutes) downloadArtifact(ctx *context.Context) {
 | 
					func (ar artifactRoutes) downloadArtifact(ctx *ArtifactContext) {
 | 
				
			||||||
	_, runID, ok := ar.validateRunID(ctx)
 | 
						_, runID, ok := ar.validateRunID(ctx)
 | 
				
			||||||
	if !ok {
 | 
						if !ok {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -98,7 +98,7 @@ func verifyAuth(r *web.Route, authMethods []auth.Method) {
 | 
				
			||||||
func CommonRoutes(ctx gocontext.Context) *web.Route {
 | 
					func CommonRoutes(ctx gocontext.Context) *web.Route {
 | 
				
			||||||
	r := web.NewRoute()
 | 
						r := web.NewRoute()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r.Use(context.PackageContexter(ctx))
 | 
						r.Use(context.PackageContexter())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	verifyAuth(r, []auth.Method{
 | 
						verifyAuth(r, []auth.Method{
 | 
				
			||||||
		&auth.OAuth2{},
 | 
							&auth.OAuth2{},
 | 
				
			||||||
| 
						 | 
					@ -574,7 +574,7 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
 | 
				
			||||||
func ContainerRoutes(ctx gocontext.Context) *web.Route {
 | 
					func ContainerRoutes(ctx gocontext.Context) *web.Route {
 | 
				
			||||||
	r := web.NewRoute()
 | 
						r := web.NewRoute()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	r.Use(context.PackageContexter(ctx))
 | 
						r.Use(context.PackageContexter())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	verifyAuth(r, []auth.Method{
 | 
						verifyAuth(r, []auth.Method{
 | 
				
			||||||
		&auth.Basic{},
 | 
							&auth.Basic{},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -149,7 +149,7 @@ func repoAssignment() func(ctx *context.APIContext) {
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				if user_model.IsErrUserNotExist(err) {
 | 
									if user_model.IsErrUserNotExist(err) {
 | 
				
			||||||
					if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
 | 
										if redirectUserID, err := user_model.LookupUserRedirect(userName); err == nil {
 | 
				
			||||||
						context.RedirectToUser(ctx.Context, userName, redirectUserID)
 | 
											context.RedirectToUser(ctx.Base, userName, redirectUserID)
 | 
				
			||||||
					} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
										} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
				
			||||||
						ctx.NotFound("GetUserByName", err)
 | 
											ctx.NotFound("GetUserByName", err)
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
| 
						 | 
					@ -170,7 +170,7 @@ func repoAssignment() func(ctx *context.APIContext) {
 | 
				
			||||||
			if repo_model.IsErrRepoNotExist(err) {
 | 
								if repo_model.IsErrRepoNotExist(err) {
 | 
				
			||||||
				redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
 | 
									redirectRepoID, err := repo_model.LookupRedirect(owner.ID, repoName)
 | 
				
			||||||
				if err == nil {
 | 
									if err == nil {
 | 
				
			||||||
					context.RedirectToRepo(ctx.Context, redirectRepoID)
 | 
										context.RedirectToRepo(ctx.Base, redirectRepoID)
 | 
				
			||||||
				} else if repo_model.IsErrRedirectNotExist(err) {
 | 
									} else if repo_model.IsErrRedirectNotExist(err) {
 | 
				
			||||||
					ctx.NotFound()
 | 
										ctx.NotFound()
 | 
				
			||||||
				} else {
 | 
									} else {
 | 
				
			||||||
| 
						 | 
					@ -274,7 +274,7 @@ func reqToken(requiredScope auth_model.AccessTokenScope) func(ctx *context.APICo
 | 
				
			||||||
			ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope)
 | 
								ctx.Error(http.StatusForbidden, "reqToken", "token does not have required scope: "+requiredScope)
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if ctx.Context.IsBasicAuth {
 | 
							if ctx.IsBasicAuth {
 | 
				
			||||||
			ctx.CheckForOTP()
 | 
								ctx.CheckForOTP()
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -295,7 +295,7 @@ func reqExploreSignIn() func(ctx *context.APIContext) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func reqBasicAuth() func(ctx *context.APIContext) {
 | 
					func reqBasicAuth() func(ctx *context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		if !ctx.Context.IsBasicAuth {
 | 
							if !ctx.IsBasicAuth {
 | 
				
			||||||
			ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required")
 | 
								ctx.Error(http.StatusUnauthorized, "reqBasicAuth", "auth required")
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -375,7 +375,7 @@ func reqAnyRepoReader() func(ctx *context.APIContext) {
 | 
				
			||||||
// reqOrgOwnership user should be an organization owner, or a site admin
 | 
					// reqOrgOwnership user should be an organization owner, or a site admin
 | 
				
			||||||
func reqOrgOwnership() func(ctx *context.APIContext) {
 | 
					func reqOrgOwnership() func(ctx *context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		if ctx.Context.IsUserSiteAdmin() {
 | 
							if ctx.IsUserSiteAdmin() {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -407,7 +407,7 @@ func reqOrgOwnership() func(ctx *context.APIContext) {
 | 
				
			||||||
// reqTeamMembership user should be an team member, or a site admin
 | 
					// reqTeamMembership user should be an team member, or a site admin
 | 
				
			||||||
func reqTeamMembership() func(ctx *context.APIContext) {
 | 
					func reqTeamMembership() func(ctx *context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		if ctx.Context.IsUserSiteAdmin() {
 | 
							if ctx.IsUserSiteAdmin() {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if ctx.Org.Team == nil {
 | 
							if ctx.Org.Team == nil {
 | 
				
			||||||
| 
						 | 
					@ -444,7 +444,7 @@ func reqTeamMembership() func(ctx *context.APIContext) {
 | 
				
			||||||
// reqOrgMembership user should be an organization member, or a site admin
 | 
					// reqOrgMembership user should be an organization member, or a site admin
 | 
				
			||||||
func reqOrgMembership() func(ctx *context.APIContext) {
 | 
					func reqOrgMembership() func(ctx *context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		if ctx.Context.IsUserSiteAdmin() {
 | 
							if ctx.IsUserSiteAdmin() {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -512,7 +512,7 @@ func orgAssignment(args ...bool) func(ctx *context.APIContext) {
 | 
				
			||||||
				if organization.IsErrOrgNotExist(err) {
 | 
									if organization.IsErrOrgNotExist(err) {
 | 
				
			||||||
					redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org"))
 | 
										redirectUserID, err := user_model.LookupUserRedirect(ctx.Params(":org"))
 | 
				
			||||||
					if err == nil {
 | 
										if err == nil {
 | 
				
			||||||
						context.RedirectToUser(ctx.Context, ctx.Params(":org"), redirectUserID)
 | 
											context.RedirectToUser(ctx.Base, ctx.Params(":org"), redirectUserID)
 | 
				
			||||||
					} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
										} else if user_model.IsErrUserRedirectNotExist(err) {
 | 
				
			||||||
						ctx.NotFound("GetOrgByName", err)
 | 
											ctx.NotFound("GetOrgByName", err)
 | 
				
			||||||
					} else {
 | 
										} else {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -41,7 +41,7 @@ func Markup(ctx *context.APIContext) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	common.RenderMarkup(ctx.Context, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
 | 
						common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Markdown render markdown document to HTML
 | 
					// Markdown render markdown document to HTML
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ func Markdown(ctx *context.APIContext) {
 | 
				
			||||||
		mode = form.Mode
 | 
							mode = form.Mode
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	common.RenderMarkup(ctx.Context, mode, form.Text, form.Context, "", form.Wiki)
 | 
						common.RenderMarkup(ctx.Base, ctx.Repo, mode, form.Text, form.Context, "", form.Wiki)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MarkdownRaw render raw markdown HTML
 | 
					// MarkdownRaw render raw markdown HTML
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -16,7 +16,6 @@ import (
 | 
				
			||||||
	"code.gitea.io/gitea/modules/markup"
 | 
						"code.gitea.io/gitea/modules/markup"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"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/templates"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/util"
 | 
						"code.gitea.io/gitea/modules/util"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web/middleware"
 | 
						"code.gitea.io/gitea/modules/web/middleware"
 | 
				
			||||||
| 
						 | 
					@ -30,26 +29,16 @@ const (
 | 
				
			||||||
	AppSubURL = AppURL + Repo + "/"
 | 
						AppSubURL = AppURL + Repo + "/"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func createContext(req *http.Request) (*context.Context, *httptest.ResponseRecorder) {
 | 
					func createAPIContext(req *http.Request) (*context.APIContext, *httptest.ResponseRecorder) {
 | 
				
			||||||
	rnd := templates.HTMLRenderer()
 | 
					 | 
				
			||||||
	resp := httptest.NewRecorder()
 | 
						resp := httptest.NewRecorder()
 | 
				
			||||||
	c := &context.Context{
 | 
						base, baseCleanUp := context.NewBaseContext(resp, req)
 | 
				
			||||||
		Req:    req,
 | 
						base.Data = middleware.ContextData{}
 | 
				
			||||||
		Resp:   context.NewResponse(resp),
 | 
						c := &context.APIContext{Base: base}
 | 
				
			||||||
		Render: rnd,
 | 
						_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
 | 
				
			||||||
		Data:   make(middleware.ContextData),
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	defer c.Close()
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return c, resp
 | 
						return c, resp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func wrap(ctx *context.Context) *context.APIContext {
 | 
					 | 
				
			||||||
	return &context.APIContext{
 | 
					 | 
				
			||||||
		Context: ctx,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) {
 | 
					func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, responseCode int) {
 | 
				
			||||||
	setting.AppURL = AppURL
 | 
						setting.AppURL = AppURL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,8 +54,7 @@ func testRenderMarkup(t *testing.T, mode, filePath, text, responseBody string, r
 | 
				
			||||||
		Method: "POST",
 | 
							Method: "POST",
 | 
				
			||||||
		URL:    requrl,
 | 
							URL:    requrl,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m, resp := createContext(req)
 | 
						ctx, resp := createAPIContext(req)
 | 
				
			||||||
	ctx := wrap(m)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	options.Text = text
 | 
						options.Text = text
 | 
				
			||||||
	web.SetForm(ctx, &options)
 | 
						web.SetForm(ctx, &options)
 | 
				
			||||||
| 
						 | 
					@ -90,8 +78,7 @@ func testRenderMarkdown(t *testing.T, mode, text, responseBody string, responseC
 | 
				
			||||||
		Method: "POST",
 | 
							Method: "POST",
 | 
				
			||||||
		URL:    requrl,
 | 
							URL:    requrl,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m, resp := createContext(req)
 | 
						ctx, resp := createAPIContext(req)
 | 
				
			||||||
	ctx := wrap(m)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	options.Text = text
 | 
						options.Text = text
 | 
				
			||||||
	web.SetForm(ctx, &options)
 | 
						web.SetForm(ctx, &options)
 | 
				
			||||||
| 
						 | 
					@ -211,8 +198,7 @@ func TestAPI_RenderSimple(t *testing.T) {
 | 
				
			||||||
		Method: "POST",
 | 
							Method: "POST",
 | 
				
			||||||
		URL:    requrl,
 | 
							URL:    requrl,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m, resp := createContext(req)
 | 
						ctx, resp := createAPIContext(req)
 | 
				
			||||||
	ctx := wrap(m)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < len(simpleCases); i += 2 {
 | 
						for i := 0; i < len(simpleCases); i += 2 {
 | 
				
			||||||
		options.Text = simpleCases[i]
 | 
							options.Text = simpleCases[i]
 | 
				
			||||||
| 
						 | 
					@ -231,8 +217,7 @@ func TestAPI_RenderRaw(t *testing.T) {
 | 
				
			||||||
		Method: "POST",
 | 
							Method: "POST",
 | 
				
			||||||
		URL:    requrl,
 | 
							URL:    requrl,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	m, resp := createContext(req)
 | 
						ctx, resp := createAPIContext(req)
 | 
				
			||||||
	ctx := wrap(m)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := 0; i < len(simpleCases); i += 2 {
 | 
						for i := 0; i < len(simpleCases); i += 2 {
 | 
				
			||||||
		ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i]))
 | 
							ctx.Req.Body = io.NopCloser(strings.NewReader(simpleCases[i]))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,7 @@ func NewAvailable(ctx *context.APIContext) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
 | 
					func getFindNotificationOptions(ctx *context.APIContext) *activities_model.FindNotificationOptions {
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -80,7 +80,7 @@ func GetRawFile(ctx *context.APIContext) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry)))
 | 
						ctx.RespHeader().Set(giteaObjectTypeHeader, string(files_service.GetObjectTypeFromTreeEntry(entry)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := common.ServeBlob(ctx.Context, blob, lastModified); err != nil {
 | 
						if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusInternalServerError, "ServeBlob", err)
 | 
							ctx.Error(http.StatusInternalServerError, "ServeBlob", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -137,7 +137,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// OK not cached - serve!
 | 
							// OK not cached - serve!
 | 
				
			||||||
		if err := common.ServeBlob(ctx.Context, blob, lastModified); err != nil {
 | 
							if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
 | 
				
			||||||
			ctx.ServerError("ServeBlob", err)
 | 
								ctx.ServerError("ServeBlob", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -159,7 +159,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := dataRc.Close(); err != nil {
 | 
						if err := dataRc.Close(); err != nil {
 | 
				
			||||||
		log.Error("Error whilst closing blob %s reader in %-v. Error: %v", blob.ID, ctx.Context.Repo.Repository, err)
 | 
							log.Error("Error whilst closing blob %s reader in %-v. Error: %v", blob.ID, ctx.Repo.Repository, err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check if the blob represents a pointer
 | 
						// Check if the blob represents a pointer
 | 
				
			||||||
| 
						 | 
					@ -173,7 +173,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// OK not cached - serve!
 | 
							// OK not cached - serve!
 | 
				
			||||||
		common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
 | 
							common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -187,7 +187,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		common.ServeContentByReader(ctx.Context, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
 | 
							common.ServeContentByReader(ctx.Base, ctx.Repo.TreePath, blob.Size(), bytes.NewReader(buf))
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	} else if err != nil {
 | 
						} else if err != nil {
 | 
				
			||||||
		ctx.ServerError("GetLFSMetaObjectByOid", err)
 | 
							ctx.ServerError("GetLFSMetaObjectByOid", err)
 | 
				
			||||||
| 
						 | 
					@ -215,7 +215,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer lfsDataRc.Close()
 | 
						defer lfsDataRc.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	common.ServeContentByReadSeeker(ctx.Context, ctx.Repo.TreePath, lastModified, lfsDataRc)
 | 
						common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) {
 | 
					func getBlobForEntry(ctx *context.APIContext) (blob *git.Blob, entry *git.TreeEntry, lastModified time.Time) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,6 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/unittest"
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
	"code.gitea.io/gitea/models/webhook"
 | 
						"code.gitea.io/gitea/models/webhook"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/test"
 | 
						"code.gitea.io/gitea/modules/test"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
| 
						 | 
					@ -18,12 +17,12 @@ import (
 | 
				
			||||||
func TestTestHook(t *testing.T) {
 | 
					func TestTestHook(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1/wiki/_pages")
 | 
						ctx := test.MockAPIContext(t, "user2/repo1/wiki/_pages")
 | 
				
			||||||
	ctx.SetParams(":id", "1")
 | 
						ctx.SetParams(":id", "1")
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	test.LoadRepoCommit(t, ctx)
 | 
						test.LoadRepoCommit(t, ctx)
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	TestHook(&context.APIContext{Context: ctx, Org: nil})
 | 
						TestHook(ctx)
 | 
				
			||||||
	assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusNoContent, ctx.Resp.Status())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
 | 
						unittest.AssertExistsAndLoadBean(t, &webhook.HookTask{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -116,7 +116,7 @@ func SearchIssues(ctx *context.APIContext) {
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/IssueList"
 | 
						//     "$ref": "#/responses/IssueList"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -368,7 +368,7 @@ func ListIssues(ctx *context.APIContext) {
 | 
				
			||||||
	// responses:
 | 
						// responses:
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/IssueList"
 | 
						//     "$ref": "#/responses/IssueList"
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -59,7 +59,7 @@ func ListIssueComments(ctx *context.APIContext) {
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/CommentList"
 | 
						//     "$ref": "#/responses/CommentList"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -156,7 +156,7 @@ func ListIssueCommentsAndTimeline(ctx *context.APIContext) {
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/TimelineList"
 | 
						//     "$ref": "#/responses/TimelineList"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -259,7 +259,7 @@ func ListRepoIssueComments(ctx *context.APIContext) {
 | 
				
			||||||
	//   "200":
 | 
						//   "200":
 | 
				
			||||||
	//     "$ref": "#/responses/CommentList"
 | 
						//     "$ref": "#/responses/CommentList"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx.Context)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -103,7 +103,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
 | 
				
			||||||
		opts.UserID = user.ID
 | 
							opts.UserID = user.ID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
 | 
						if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -522,7 +522,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
 | 
						if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -596,7 +596,7 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
	if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Context); err != nil {
 | 
						if opts.CreatedBeforeUnix, opts.CreatedAfterUnix, err = context.GetQueryBeforeSince(ctx.Base); err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
							ctx.Error(http.StatusUnprocessableEntity, "GetQueryBeforeSince", err)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ func Migrate(ctx *context.APIContext) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.HasError() {
 | 
						if ctx.HasAPIError() {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
 | 
							ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,6 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	repo_model "code.gitea.io/gitea/models/repo"
 | 
						repo_model "code.gitea.io/gitea/models/repo"
 | 
				
			||||||
	"code.gitea.io/gitea/models/unittest"
 | 
						"code.gitea.io/gitea/models/unittest"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
					 | 
				
			||||||
	api "code.gitea.io/gitea/modules/structs"
 | 
						api "code.gitea.io/gitea/modules/structs"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/test"
 | 
						"code.gitea.io/gitea/modules/test"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/web"
 | 
						"code.gitea.io/gitea/modules/web"
 | 
				
			||||||
| 
						 | 
					@ -20,7 +19,7 @@ import (
 | 
				
			||||||
func TestRepoEdit(t *testing.T) {
 | 
					func TestRepoEdit(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1")
 | 
						ctx := test.MockAPIContext(t, "user2/repo1")
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	ctx.Repo.Owner = ctx.Doer
 | 
						ctx.Repo.Owner = ctx.Doer
 | 
				
			||||||
| 
						 | 
					@ -54,9 +53,8 @@ func TestRepoEdit(t *testing.T) {
 | 
				
			||||||
		Archived:                  &archived,
 | 
							Archived:                  &archived,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiCtx := &context.APIContext{Context: ctx, Org: nil}
 | 
						web.SetForm(ctx, &opts)
 | 
				
			||||||
	web.SetForm(apiCtx, &opts)
 | 
						Edit(ctx)
 | 
				
			||||||
	Edit(apiCtx)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
 | 
						unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
 | 
				
			||||||
| 
						 | 
					@ -67,7 +65,7 @@ func TestRepoEdit(t *testing.T) {
 | 
				
			||||||
func TestRepoEditNameChange(t *testing.T) {
 | 
					func TestRepoEditNameChange(t *testing.T) {
 | 
				
			||||||
	unittest.PrepareTestEnv(t)
 | 
						unittest.PrepareTestEnv(t)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ctx := test.MockContext(t, "user2/repo1")
 | 
						ctx := test.MockAPIContext(t, "user2/repo1")
 | 
				
			||||||
	test.LoadRepo(t, ctx, 1)
 | 
						test.LoadRepo(t, ctx, 1)
 | 
				
			||||||
	test.LoadUser(t, ctx, 2)
 | 
						test.LoadUser(t, ctx, 2)
 | 
				
			||||||
	ctx.Repo.Owner = ctx.Doer
 | 
						ctx.Repo.Owner = ctx.Doer
 | 
				
			||||||
| 
						 | 
					@ -76,9 +74,8 @@ func TestRepoEditNameChange(t *testing.T) {
 | 
				
			||||||
		Name: &name,
 | 
							Name: &name,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	apiCtx := &context.APIContext{Context: ctx, Org: nil}
 | 
						web.SetForm(ctx, &opts)
 | 
				
			||||||
	web.SetForm(apiCtx, &opts)
 | 
						Edit(ctx)
 | 
				
			||||||
	Edit(apiCtx)
 | 
					 | 
				
			||||||
	assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
						assert.EqualValues(t, http.StatusOK, ctx.Resp.Status())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
 | 
						unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -183,7 +183,7 @@ func getCommitStatuses(ctx *context.APIContext, sha string) {
 | 
				
			||||||
		ctx.Error(http.StatusBadRequest, "ref/sha not given", nil)
 | 
							ctx.Error(http.StatusBadRequest, "ref/sha not given", nil)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sha = utils.MustConvertToSHA1(ctx.Context, sha)
 | 
						sha = utils.MustConvertToSHA1(ctx.Base, ctx.Repo, sha)
 | 
				
			||||||
	repo := ctx.Repo.Repository
 | 
						repo := ctx.Repo.Repository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	listOptions := utils.GetListOptions(ctx)
 | 
						listOptions := utils.GetListOptions(ctx)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ func GetUserByParamsName(ctx *context.APIContext, name string) *user_model.User
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if user_model.IsErrUserNotExist(err) {
 | 
							if user_model.IsErrUserNotExist(err) {
 | 
				
			||||||
			if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil {
 | 
								if redirectUserID, err2 := user_model.LookupUserRedirect(username); err2 == nil {
 | 
				
			||||||
				context.RedirectToUser(ctx.Context, username, redirectUserID)
 | 
									context.RedirectToUser(ctx.Base, username, redirectUserID)
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ctx.NotFound("GetUserByName", err)
 | 
									ctx.NotFound("GetUserByName", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
package utils
 | 
					package utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						gocontext "context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,7 +34,7 @@ func ResolveRefOrSha(ctx *context.APIContext, ref string) string {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sha = MustConvertToSHA1(ctx.Context, sha)
 | 
						sha = MustConvertToSHA1(ctx, ctx.Repo, sha)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.Repo.GitRepo != nil {
 | 
						if ctx.Repo.GitRepo != nil {
 | 
				
			||||||
		err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha)
 | 
							err := ctx.Repo.GitRepo.AddLastCommitCache(ctx.Repo.Repository.GetCommitsCountCacheKey(ref, ref != sha), ctx.Repo.Repository.FullName(), sha)
 | 
				
			||||||
| 
						 | 
					@ -69,7 +70,7 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertToSHA1 returns a full-length SHA1 from a potential ID string
 | 
					// ConvertToSHA1 returns a full-length SHA1 from a potential ID string
 | 
				
			||||||
func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
 | 
					func ConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) (git.SHA1, error) {
 | 
				
			||||||
	if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
 | 
						if len(commitID) == git.SHAFullLength && git.IsValidSHAPattern(commitID) {
 | 
				
			||||||
		sha1, err := git.NewIDFromString(commitID)
 | 
							sha1, err := git.NewIDFromString(commitID)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
| 
						 | 
					@ -77,7 +78,7 @@ func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, ctx.Repo.Repository.RepoPath())
 | 
						gitRepo, closer, err := git.RepositoryFromContextOrOpen(ctx, repo.Repository.RepoPath())
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
 | 
							return git.SHA1{}, fmt.Errorf("RepositoryFromContextOrOpen: %w", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -87,8 +88,8 @@ func ConvertToSHA1(ctx *context.Context, commitID string) (git.SHA1, error) {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
 | 
					// MustConvertToSHA1 returns a full-length SHA1 string from a potential ID string, or returns origin input if it can't convert to SHA1
 | 
				
			||||||
func MustConvertToSHA1(ctx *context.Context, commitID string) string {
 | 
					func MustConvertToSHA1(ctx gocontext.Context, repo *context.Repository, commitID string) string {
 | 
				
			||||||
	sha, err := ConvertToSHA1(ctx, commitID)
 | 
						sha, err := ConvertToSHA1(ctx, repo, commitID)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return commitID
 | 
							return commitID
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RenderMarkup renders markup text for the /markup and /markdown endpoints
 | 
					// RenderMarkup renders markup text for the /markup and /markdown endpoints
 | 
				
			||||||
func RenderMarkup(ctx *context.Context, mode, text, urlPrefix, filePath string, wiki bool) {
 | 
					func RenderMarkup(ctx *context.Base, repo *context.Repository, mode, text, urlPrefix, filePath string, wiki bool) {
 | 
				
			||||||
	var markupType string
 | 
						var markupType string
 | 
				
			||||||
	relativePath := ""
 | 
						relativePath := ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -63,11 +63,11 @@ func RenderMarkup(ctx *context.Context, mode, text, urlPrefix, filePath string,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	meta := map[string]string{}
 | 
						meta := map[string]string{}
 | 
				
			||||||
	if ctx.Repo != nil && ctx.Repo.Repository != nil {
 | 
						if repo != nil && repo.Repository != nil {
 | 
				
			||||||
		if mode == "comment" {
 | 
							if mode == "comment" {
 | 
				
			||||||
			meta = ctx.Repo.Repository.ComposeMetas()
 | 
								meta = repo.Repository.ComposeMetas()
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			meta = ctx.Repo.Repository.ComposeDocumentMetas()
 | 
								meta = repo.Repository.ComposeDocumentMetas()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if mode != "comment" {
 | 
						if mode != "comment" {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,7 +42,7 @@ func ProtocolMiddlewares() (handlers []any) {
 | 
				
			||||||
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
			ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
 | 
								ctx, _, finished := process.GetManager().AddTypedContext(req.Context(), fmt.Sprintf("%s: %s", req.Method, req.RequestURI), process.RequestProcessType, true)
 | 
				
			||||||
			defer finished()
 | 
								defer finished()
 | 
				
			||||||
			next.ServeHTTP(context.NewResponse(resp), req.WithContext(cache.WithCacheContext(ctx)))
 | 
								next.ServeHTTP(context.WrapResponseWriter(resp), req.WithContext(cache.WithCacheContext(ctx)))
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ServeBlob download a git.Blob
 | 
					// ServeBlob download a git.Blob
 | 
				
			||||||
func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) error {
 | 
					func ServeBlob(ctx *context.Base, filePath string, blob *git.Blob, lastModified time.Time) error {
 | 
				
			||||||
	if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
 | 
						if httpcache.HandleGenericETagTimeCache(ctx.Req, ctx.Resp, `"`+blob.ID.String()+`"`, lastModified) {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -30,14 +30,14 @@ func ServeBlob(ctx *context.Context, blob *git.Blob, lastModified time.Time) err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}()
 | 
						}()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	httplib.ServeContentByReader(ctx.Req, ctx.Resp, ctx.Repo.TreePath, blob.Size(), dataRc)
 | 
						httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, blob.Size(), dataRc)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ServeContentByReader(ctx *context.Context, filePath string, size int64, reader io.Reader) {
 | 
					func ServeContentByReader(ctx *context.Base, filePath string, size int64, reader io.Reader) {
 | 
				
			||||||
	httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader)
 | 
						httplib.ServeContentByReader(ctx.Req, ctx.Resp, filePath, size, reader)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func ServeContentByReadSeeker(ctx *context.Context, filePath string, modTime time.Time, reader io.ReadSeeker) {
 | 
					func ServeContentByReadSeeker(ctx *context.Base, filePath string, modTime time.Time, reader io.ReadSeeker) {
 | 
				
			||||||
	httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader)
 | 
						httplib.ServeContentByReadSeeker(ctx.Req, ctx.Resp, filePath, modTime, reader)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,7 +198,7 @@ func NormalRoutes(ctx context.Context) *web.Route {
 | 
				
			||||||
		// In Github, it uses ACTIONS_RUNTIME_URL=https://pipelines.actions.githubusercontent.com/fLgcSHkPGySXeIFrg8W8OBSfeg3b5Fls1A1CwX566g8PayEGlg/
 | 
							// In Github, it uses ACTIONS_RUNTIME_URL=https://pipelines.actions.githubusercontent.com/fLgcSHkPGySXeIFrg8W8OBSfeg3b5Fls1A1CwX566g8PayEGlg/
 | 
				
			||||||
		// TODO: this prefix should be generated with a token string with runner ?
 | 
							// TODO: this prefix should be generated with a token string with runner ?
 | 
				
			||||||
		prefix = "/api/actions_pipeline"
 | 
							prefix = "/api/actions_pipeline"
 | 
				
			||||||
		r.Mount(prefix, actions_router.ArtifactsRoutes(ctx, prefix))
 | 
							r.Mount(prefix, actions_router.ArtifactsRoutes(prefix))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -58,15 +58,14 @@ func Contexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
	dbTypeNames := getSupportedDbTypeNames()
 | 
						dbTypeNames := getSupportedDbTypeNames()
 | 
				
			||||||
	return func(next http.Handler) http.Handler {
 | 
						return func(next http.Handler) http.Handler {
 | 
				
			||||||
		return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
							return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
 | 
				
			||||||
 | 
								base, baseCleanUp := context.NewBaseContext(resp, req)
 | 
				
			||||||
			ctx := context.Context{
 | 
								ctx := context.Context{
 | 
				
			||||||
				Resp:    context.NewResponse(resp),
 | 
									Base:    base,
 | 
				
			||||||
				Flash:   &middleware.Flash{},
 | 
									Flash:   &middleware.Flash{},
 | 
				
			||||||
				Locale:  middleware.Locale(resp, req),
 | 
					 | 
				
			||||||
				Render:  rnd,
 | 
									Render:  rnd,
 | 
				
			||||||
				Data:    middleware.GetContextData(req.Context()),
 | 
					 | 
				
			||||||
				Session: session.GetSession(req),
 | 
									Session: session.GetSession(req),
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			defer ctx.Close()
 | 
								defer baseCleanUp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
 | 
								ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
 | 
				
			||||||
			ctx.Data.MergeFrom(middleware.ContextData{
 | 
								ctx.Data.MergeFrom(middleware.ContextData{
 | 
				
			||||||
| 
						 | 
					@ -78,7 +77,6 @@ func Contexter() func(next http.Handler) http.Handler {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				"PasswordHashAlgorithms": hash.RecommendedHashAlgorithms,
 | 
									"PasswordHashAlgorithms": hash.RecommendedHashAlgorithms,
 | 
				
			||||||
			})
 | 
								})
 | 
				
			||||||
			ctx.Req = context.WithContext(req, &ctx)
 | 
					 | 
				
			||||||
			next.ServeHTTP(resp, ctx.Req)
 | 
								next.ServeHTTP(resp, ctx.Req)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					@ -249,15 +247,8 @@ func SubmitInstall(ctx *context.Context) {
 | 
				
			||||||
	ctx.Data["CurDbType"] = form.DbType
 | 
						ctx.Data["CurDbType"] = form.DbType
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.HasError() {
 | 
						if ctx.HasError() {
 | 
				
			||||||
		if ctx.HasValue("Err_SMTPUser") {
 | 
							ctx.Data["Err_SMTP"] = ctx.Data["Err_SMTPUser"] != nil
 | 
				
			||||||
			ctx.Data["Err_SMTP"] = true
 | 
							ctx.Data["Err_Admin"] = ctx.Data["Err_AdminName"] != nil || ctx.Data["Err_AdminPasswd"] != nil || ctx.Data["Err_AdminEmail"] != nil
 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if ctx.HasValue("Err_AdminName") ||
 | 
					 | 
				
			||||||
			ctx.HasValue("Err_AdminPasswd") ||
 | 
					 | 
				
			||||||
			ctx.HasValue("Err_AdminEmail") {
 | 
					 | 
				
			||||||
			ctx.Data["Err_Admin"] = true
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		ctx.HTML(http.StatusOK, tplInstall)
 | 
							ctx.HTML(http.StatusOK, tplInstall)
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,8 +5,6 @@
 | 
				
			||||||
package misc
 | 
					package misc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"net/http"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	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"
 | 
				
			||||||
| 
						 | 
					@ -16,11 +14,5 @@ import (
 | 
				
			||||||
// Markup render markup document to HTML
 | 
					// Markup render markup document to HTML
 | 
				
			||||||
func Markup(ctx *context.Context) {
 | 
					func Markup(ctx *context.Context) {
 | 
				
			||||||
	form := web.GetForm(ctx).(*api.MarkupOption)
 | 
						form := web.GetForm(ctx).(*api.MarkupOption)
 | 
				
			||||||
 | 
						common.RenderMarkup(ctx.Base, ctx.Repo, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
 | 
				
			||||||
	if ctx.HasAPIError() {
 | 
					 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, "", ctx.GetErrMsg())
 | 
					 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	common.RenderMarkup(ctx, form.Mode, form.Text, form.Context, form.FilePath, form.Wiki)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -153,7 +153,7 @@ func ServeAttachment(ctx *context.Context, uuid string) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer fr.Close()
 | 
						defer fr.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	common.ServeContentByReadSeeker(ctx, attach.Name, attach.CreatedUnix.AsTime(), fr)
 | 
						common.ServeContentByReadSeeker(ctx.Base, attach.Name, attach.CreatedUnix.AsTime(), fr)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GetAttachment serve attachments
 | 
					// GetAttachment serve attachments
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -47,7 +47,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
 | 
				
			||||||
				log.Error("ServeBlobOrLFS: Close: %v", err)
 | 
									log.Error("ServeBlobOrLFS: Close: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			closed = true
 | 
								closed = true
 | 
				
			||||||
			return common.ServeBlob(ctx, blob, lastModified)
 | 
								return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
 | 
							if httpcache.HandleGenericETagCache(ctx.Req, ctx.Resp, `"`+pointer.Oid+`"`) {
 | 
				
			||||||
			return nil
 | 
								return nil
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
 | 
				
			||||||
				log.Error("ServeBlobOrLFS: Close: %v", err)
 | 
									log.Error("ServeBlobOrLFS: Close: %v", err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
		common.ServeContentByReadSeeker(ctx, ctx.Repo.TreePath, lastModified, lfsDataRc)
 | 
							common.ServeContentByReadSeeker(ctx.Base, ctx.Repo.TreePath, lastModified, lfsDataRc)
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err = dataRc.Close(); err != nil {
 | 
						if err = dataRc.Close(); err != nil {
 | 
				
			||||||
| 
						 | 
					@ -79,7 +79,7 @@ func ServeBlobOrLFS(ctx *context.Context, blob *git.Blob, lastModified time.Time
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	closed = true
 | 
						closed = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return common.ServeBlob(ctx, blob, lastModified)
 | 
						return common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) {
 | 
					func getBlobForEntry(ctx *context.Context) (blob *git.Blob, lastModified time.Time) {
 | 
				
			||||||
| 
						 | 
					@ -120,7 +120,7 @@ func SingleDownload(ctx *context.Context) {
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := common.ServeBlob(ctx, blob, lastModified); err != nil {
 | 
						if err := common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, lastModified); err != nil {
 | 
				
			||||||
		ctx.ServerError("ServeBlob", err)
 | 
							ctx.ServerError("ServeBlob", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -148,7 +148,7 @@ func DownloadByID(ctx *context.Context) {
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err = common.ServeBlob(ctx, blob, time.Time{}); err != nil {
 | 
						if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, blob, time.Time{}); err != nil {
 | 
				
			||||||
		ctx.ServerError("ServeBlob", err)
 | 
							ctx.ServerError("ServeBlob", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -109,7 +109,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) {
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		if repo_model.IsErrRepoNotExist(err) {
 | 
							if repo_model.IsErrRepoNotExist(err) {
 | 
				
			||||||
			if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil {
 | 
								if redirectRepoID, err := repo_model.LookupRedirect(owner.ID, reponame); err == nil {
 | 
				
			||||||
				context.RedirectToRepo(ctx, redirectRepoID)
 | 
									context.RedirectToRepo(ctx.Base, redirectRepoID)
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			repoExist = false
 | 
								repoExist = false
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2344,7 +2344,7 @@ func UpdatePullReviewRequest(ctx *context.Context) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SearchIssues searches for issues across the repositories that the user has access to
 | 
					// SearchIssues searches for issues across the repositories that the user has access to
 | 
				
			||||||
func SearchIssues(ctx *context.Context) {
 | 
					func SearchIssues(ctx *context.Context) {
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, err.Error())
 | 
							ctx.Error(http.StatusUnprocessableEntity, err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					@ -2545,7 +2545,7 @@ func getUserIDForFilter(ctx *context.Context, queryName string) int64 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ListIssues list the issues of a repository
 | 
					// ListIssues list the issues of a repository
 | 
				
			||||||
func ListIssues(ctx *context.Context) {
 | 
					func ListIssues(ctx *context.Context) {
 | 
				
			||||||
	before, since, err := context.GetQueryBeforeSince(ctx)
 | 
						before, since, err := context.GetQueryBeforeSince(ctx.Base)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		ctx.Error(http.StatusUnprocessableEntity, err.Error())
 | 
							ctx.Error(http.StatusUnprocessableEntity, err.Error())
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -671,7 +671,7 @@ func WikiRaw(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if entry != nil {
 | 
						if entry != nil {
 | 
				
			||||||
		if err = common.ServeBlob(ctx, entry.Blob(), time.Time{}); err != nil {
 | 
							if err = common.ServeBlob(ctx.Base, ctx.Repo.TreePath, entry.Blob(), time.Time{}); err != nil {
 | 
				
			||||||
			ctx.ServerError("ServeBlob", err)
 | 
								ctx.ServerError("ServeBlob", err)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import (
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/auth"
 | 
						"code.gitea.io/gitea/models/auth"
 | 
				
			||||||
 | 
						user_model "code.gitea.io/gitea/models/user"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/context"
 | 
						"code.gitea.io/gitea/modules/context"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/log"
 | 
						"code.gitea.io/gitea/modules/log"
 | 
				
			||||||
	"code.gitea.io/gitea/modules/setting"
 | 
						"code.gitea.io/gitea/modules/setting"
 | 
				
			||||||
| 
						 | 
					@ -17,11 +18,15 @@ import (
 | 
				
			||||||
// Auth is a middleware to authenticate a web user
 | 
					// Auth is a middleware to authenticate a web user
 | 
				
			||||||
func Auth(authMethod Method) func(*context.Context) {
 | 
					func Auth(authMethod Method) func(*context.Context) {
 | 
				
			||||||
	return func(ctx *context.Context) {
 | 
						return func(ctx *context.Context) {
 | 
				
			||||||
		if err := authShared(ctx, authMethod); err != nil {
 | 
							ar, err := authShared(ctx.Base, ctx.Session, authMethod)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
			log.Error("Failed to verify user: %v", err)
 | 
								log.Error("Failed to verify user: %v", err)
 | 
				
			||||||
			ctx.Error(http.StatusUnauthorized, "Verify")
 | 
								ctx.Error(http.StatusUnauthorized, "Verify")
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							ctx.Doer = ar.Doer
 | 
				
			||||||
 | 
							ctx.IsSigned = ar.Doer != nil
 | 
				
			||||||
 | 
							ctx.IsBasicAuth = ar.IsBasicAuth
 | 
				
			||||||
		if ctx.Doer == nil {
 | 
							if ctx.Doer == nil {
 | 
				
			||||||
			// ensure the session uid is deleted
 | 
								// ensure the session uid is deleted
 | 
				
			||||||
			_ = ctx.Session.Delete("uid")
 | 
								_ = ctx.Session.Delete("uid")
 | 
				
			||||||
| 
						 | 
					@ -32,32 +37,41 @@ func Auth(authMethod Method) func(*context.Context) {
 | 
				
			||||||
// APIAuth is a middleware to authenticate an api user
 | 
					// APIAuth is a middleware to authenticate an api user
 | 
				
			||||||
func APIAuth(authMethod Method) func(*context.APIContext) {
 | 
					func APIAuth(authMethod Method) func(*context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		if err := authShared(ctx.Context, authMethod); err != nil {
 | 
							ar, err := authShared(ctx.Base, nil, authMethod)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
			ctx.Error(http.StatusUnauthorized, "APIAuth", err)
 | 
								ctx.Error(http.StatusUnauthorized, "APIAuth", err)
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							ctx.Doer = ar.Doer
 | 
				
			||||||
 | 
							ctx.IsSigned = ar.Doer != nil
 | 
				
			||||||
 | 
							ctx.IsBasicAuth = ar.IsBasicAuth
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func authShared(ctx *context.Context, authMethod Method) error {
 | 
					type authResult struct {
 | 
				
			||||||
	var err error
 | 
						Doer        *user_model.User
 | 
				
			||||||
	ctx.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
 | 
						IsBasicAuth bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func authShared(ctx *context.Base, sessionStore SessionStore, authMethod Method) (ar authResult, err error) {
 | 
				
			||||||
 | 
						ar.Doer, err = authMethod.Verify(ctx.Req, ctx.Resp, ctx, sessionStore)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return ar, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if ctx.Doer != nil {
 | 
						if ar.Doer != nil {
 | 
				
			||||||
		if ctx.Locale.Language() != ctx.Doer.Language {
 | 
							if ctx.Locale.Language() != ar.Doer.Language {
 | 
				
			||||||
			ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
 | 
								ctx.Locale = middleware.Locale(ctx.Resp, ctx.Req)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		ctx.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == BasicMethodName
 | 
							ar.IsBasicAuth = ctx.Data["AuthedMethod"].(string) == BasicMethodName
 | 
				
			||||||
		ctx.IsSigned = true
 | 
					
 | 
				
			||||||
		ctx.Data["IsSigned"] = ctx.IsSigned
 | 
							ctx.Data["IsSigned"] = true
 | 
				
			||||||
		ctx.Data[middleware.ContextDataKeySignedUser] = ctx.Doer
 | 
							ctx.Data[middleware.ContextDataKeySignedUser] = ar.Doer
 | 
				
			||||||
		ctx.Data["SignedUserID"] = ctx.Doer.ID
 | 
							ctx.Data["SignedUserID"] = ar.Doer.ID
 | 
				
			||||||
		ctx.Data["IsAdmin"] = ctx.Doer.IsAdmin
 | 
							ctx.Data["IsAdmin"] = ar.Doer.IsAdmin
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		ctx.Data["SignedUserID"] = int64(0)
 | 
							ctx.Data["SignedUserID"] = int64(0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return ar, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// VerifyOptions contains required or check options
 | 
					// VerifyOptions contains required or check options
 | 
				
			||||||
| 
						 | 
					@ -68,7 +82,7 @@ type VerifyOptions struct {
 | 
				
			||||||
	DisableCSRF     bool
 | 
						DisableCSRF     bool
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Checks authentication according to options
 | 
					// VerifyAuthWithOptions checks authentication according to options
 | 
				
			||||||
func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) {
 | 
					func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) {
 | 
				
			||||||
	return func(ctx *context.Context) {
 | 
						return func(ctx *context.Context) {
 | 
				
			||||||
		// Check prohibit login users.
 | 
							// Check prohibit login users.
 | 
				
			||||||
| 
						 | 
					@ -153,7 +167,7 @@ func VerifyAuthWithOptions(options *VerifyOptions) func(ctx *context.Context) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Checks authentication according to options
 | 
					// VerifyAuthWithOptionsAPI checks authentication according to options
 | 
				
			||||||
func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIContext) {
 | 
					func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		// Check prohibit login users.
 | 
							// Check prohibit login users.
 | 
				
			||||||
| 
						 | 
					@ -197,7 +211,9 @@ func VerifyAuthWithOptionsAPI(options *VerifyOptions) func(ctx *context.APIConte
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			} else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
 | 
								} else if !ctx.Doer.IsActive && setting.Service.RegisterEmailConfirm {
 | 
				
			||||||
				ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
 | 
									ctx.Data["Title"] = ctx.Tr("auth.active_your_account")
 | 
				
			||||||
				ctx.HTML(http.StatusOK, "user/auth/activate")
 | 
									ctx.JSON(http.StatusForbidden, map[string]string{
 | 
				
			||||||
 | 
										"message": "This account is not activated.",
 | 
				
			||||||
 | 
									})
 | 
				
			||||||
				return
 | 
									return
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if ctx.IsSigned && ctx.IsBasicAuth {
 | 
								if ctx.IsSigned && ctx.IsBasicAuth {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,7 +15,7 @@ import (
 | 
				
			||||||
// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
 | 
					// UserAssignmentWeb returns a middleware to handle context-user assignment for web routes
 | 
				
			||||||
func UserAssignmentWeb() func(ctx *context.Context) {
 | 
					func UserAssignmentWeb() func(ctx *context.Context) {
 | 
				
			||||||
	return func(ctx *context.Context) {
 | 
						return func(ctx *context.Context) {
 | 
				
			||||||
		userAssignment(ctx, func(status int, title string, obj interface{}) {
 | 
							errorFn := func(status int, title string, obj interface{}) {
 | 
				
			||||||
			err, ok := obj.(error)
 | 
								err, ok := obj.(error)
 | 
				
			||||||
			if !ok {
 | 
								if !ok {
 | 
				
			||||||
				err = fmt.Errorf("%s", obj)
 | 
									err = fmt.Errorf("%s", obj)
 | 
				
			||||||
| 
						 | 
					@ -25,7 +25,8 @@ func UserAssignmentWeb() func(ctx *context.Context) {
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				ctx.ServerError(title, err)
 | 
									ctx.ServerError(title, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		})
 | 
							}
 | 
				
			||||||
 | 
							ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, errorFn)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,18 +54,18 @@ func UserIDAssignmentAPI() func(ctx *context.APIContext) {
 | 
				
			||||||
// UserAssignmentAPI returns a middleware to handle context-user assignment for api routes
 | 
					// UserAssignmentAPI returns a middleware to handle context-user assignment for api routes
 | 
				
			||||||
func UserAssignmentAPI() func(ctx *context.APIContext) {
 | 
					func UserAssignmentAPI() func(ctx *context.APIContext) {
 | 
				
			||||||
	return func(ctx *context.APIContext) {
 | 
						return func(ctx *context.APIContext) {
 | 
				
			||||||
		userAssignment(ctx.Context, ctx.Error)
 | 
							ctx.ContextUser = userAssignment(ctx.Base, ctx.Doer, ctx.Error)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func userAssignment(ctx *context.Context, errCb func(int, string, interface{})) {
 | 
					func userAssignment(ctx *context.Base, doer *user_model.User, errCb func(int, string, interface{})) (contextUser *user_model.User) {
 | 
				
			||||||
	username := ctx.Params(":username")
 | 
						username := ctx.Params(":username")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ctx.IsSigned && ctx.Doer.LowerName == strings.ToLower(username) {
 | 
						if doer != nil && doer.LowerName == strings.ToLower(username) {
 | 
				
			||||||
		ctx.ContextUser = ctx.Doer
 | 
							contextUser = doer
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		var err error
 | 
							var err error
 | 
				
			||||||
		ctx.ContextUser, err = user_model.GetUserByName(ctx, username)
 | 
							contextUser, err = user_model.GetUserByName(ctx, username)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			if user_model.IsErrUserNotExist(err) {
 | 
								if user_model.IsErrUserNotExist(err) {
 | 
				
			||||||
				if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil {
 | 
									if redirectUserID, err := user_model.LookupUserRedirect(username); err == nil {
 | 
				
			||||||
| 
						 | 
					@ -79,4 +80,5 @@ func userAssignment(ctx *context.Context, errCb func(int, string, interface{}))
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						return contextUser
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ type AdminCreateUserForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates form fields
 | 
					// Validate validates form fields
 | 
				
			||||||
func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AdminCreateUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -55,7 +55,7 @@ type AdminEditUserForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates form fields
 | 
					// Validate validates form fields
 | 
				
			||||||
func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AdminEditUserForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,6 +67,6 @@ type AdminDashboardForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates form fields
 | 
					// Validate validates form fields
 | 
				
			||||||
func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AdminDashboardForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -86,6 +86,6 @@ type AuthenticationForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates fields
 | 
					// Validate validates fields
 | 
				
			||||||
func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AuthenticationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -30,7 +30,7 @@ type CreateOrgForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateOrgForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,7 +48,7 @@ type UpdateOrgSettingForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *UpdateOrgSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -70,6 +70,6 @@ type CreateTeamForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateTeamForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,6 @@ type PackageCleanupRuleForm struct {
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (f *PackageCleanupRuleForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *PackageCleanupRuleForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,7 +21,7 @@ type NewBranchForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -33,6 +33,6 @@ type RenameBranchForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *RenameBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *RenameBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -54,7 +54,7 @@ type CreateRepoForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -87,7 +87,7 @@ type MigrateRepoForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *MigrateRepoForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -176,7 +176,7 @@ type RepoSettingForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -215,7 +215,7 @@ type ProtectBranchForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *ProtectBranchForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -280,7 +280,7 @@ type NewWebhookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewWebhookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,7 +294,7 @@ type NewGogshookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewGogshookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -310,7 +310,7 @@ type NewSlackHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewSlackHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
 | 
						if !webhook.IsValidSlackChannel(strings.TrimSpace(f.Channel)) {
 | 
				
			||||||
		errs = append(errs, binding.Error{
 | 
							errs = append(errs, binding.Error{
 | 
				
			||||||
			FieldNames:     []string{"Channel"},
 | 
								FieldNames:     []string{"Channel"},
 | 
				
			||||||
| 
						 | 
					@ -331,7 +331,7 @@ type NewDiscordHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewDiscordHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -343,7 +343,7 @@ type NewDingtalkHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewDingtalkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -356,7 +356,7 @@ type NewTelegramHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewTelegramHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -370,7 +370,7 @@ type NewMatrixHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewMatrixHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -382,7 +382,7 @@ type NewMSTeamsHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewMSTeamsHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -394,7 +394,7 @@ type NewFeishuHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewFeishuHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -406,7 +406,7 @@ type NewWechatWorkHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewWechatWorkHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -420,7 +420,7 @@ type NewPackagistHookForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewPackagistHookForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -447,7 +447,7 @@ type CreateIssueForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateIssueForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -460,7 +460,7 @@ type CreateCommentForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -471,7 +471,7 @@ type ReactionForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *ReactionForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -482,7 +482,7 @@ type IssueLockForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (i *IssueLockForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, i, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, i, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -550,7 +550,7 @@ type CreateMilestoneForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateMilestoneForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -572,7 +572,7 @@ type CreateLabelForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CreateLabelForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -583,7 +583,7 @@ type InitializeLabelsForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -611,7 +611,7 @@ type MergePullRequestForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *MergePullRequestForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -629,7 +629,7 @@ type CodeCommentForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CodeCommentForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -643,7 +643,7 @@ type SubmitReviewForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *SubmitReviewForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -704,7 +704,7 @@ type NewReleaseForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -719,7 +719,7 @@ type EditReleaseForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *EditReleaseForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -740,7 +740,7 @@ type NewWikiForm struct {
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
// FIXME: use code generation to generate this method.
 | 
					// FIXME: use code generation to generate this method.
 | 
				
			||||||
func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewWikiForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -765,7 +765,7 @@ type EditRepoFileForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *EditRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -776,7 +776,7 @@ type EditPreviewDiffForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *EditPreviewDiffForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -800,7 +800,7 @@ type CherryPickForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *CherryPickForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *CherryPickForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -825,7 +825,7 @@ type UploadRepoFileForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *UploadRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -836,7 +836,7 @@ type RemoveUploadFileForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *RemoveUploadFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -859,7 +859,7 @@ type DeleteRepoFileForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *DeleteRepoFileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -878,7 +878,7 @@ type AddTimeManuallyForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AddTimeManuallyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -894,6 +894,6 @@ type DeadlineForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *DeadlineForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,6 +21,6 @@ type ProtectTagForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *ProtectTagForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *ProtectTagForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,6 +20,6 @@ type EditRunnerForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates form fields
 | 
					// Validate validates form fields
 | 
				
			||||||
func (f *EditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *EditRunnerForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -78,7 +78,7 @@ type InstallForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *InstallForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,7 +99,7 @@ type RegisterForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *RegisterForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -148,7 +148,7 @@ type MustChangePasswordForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *MustChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -162,7 +162,7 @@ type SignInForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *SignInForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -182,7 +182,7 @@ type AuthorizationForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AuthorizationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,7 +197,7 @@ type GrantApplicationForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *GrantApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -216,7 +216,7 @@ type AccessTokenForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -227,7 +227,7 @@ type IntrospectTokenForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *IntrospectTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -252,7 +252,7 @@ type UpdateProfileForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *UpdateProfileForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,7 @@ type UpdateLanguageForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *UpdateLanguageForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *UpdateLanguageForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -283,7 +283,7 @@ type AvatarForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AvatarForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -294,7 +294,7 @@ type AddEmailForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AddEmailForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -305,7 +305,7 @@ type UpdateThemeForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the field
 | 
					// Validate validates the field
 | 
				
			||||||
func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *UpdateThemeForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -332,7 +332,7 @@ type ChangePasswordForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *ChangePasswordForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -343,7 +343,7 @@ type AddOpenIDForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AddOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -360,7 +360,7 @@ type AddKeyForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AddKeyForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -372,7 +372,7 @@ type AddSecretForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *AddSecretForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -384,7 +384,7 @@ type NewAccessTokenForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *NewAccessTokenForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -403,7 +403,7 @@ type EditOAuth2ApplicationForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *EditOAuth2ApplicationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -414,7 +414,7 @@ type TwoFactorAuthForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *TwoFactorAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -425,7 +425,7 @@ type TwoFactorScratchAuthForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *TwoFactorScratchAuthForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -436,7 +436,7 @@ type WebauthnRegistrationForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *WebauthnRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *WebauthnRegistrationForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -447,7 +447,7 @@ type WebauthnDeleteForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *WebauthnDeleteForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -459,6 +459,6 @@ type PackageSettingForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *PackageSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *PackageSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ type SignInOpenIDForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *SignInOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ type SignUpOpenIDForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *SignUpOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,6 +44,6 @@ type ConnectOpenIDForm struct {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Validate validates the fields
 | 
					// Validate validates the fields
 | 
				
			||||||
func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
					func (f *ConnectOpenIDForm) Validate(req *http.Request, errs binding.Errors) binding.Errors {
 | 
				
			||||||
	ctx := context.GetContext(req)
 | 
						ctx := context.GetValidateContext(req)
 | 
				
			||||||
	return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
						return middleware.Validate(errs, ctx.Data, f, ctx.Locale)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,6 +6,7 @@ package markup
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/http/httptest"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"code.gitea.io/gitea/models/db"
 | 
						"code.gitea.io/gitea/models/db"
 | 
				
			||||||
| 
						 | 
					@ -36,12 +37,12 @@ func TestProcessorHelper(t *testing.T) {
 | 
				
			||||||
	assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch))
 | 
						assert.False(t, ProcessorHelper().IsUsernameMentionable(context.Background(), userNoSuch))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// when using web context, use user.IsUserVisibleToViewer to check
 | 
						// when using web context, use user.IsUserVisibleToViewer to check
 | 
				
			||||||
	var err error
 | 
						req, err := http.NewRequest("GET", "/", nil)
 | 
				
			||||||
	giteaCtx := &gitea_context.Context{}
 | 
					 | 
				
			||||||
	giteaCtx.Req, err = http.NewRequest("GET", "/", nil)
 | 
					 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
						base, baseCleanUp := gitea_context.NewBaseContext(httptest.NewRecorder(), req)
 | 
				
			||||||
 | 
						defer baseCleanUp()
 | 
				
			||||||
 | 
						giteaCtx := &gitea_context.Context{Base: base}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	giteaCtx.Doer = nil
 | 
					 | 
				
			||||||
	assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic))
 | 
						assert.True(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPublic))
 | 
				
			||||||
	assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate))
 | 
						assert.False(t, ProcessorHelper().IsUsernameMentionable(giteaCtx, userPrivate))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue