diff options
Diffstat (limited to 'modules/context/context.go')
-rw-r--r-- | modules/context/context.go | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/modules/context/context.go b/modules/context/context.go new file mode 100644 index 0000000000..0c1ec520c1 --- /dev/null +++ b/modules/context/context.go @@ -0,0 +1,266 @@ +// Copyright 2014 The Gogs Authors. All rights reserved. +// Use of this source code is governed by a MIT-style +// license that can be found in the LICENSE file. + +package context + +import ( + "fmt" + "html/template" + "io" + "net/http" + "strings" + "time" + + "github.com/go-macaron/cache" + "github.com/go-macaron/csrf" + "github.com/go-macaron/i18n" + "github.com/go-macaron/session" + "gopkg.in/macaron.v1" + + "github.com/gogits/git-module" + + "github.com/gogits/gogs/models" + "github.com/gogits/gogs/modules/auth" + "github.com/gogits/gogs/modules/base" + "github.com/gogits/gogs/modules/log" + "github.com/gogits/gogs/modules/setting" +) + +type PullRequest struct { + BaseRepo *models.Repository + Allowed bool + SameRepo bool + HeadInfo string // [<user>:]<branch> +} + +type Repository struct { + AccessMode models.AccessMode + IsWatching bool + IsViewBranch bool + IsViewTag bool + IsViewCommit bool + Repository *models.Repository + Owner *models.User + Commit *git.Commit + Tag *git.Tag + GitRepo *git.Repository + BranchName string + TagName string + TreeName string + CommitID string + RepoLink string + CloneLink models.CloneLink + CommitsCount int64 + Mirror *models.Mirror + + PullRequest *PullRequest +} + +// IsOwner returns true if current user is the owner of repository. +func (r *Repository) IsOwner() bool { + return r.AccessMode >= models.ACCESS_MODE_OWNER +} + +// IsAdmin returns true if current user has admin or higher access of repository. +func (r *Repository) IsAdmin() bool { + return r.AccessMode >= models.ACCESS_MODE_ADMIN +} + +// IsWriter returns true if current user has write or higher access of repository. +func (r *Repository) IsWriter() bool { + return r.AccessMode >= models.ACCESS_MODE_WRITE +} + +// HasAccess returns true if the current user has at least read access for this repository +func (r *Repository) HasAccess() bool { + return r.AccessMode >= models.ACCESS_MODE_READ +} + +// Context represents context of a request. +type Context struct { + *macaron.Context + Cache cache.Cache + csrf csrf.CSRF + Flash *session.Flash + Session session.Store + + User *models.User + IsSigned bool + IsBasicAuth bool + + Repo *Repository + + Org struct { + IsOwner bool + IsMember bool + IsTeamMember bool // Is member of team. + IsTeamAdmin bool // In owner team or team that has admin permission level. + Organization *models.User + OrgLink string + + Team *models.Team + } +} + +// HasError 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) +} + +func (ctx *Context) GetErrMsg() string { + return ctx.Data["ErrorMsg"].(string) +} + +// HasError returns true if error occurs in form validation. +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 +} + +// HTML calls Context.HTML and converts template name to string. +func (ctx *Context) HTML(status int, name base.TplName) { + log.Debug("Template: %s", name) + ctx.Context.HTML(status, string(name)) +} + +// RenderWithErr used for page has form validation but need to prompt error to users. +func (ctx *Context) RenderWithErr(msg string, tpl base.TplName, form interface{}) { + if form != nil { + auth.AssignForm(form, ctx.Data) + } + ctx.Flash.ErrorMsg = msg + ctx.Data["Flash"] = ctx.Flash + ctx.HTML(200, tpl) +} + +// Handle handles and logs error by given status. +func (ctx *Context) Handle(status int, title string, err error) { + if err != nil { + log.Error(4, "%s: %v", title, err) + if macaron.Env != macaron.PROD { + ctx.Data["ErrorMsg"] = err + } + } + + switch status { + case 404: + ctx.Data["Title"] = "Page Not Found" + case 500: + ctx.Data["Title"] = "Internal Server Error" + } + ctx.HTML(status, base.TplName(fmt.Sprintf("status/%d", status))) +} + +func (ctx *Context) HandleText(status int, title string) { + if (status/100 == 4) || (status/100 == 5) { + log.Error(4, "%s", title) + } + ctx.PlainText(status, []byte(title)) +} + +// APIError logs error with title if status is 500. +func (ctx *Context) APIError(status int, title string, obj interface{}) { + var message string + if err, ok := obj.(error); ok { + message = err.Error() + } else { + message = obj.(string) + } + + if status == 500 { + log.Error(4, "%s: %s", title, message) + } + + ctx.JSON(status, map[string]string{ + "message": message, + "url": base.DOC_URL, + }) +} + +func (ctx *Context) ServeContent(name string, r io.ReadSeeker, params ...interface{}) { + modtime := time.Now() + for _, p := range params { + switch v := p.(type) { + case time.Time: + modtime = v + } + } + ctx.Resp.Header().Set("Content-Description", "File Transfer") + ctx.Resp.Header().Set("Content-Type", "application/octet-stream") + ctx.Resp.Header().Set("Content-Disposition", "attachment; filename="+name) + ctx.Resp.Header().Set("Content-Transfer-Encoding", "binary") + ctx.Resp.Header().Set("Expires", "0") + ctx.Resp.Header().Set("Cache-Control", "must-revalidate") + ctx.Resp.Header().Set("Pragma", "public") + http.ServeContent(ctx.Resp, ctx.Req.Request, name, modtime, r) +} + +// Contexter initializes a classic context for a request. +func Contexter() macaron.Handler { + return func(c *macaron.Context, l i18n.Locale, cache cache.Cache, sess session.Store, f *session.Flash, x csrf.CSRF) { + ctx := &Context{ + Context: c, + Cache: cache, + csrf: x, + Flash: f, + Session: sess, + Repo: &Repository{ + PullRequest: &PullRequest{}, + }, + } + // Compute current URL for real-time change language. + ctx.Data["Link"] = setting.AppSubUrl + strings.TrimSuffix(ctx.Req.URL.Path, "/") + + ctx.Data["PageStartTime"] = time.Now() + + // Get user from session if logined. + ctx.User, ctx.IsBasicAuth = auth.SignedInUser(ctx.Context, ctx.Session) + + if ctx.User != nil { + ctx.IsSigned = true + ctx.Data["IsSigned"] = ctx.IsSigned + ctx.Data["SignedUser"] = ctx.User + ctx.Data["SignedUserID"] = ctx.User.Id + ctx.Data["SignedUserName"] = ctx.User.Name + ctx.Data["IsAdmin"] = ctx.User.IsAdmin + } else { + ctx.Data["SignedUserID"] = 0 + ctx.Data["SignedUserName"] = "" + } + + // 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 err := ctx.Req.ParseMultipartForm(setting.AttachmentMaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size + ctx.Handle(500, "ParseMultipartForm", err) + return + } + } + + ctx.Data["CsrfToken"] = x.GetToken() + ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + x.GetToken() + `">`) + log.Debug("Session ID: %s", sess.ID()) + log.Debug("CSRF Token: %v", ctx.Data["CsrfToken"]) + + ctx.Data["ShowRegistrationButton"] = setting.Service.ShowRegistrationButton + ctx.Data["ShowFooterBranding"] = setting.ShowFooterBranding + ctx.Data["ShowFooterVersion"] = setting.ShowFooterVersion + + c.Map(ctx) + } +} |