|
|
|
// Copyright 2014 The Gogs Authors. All rights reserved.
|
|
|
|
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package context
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/hex"
|
|
|
|
"html"
|
|
|
|
"html/template"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"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/httpcache"
|
|
|
|
"code.gitea.io/gitea/modules/setting"
|
|
|
|
"code.gitea.io/gitea/modules/templates"
|
|
|
|
"code.gitea.io/gitea/modules/translation"
|
|
|
|
"code.gitea.io/gitea/modules/web"
|
|
|
|
"code.gitea.io/gitea/modules/web/middleware"
|
|
|
|
web_types "code.gitea.io/gitea/modules/web/types"
|
|
|
|
|
|
|
|
"gitea.com/go-chi/cache"
|
|
|
|
"gitea.com/go-chi/session"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Render represents a template render
|
|
|
|
type Render interface {
|
|
|
|
TemplateLookup(tmpl string, templateCtx context.Context) (templates.TemplateExecutor, error)
|
|
|
|
HTML(w io.Writer, status int, name string, data any, templateCtx context.Context) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Context represents context of a request.
|
|
|
|
type Context struct {
|
|
|
|
*Base
|
|
|
|
|
|
|
|
TemplateContext TemplateContext
|
|
|
|
|
|
|
|
Render Render
|
|
|
|
PageData map[string]any // data used by JavaScript modules in one page, it's `window.config.pageData`
|
|
|
|
|
|
|
|
Cache cache.Cache
|
|
|
|
Csrf CSRFProtector
|
|
|
|
Flash *middleware.Flash
|
|
|
|
Session session.Store
|
|
|
|
|
|
|
|
Link string // current request URL (without query string)
|
|
|
|
|
|
|
|
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 *Organization
|
|
|
|
Package *Package
|
|
|
|
}
|
|
|
|
|
|
|
|
type TemplateContext map[string]any
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
web.RegisterResponseStatusProvider[*Context](func(req *http.Request) web_types.ResponseStatusProvider {
|
|
|
|
return req.Context().Value(WebContextKey).(*Context)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// TrHTMLEscapeArgs runs ".Locale.Tr()" but pre-escapes all arguments with html.EscapeString.
|
|
|
|
// This is useful if the locale message is intended to only produce HTML content.
|
|
|
|
func (ctx *Context) TrHTMLEscapeArgs(msg string, args ...string) string {
|
|
|
|
trArgs := make([]any, len(args))
|
|
|
|
for i, arg := range args {
|
|
|
|
trArgs[i] = html.EscapeString(arg)
|
|
|
|
}
|
|
|
|
return ctx.Locale.Tr(msg, trArgs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
type webContextKeyType struct{}
|
|
|
|
|
|
|
|
var WebContextKey = webContextKeyType{}
|
|
|
|
|
|
|
|
func GetWebContext(req *http.Request) *Context {
|
|
|
|
ctx, _ := req.Context().Value(WebContextKey).(*Context)
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// ValidateContext is a special context for form validation middleware. It may be different from other contexts.
|
|
|
|
type ValidateContext struct {
|
|
|
|
*Base
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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(WebContextKey).(*Context); ok {
|
|
|
|
ctx = &ValidateContext{Base: ctxWeb.Base}
|
|
|
|
} else {
|
|
|
|
panic("invalid context, expect either APIContext or Context")
|
|
|
|
}
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewTemplateContextForWeb(ctx *Context) TemplateContext {
|
|
|
|
tmplCtx := NewTemplateContext(ctx)
|
|
|
|
tmplCtx["Locale"] = ctx.Base.Locale
|
|
|
|
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
|
|
|
|
return tmplCtx
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewWebContext(base *Base, render Render, session session.Store) *Context {
|
|
|
|
ctx := &Context{
|
|
|
|
Base: base,
|
|
|
|
Render: render,
|
|
|
|
Session: session,
|
|
|
|
|
|
|
|
Cache: mc.GetCache(),
|
|
|
|
Link: setting.AppSubURL + strings.TrimSuffix(base.Req.URL.EscapedPath(), "/"),
|
|
|
|
Repo: &Repository{PullRequest: &PullRequest{}},
|
|
|
|
Org: &Organization{},
|
|
|
|
}
|
|
|
|
ctx.TemplateContext = NewTemplateContextForWeb(ctx)
|
|
|
|
ctx.Flash = &middleware.Flash{DataStore: ctx, Values: url.Values{}}
|
|
|
|
return ctx
|
|
|
|
}
|
|
|
|
|
|
|
|
// Contexter initializes a classic context for a request.
|
|
|
|
func Contexter() func(next http.Handler) http.Handler {
|
|
|
|
rnd := templates.HTMLRenderer()
|
|
|
|
csrfOpts := CsrfOptions{
|
|
|
|
Secret: hex.EncodeToString(setting.GetGeneralTokenSigningSecret()),
|
|
|
|
Cookie: setting.CSRFCookieName,
|
|
|
|
SetCookie: true,
|
|
|
|
Secure: setting.SessionConfig.Secure,
|
|
|
|
CookieHTTPOnly: setting.CSRFCookieHTTPOnly,
|
|
|
|
Header: "X-Csrf-Token",
|
|
|
|
CookieDomain: setting.SessionConfig.Domain,
|
|
|
|
CookiePath: setting.SessionConfig.CookiePath,
|
|
|
|
SameSite: setting.SessionConfig.SameSite,
|
|
|
|
}
|
|
|
|
if !setting.IsProd {
|
|
|
|
CsrfTokenRegenerationInterval = 5 * time.Second // in dev, re-generate the tokens more aggressively for debug purpose
|
|
|
|
}
|
|
|
|
return func(next http.Handler) http.Handler {
|
|
|
|
return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
|
|
|
|
base, baseCleanUp := NewBaseContext(resp, req)
|
|
|
|
defer baseCleanUp()
|
|
|
|
ctx := NewWebContext(base, rnd, session.GetSession(req))
|
|
|
|
|
|
|
|
ctx.Data.MergeFrom(middleware.CommonTemplateContextData())
|
|
|
|
ctx.Data["Context"] = ctx // TODO: use "ctx" in template and remove this
|
|
|
|
ctx.Data["CurrentURL"] = setting.AppSubURL + req.URL.RequestURI()
|
|
|
|
ctx.Data["Link"] = ctx.Link
|
|
|
|
|
|
|
|
// PageData is passed by reference, and it will be rendered to `window.config.pageData` in `head.tmpl` for JavaScript modules
|
|
|
|
ctx.PageData = map[string]any{}
|
|
|
|
ctx.Data["PageData"] = ctx.PageData
|
|
|
|
|
|
|
|
ctx.Base.AppendContextValue(WebContextKey, ctx)
|
|
|
|
ctx.Base.AppendContextValueFunc(git.RepositoryContextKey, func() any { return ctx.Repo.GitRepo })
|
|
|
|
|
|
|
|
ctx.Csrf = PrepareCSRFProtector(csrfOpts, ctx)
|
|
|
|
|
|
|
|
// Get the last flash message from cookie
|
|
|
|
lastFlashCookie := middleware.GetSiteCookie(ctx.Req, CookieNameFlash)
|
|
|
|
if vals, _ := url.ParseQuery(lastFlashCookie); len(vals) > 0 {
|
|
|
|
// store last Flash message into the template data, to render it
|
|
|
|
ctx.Data["Flash"] = &middleware.Flash{
|
|
|
|
DataStore: ctx,
|
|
|
|
Values: vals,
|
|
|
|
ErrorMsg: vals.Get("error"),
|
|
|
|
SuccessMsg: vals.Get("success"),
|
|
|
|
InfoMsg: vals.Get("info"),
|
|
|
|
WarningMsg: vals.Get("warning"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// if there are new messages in the ctx.Flash, write them into cookie
|
|
|
|
ctx.Resp.Before(func(resp ResponseWriter) {
|
|
|
|
if val := ctx.Flash.Encode(); val != "" {
|
|
|
|
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, val, 0)
|
|
|
|
} else if lastFlashCookie != "" {
|
|
|
|
middleware.SetSiteCookie(ctx.Resp, CookieNameFlash, "", -1)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
// 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.Attachment.MaxSize << 20); err != nil && !strings.Contains(err.Error(), "EOF") { // 32MB max size
|
|
|
|
ctx.ServerError("ParseMultipartForm", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
httpcache.SetCacheControlInHeader(ctx.Resp.Header(), 0, "no-transform")
|
|
|
|
ctx.Resp.Header().Set(`X-Frame-Options`, setting.CORSConfig.XFrameOptions)
|
|
|
|
|
|
|
|
ctx.Data["CsrfToken"] = ctx.Csrf.GetToken()
|
|
|
|
ctx.Data["CsrfTokenHtml"] = template.HTML(`<input type="hidden" name="_csrf" value="` + ctx.Data["CsrfToken"].(string) + `">`)
|
|
|
|
|
|
|
|
// FIXME: do we really always need these setting? There should be someway to have to avoid having to always set these
|
|
|
|
ctx.Data["DisableMigrations"] = setting.Repository.DisableMigrations
|
|
|
|
ctx.Data["DisableStars"] = setting.Repository.DisableStars
|
Implement actions (#21937)
Close #13539.
Co-authored by: @lunny @appleboy @fuxiaohei and others.
Related projects:
- https://gitea.com/gitea/actions-proto-def
- https://gitea.com/gitea/actions-proto-go
- https://gitea.com/gitea/act
- https://gitea.com/gitea/act_runner
### Summary
The target of this PR is to bring a basic implementation of "Actions",
an internal CI/CD system of Gitea. That means even though it has been
merged, the state of the feature is **EXPERIMENTAL**, and please note
that:
- It is disabled by default;
- It shouldn't be used in a production environment currently;
- It shouldn't be used in a public Gitea instance currently;
- Breaking changes may be made before it's stable.
**Please comment on #13539 if you have any different product design
ideas**, all decisions reached there will be adopted here. But in this
PR, we don't talk about **naming, feature-creep or alternatives**.
### ⚠️ Breaking
`gitea-actions` will become a reserved user name. If a user with the
name already exists in the database, it is recommended to rename it.
### Some important reviews
- What is `DEFAULT_ACTIONS_URL` in `app.ini` for?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1055954954
- Why the api for runners is not under the normal `/api/v1` prefix?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061173592
- Why DBFS?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061301178
- Why ignore events triggered by `gitea-actions` bot?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1063254103
- Why there's no permission control for actions?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1090229868
### What it looks like
<details>
#### Manage runners
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205870657-c72f590e-2e08-4cd4-be7f-2e0abb299bbf.png">
#### List runs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872794-50fde990-2b45-48c1-a178-908e4ec5b627.png">
#### View logs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872501-9b7b9000-9542-4991-8f55-18ccdada77c3.png">
</details>
### How to try it
<details>
#### 1. Start Gitea
Clone this branch and [install from
source](https://docs.gitea.io/en-us/install-from-source).
Add additional configurations in `app.ini` to enable Actions:
```ini
[actions]
ENABLED = true
```
Start it.
If all is well, you'll see the management page of runners:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205877365-8e30a780-9b10-4154-b3e8-ee6c3cb35a59.png">
#### 2. Start runner
Clone the [act_runner](https://gitea.com/gitea/act_runner), and follow
the
[README](https://gitea.com/gitea/act_runner/src/branch/main/README.md)
to start it.
If all is well, you'll see a new runner has been added:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205878000-216f5937-e696-470d-b66c-8473987d91c3.png">
#### 3. Enable actions for a repo
Create a new repo or open an existing one, check the `Actions` checkbox
in settings and submit.
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879705-53e09208-73c0-4b3e-a123-2dcf9aba4b9c.png">
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879383-23f3d08f-1a85-41dd-a8b3-54e2ee6453e8.png">
If all is well, you'll see a new tab "Actions":
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205881648-a8072d8c-5803-4d76-b8a8-9b2fb49516c1.png">
#### 4. Upload workflow files
Upload some workflow files to `.gitea/workflows/xxx.yaml`, you can
follow the [quickstart](https://docs.github.com/en/actions/quickstart)
of GitHub Actions. Yes, Gitea Actions is compatible with GitHub Actions
in most cases, you can use the same demo:
```yaml
name: GitHub Actions Demo
run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
on: [push]
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."
```
If all is well, you'll see a new run in `Actions` tab:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884473-79a874bc-171b-4aaf-acd5-0241a45c3b53.png">
#### 5. Check the logs of jobs
Click a run and you'll see the logs:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884800-994b0374-67f7-48ff-be9a-4c53f3141547.png">
#### 6. Go on
You can try more examples in [the
documents](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
of GitHub Actions, then you might find a lot of bugs.
Come on, PRs are welcome.
</details>
See also: [Feature Preview: Gitea
Actions](https://blog.gitea.io/2022/12/feature-preview-gitea-actions/)
---------
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2 years ago
|
|
|
ctx.Data["EnableActions"] = setting.Actions.Enabled
|
|
|
|
|
|
|
|
ctx.Data["ManifestData"] = setting.ManifestData
|
|
|
|
|
|
|
|
ctx.Data["UnitWikiGlobalDisabled"] = unit.TypeWiki.UnitGlobalDisabled()
|
|
|
|
ctx.Data["UnitIssuesGlobalDisabled"] = unit.TypeIssues.UnitGlobalDisabled()
|
|
|
|
ctx.Data["UnitPullsGlobalDisabled"] = unit.TypePullRequests.UnitGlobalDisabled()
|
|
|
|
ctx.Data["UnitProjectsGlobalDisabled"] = unit.TypeProjects.UnitGlobalDisabled()
|
Implement actions (#21937)
Close #13539.
Co-authored by: @lunny @appleboy @fuxiaohei and others.
Related projects:
- https://gitea.com/gitea/actions-proto-def
- https://gitea.com/gitea/actions-proto-go
- https://gitea.com/gitea/act
- https://gitea.com/gitea/act_runner
### Summary
The target of this PR is to bring a basic implementation of "Actions",
an internal CI/CD system of Gitea. That means even though it has been
merged, the state of the feature is **EXPERIMENTAL**, and please note
that:
- It is disabled by default;
- It shouldn't be used in a production environment currently;
- It shouldn't be used in a public Gitea instance currently;
- Breaking changes may be made before it's stable.
**Please comment on #13539 if you have any different product design
ideas**, all decisions reached there will be adopted here. But in this
PR, we don't talk about **naming, feature-creep or alternatives**.
### ⚠️ Breaking
`gitea-actions` will become a reserved user name. If a user with the
name already exists in the database, it is recommended to rename it.
### Some important reviews
- What is `DEFAULT_ACTIONS_URL` in `app.ini` for?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1055954954
- Why the api for runners is not under the normal `/api/v1` prefix?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061173592
- Why DBFS?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1061301178
- Why ignore events triggered by `gitea-actions` bot?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1063254103
- Why there's no permission control for actions?
- https://github.com/go-gitea/gitea/pull/21937#discussion_r1090229868
### What it looks like
<details>
#### Manage runners
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205870657-c72f590e-2e08-4cd4-be7f-2e0abb299bbf.png">
#### List runs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872794-50fde990-2b45-48c1-a178-908e4ec5b627.png">
#### View logs
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205872501-9b7b9000-9542-4991-8f55-18ccdada77c3.png">
</details>
### How to try it
<details>
#### 1. Start Gitea
Clone this branch and [install from
source](https://docs.gitea.io/en-us/install-from-source).
Add additional configurations in `app.ini` to enable Actions:
```ini
[actions]
ENABLED = true
```
Start it.
If all is well, you'll see the management page of runners:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205877365-8e30a780-9b10-4154-b3e8-ee6c3cb35a59.png">
#### 2. Start runner
Clone the [act_runner](https://gitea.com/gitea/act_runner), and follow
the
[README](https://gitea.com/gitea/act_runner/src/branch/main/README.md)
to start it.
If all is well, you'll see a new runner has been added:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205878000-216f5937-e696-470d-b66c-8473987d91c3.png">
#### 3. Enable actions for a repo
Create a new repo or open an existing one, check the `Actions` checkbox
in settings and submit.
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879705-53e09208-73c0-4b3e-a123-2dcf9aba4b9c.png">
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205879383-23f3d08f-1a85-41dd-a8b3-54e2ee6453e8.png">
If all is well, you'll see a new tab "Actions":
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205881648-a8072d8c-5803-4d76-b8a8-9b2fb49516c1.png">
#### 4. Upload workflow files
Upload some workflow files to `.gitea/workflows/xxx.yaml`, you can
follow the [quickstart](https://docs.github.com/en/actions/quickstart)
of GitHub Actions. Yes, Gitea Actions is compatible with GitHub Actions
in most cases, you can use the same demo:
```yaml
name: GitHub Actions Demo
run-name: ${{ github.actor }} is testing out GitHub Actions 🚀
on: [push]
jobs:
Explore-GitHub-Actions:
runs-on: ubuntu-latest
steps:
- run: echo "🎉 The job was automatically triggered by a ${{ github.event_name }} event."
- run: echo "🐧 This job is now running on a ${{ runner.os }} server hosted by GitHub!"
- run: echo "🔎 The name of your branch is ${{ github.ref }} and your repository is ${{ github.repository }}."
- name: Check out repository code
uses: actions/checkout@v3
- run: echo "💡 The ${{ github.repository }} repository has been cloned to the runner."
- run: echo "🖥️ The workflow is now ready to test your code on the runner."
- name: List files in the repository
run: |
ls ${{ github.workspace }}
- run: echo "🍏 This job's status is ${{ job.status }}."
```
If all is well, you'll see a new run in `Actions` tab:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884473-79a874bc-171b-4aaf-acd5-0241a45c3b53.png">
#### 5. Check the logs of jobs
Click a run and you'll see the logs:
<img width="1792" alt="image"
src="https://user-images.githubusercontent.com/9418365/205884800-994b0374-67f7-48ff-be9a-4c53f3141547.png">
#### 6. Go on
You can try more examples in [the
documents](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions)
of GitHub Actions, then you might find a lot of bugs.
Come on, PRs are welcome.
</details>
See also: [Feature Preview: Gitea
Actions](https://blog.gitea.io/2022/12/feature-preview-gitea-actions/)
---------
Co-authored-by: a1012112796 <1012112796@qq.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-authored-by: delvh <dev.lh@web.de>
Co-authored-by: ChristopherHX <christopher.homberger@web.de>
Co-authored-by: John Olheiser <john.olheiser@gmail.com>
2 years ago
|
|
|
ctx.Data["UnitActionsGlobalDisabled"] = unit.TypeActions.UnitGlobalDisabled()
|
|
|
|
|
|
|
|
ctx.Data["AllLangs"] = translation.AllLangs()
|
|
|
|
|
|
|
|
next.ServeHTTP(ctx.Resp, ctx.Req)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) JSONRedirect(redirect string) {
|
|
|
|
ctx.JSON(http.StatusOK, map[string]any{"redirect": redirect})
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) JSONOK() {
|
|
|
|
ctx.JSON(http.StatusOK, map[string]any{"ok": true}) // this is only a dummy response, frontend seldom uses it
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ctx *Context) JSONError(msg string) {
|
|
|
|
ctx.JSON(http.StatusBadRequest, map[string]any{"errorMessage": msg})
|
|
|
|
}
|