diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 846823abc7744..c9ea481af4bff 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -46,6 +46,9 @@ overrides: - files: ["*.config.*"] rules: import/no-unused-modules: [0] + - files: ["web_src/js/modules/fetch.js", "web_src/js/standalone/**/*"] + rules: + no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression] rules: "@eslint-community/eslint-comments/disable-enable-pair": [2] @@ -420,7 +423,7 @@ rules: no-restricted-exports: [0] no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, location, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, self, status, statusbar, stop, toolbar, top, __dirname, __filename] no-restricted-imports: [0] - no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression] + no-restricted-syntax: [2, WithStatement, ForInStatement, LabeledStatement, SequenceExpression, {selector: "CallExpression[callee.name='fetch']", message: "use modules/fetch.js instead"}] no-return-assign: [0] no-script-url: [2] no-self-assign: [2, {props: true}] diff --git a/docs/content/contributing/guidelines-frontend.en-us.md b/docs/content/contributing/guidelines-frontend.en-us.md index 921c2b0233690..0d9e510e70039 100644 --- a/docs/content/contributing/guidelines-frontend.en-us.md +++ b/docs/content/contributing/guidelines-frontend.en-us.md @@ -95,7 +95,7 @@ Some lint rules and IDEs also have warnings if the returned Promise is not handl ### Fetching data To fetch data, use the wrapper functions `GET`, `POST` etc. from `modules/fetch.js`. They -accept a `data` option for the content, will automatically set CSFR token and return a +accept a `data` option for the content, will automatically set CSRF token and return a Promise for a [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response). ### HTML Attributes and `dataset` diff --git a/docs/content/usage/actions/comparison.en-us.md b/docs/content/usage/actions/comparison.en-us.md index 64a5eff4d5a7e..caec9d3dea3c7 100644 --- a/docs/content/usage/actions/comparison.en-us.md +++ b/docs/content/usage/actions/comparison.en-us.md @@ -91,12 +91,6 @@ As a workaround, you can use [go-hashfiles](https://gitea.com/actions/go-hashfil ## Missing features -### Variables - -See [Variables](https://docs.github.com/en/actions/learn-github-actions/variables). - -It's under development. - ### Problem Matchers Problem Matchers are a way to scan the output of actions for a specified regex pattern and surface that information prominently in the UI. @@ -120,15 +114,17 @@ Pre and Post steps don't have their own section in the job log user interface. ### Downloading actions -Gitea Actions doesn't download actions from GitHub by default. -"By default" means that you don't specify the host in the `uses` field, like `uses: actions/checkout@v3`. -As a contrast, `uses: https://github.com/actions/checkout@v3` has specified host. +Previously (Pre 1.21.0), `[actions].DEFAULT_ACTIONS_URL` defaulted to `https://gitea.com`. +We have since restricted this option to only allow two values (`github` and `self`). +When set to `github`, the new default, Gitea will download non-fully-qualified actions from . +For example, if you use `uses: actions/checkout@v3`, it will download the checkout repository from . + +If you want to download an action from another git hoster, you can use an absolute URL, e.g. `uses: https://gitea.com/actions/checkout@v3`. -The missing host will be filled with `https://gitea.com` if you don't configure it. -That means `uses: actions/checkout@v3` will download the action from [gitea.com/actions/checkout](https://gitea.com/actions/checkout), instead of [github.com/actions/checkout](https://github.com/actions/checkout). +If your Gitea instance is in an intranet or a restricted area, you can set the URL to `self` to only download actions from your own instance by default. +Of course, you can still use absolute URLs in workflows. -As mentioned, it's configurable. -If you want your runners to download actions from GitHub or your own Gitea instance by default, you can configure it by setting `[actions].DEFAULT_ACTIONS_URL`. See [Configuration Cheat Sheet](administration/config-cheat-sheet.md#actions-actions). +More details about the `[actions].DEFAULT_ACTIONS_URL` configuration can be found in the [Configuration Cheat Sheet](administration/config-cheat-sheet.md#actions-actions)。 ### Context availability diff --git a/docs/content/usage/actions/comparison.zh-cn.md b/docs/content/usage/actions/comparison.zh-cn.md index cfea7970f7d0e..5dae75a44ba3a 100644 --- a/docs/content/usage/actions/comparison.zh-cn.md +++ b/docs/content/usage/actions/comparison.zh-cn.md @@ -120,15 +120,13 @@ Gitea Actions目前不支持此功能。 ### 下载Actions -Gitea Actions默认不从GitHub下载Actions。 -"默认" 意味着您在`uses` 字段中不指定主机,如`uses: actions/checkout@v3`。 -相反,`uses: https://github.com/actions/checkout@v3`是有指定主机的。 +当 `[actions].DEFAULT_ACTIONS_URL` 保持默认值为 `github` 时,Gitea将会从 https://github.com 下载相对路径的actions。比如: +如果你使用 `uses: actions/checkout@v3`,Gitea将会从 https://github.com/actions/checkout.git 下载这个 actions 项目。 +如果你想要从另外一个 Git服务下载actions,你只需要使用绝对URL `uses: https://gitea.com/actions/checkout@v3` 来下载。 -如果您不进行配置,缺失的主机将填充为`https://gitea.com`。 -这意味着`uses: actions/checkout@v3`将从[gitea.com/actions/checkout](https://gitea.com/actions/checkout)下载该Action,而不是[github.com/actions/checkout](https://github.com/actions/checkout)。 +如果你的 Gitea 实例是部署在一个互联网限制的网络中,有可以使用绝对地址来下载 actions。你也可以讲配置项修改为 `[actions].DEFAULT_ACTIONS_URL = self`。这样所有的相对路径的actions引用,将不再会从 github.com 去下载,而会从这个 Gitea 实例自己的仓库中去下载。例如: `uses: actions/checkout@v3` 将会从 `[server].ROOT_URL`/actions/checkout.git 这个地址去下载 actions。 -正如前面提到的,这是可配置的。 -如果您希望您的运行程序默认从GitHub或您自己的Gitea实例下载动作,您可以通过设置`[actions].DEFAULT_ACTIONS_URL`进行配置。请参阅[配置备忘单](administration/config-cheat-sheet.md#actions-actions)。 +设置`[actions].DEFAULT_ACTIONS_URL`进行配置。请参阅[配置备忘单](administration/config-cheat-sheet.md#actions-actions)。 ### 上下文可用性 diff --git a/docs/content/usage/actions/faq.en-us.md b/docs/content/usage/actions/faq.en-us.md index 031509a033e1f..1d59872936a46 100644 --- a/docs/content/usage/actions/faq.en-us.md +++ b/docs/content/usage/actions/faq.en-us.md @@ -180,3 +180,6 @@ For events supported only by GitHub, see GitHub's [documentation](https://docs.g | pull_request_review_comment | `created`, `edited` | | release | `published`, `edited` | | registry_package | `published` | + +> For `pull_request` events, in [GitHub Actions](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request), the `ref` is `refs/pull/:prNumber/merge`, which is a reference to the merge commit preview. However, Gitea has no such reference. +> Therefore, the `ref` in Gitea Actions is `refs/pull/:prNumber/head`, which points to the head of pull request rather than the preview of the merge commit. diff --git a/docs/content/usage/actions/faq.zh-cn.md b/docs/content/usage/actions/faq.zh-cn.md index f5dc8e179bdd1..7bb79d02fc4a8 100644 --- a/docs/content/usage/actions/faq.zh-cn.md +++ b/docs/content/usage/actions/faq.zh-cn.md @@ -180,3 +180,6 @@ defaults: | pull_request_review_comment | `created`, `edited` | | release | `published`, `edited` | | registry_package | `published` | + +> 对于 `pull_request` 事件,在 [GitHub Actions](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request) 中 `ref` 是 `refs/pull/:prNumber/merge`,它指向这个拉取请求合并提交的一个预览。但是 Gitea 没有这种 reference。 +> 因此,Gitea Actions 中 `ref` 是 `refs/pull/:prNumber/head`,它指向这个拉取请求的头分支而不是合并提交的预览。 diff --git a/models/migrations/v1_21/v276.go b/models/migrations/v1_21/v276.go index 8746c8851e63a..ed1bc3bda5241 100644 --- a/models/migrations/v1_21/v276.go +++ b/models/migrations/v1_21/v276.go @@ -41,6 +41,8 @@ func migratePullMirrors(x *xorm.Engine) error { ID int64 `xorm:"pk autoincr"` RepoID int64 `xorm:"INDEX"` RemoteAddress string `xorm:"VARCHAR(2048)"` + RepoOwner string + RepoName string } sess := x.NewSession() @@ -59,7 +61,9 @@ func migratePullMirrors(x *xorm.Engine) error { for { var mirrors []Mirror - if err := sess.Limit(limit, start).Find(&mirrors); err != nil { + if err := sess.Select("mirror.id, mirror.repo_id, mirror.remote_address, repository.owner_name as repo_owner, repository.name as repo_name"). + Join("INNER", "repository", "repository.id = mirror.repo_id"). + Limit(limit, start).Find(&mirrors); err != nil { return err } @@ -69,7 +73,7 @@ func migratePullMirrors(x *xorm.Engine) error { start += len(mirrors) for _, m := range mirrors { - remoteAddress, err := getRemoteAddress(sess, m.RepoID, "origin") + remoteAddress, err := getRemoteAddress(m.RepoOwner, m.RepoName, "origin") if err != nil { return err } @@ -100,6 +104,8 @@ func migratePushMirrors(x *xorm.Engine) error { RepoID int64 `xorm:"INDEX"` RemoteName string RemoteAddress string `xorm:"VARCHAR(2048)"` + RepoOwner string + RepoName string } sess := x.NewSession() @@ -118,7 +124,9 @@ func migratePushMirrors(x *xorm.Engine) error { for { var mirrors []PushMirror - if err := sess.Limit(limit, start).Find(&mirrors); err != nil { + if err := sess.Select("push_mirror.id, push_mirror.repo_id, push_mirror.remote_name, push_mirror.remote_address, repository.owner_name as repo_owner, repository.name as repo_name"). + Join("INNER", "repository", "repository.id = push_mirror.repo_id"). + Limit(limit, start).Find(&mirrors); err != nil { return err } @@ -128,7 +136,7 @@ func migratePushMirrors(x *xorm.Engine) error { start += len(mirrors) for _, m := range mirrors { - remoteAddress, err := getRemoteAddress(sess, m.RepoID, m.RemoteName) + remoteAddress, err := getRemoteAddress(m.RepoOwner, m.RepoName, m.RemoteName) if err != nil { return err } @@ -153,25 +161,12 @@ func migratePushMirrors(x *xorm.Engine) error { return sess.Commit() } -func getRemoteAddress(sess *xorm.Session, repoID int64, remoteName string) (string, error) { - var ownerName string - var repoName string - has, err := sess. - Table("repository"). - Cols("owner_name", "lower_name"). - Where("id=?", repoID). - Get(&ownerName, &repoName) - if err != nil { - return "", err - } else if !has { - return "", fmt.Errorf("repository [%v] not found", repoID) - } - +func getRemoteAddress(ownerName, repoName, remoteName string) (string, error) { repoPath := filepath.Join(setting.RepoRootPath, strings.ToLower(ownerName), strings.ToLower(repoName)+".git") remoteURL, err := git.GetRemoteAddress(context.Background(), repoPath, remoteName) if err != nil { - return "", err + return "", fmt.Errorf("get remote %s's address of %s/%s failed: %v", remoteName, ownerName, repoName, err) } u, err := giturl.Parse(remoteURL) diff --git a/modules/lfs/filesystem_client.go b/modules/lfs/filesystem_client.go index 835551e00c6c9..3503a9effc40b 100644 --- a/modules/lfs/filesystem_client.go +++ b/modules/lfs/filesystem_client.go @@ -15,7 +15,7 @@ import ( // FilesystemClient is used to read LFS data from a filesystem path type FilesystemClient struct { - lfsdir string + lfsDir string } // BatchSize returns the preferred size of batchs to process @@ -25,16 +25,12 @@ func (c *FilesystemClient) BatchSize() int { func newFilesystemClient(endpoint *url.URL) *FilesystemClient { path, _ := util.FileURLToPath(endpoint) - - lfsdir := filepath.Join(path, "lfs", "objects") - - client := &FilesystemClient{lfsdir} - - return client + lfsDir := filepath.Join(path, "lfs", "objects") + return &FilesystemClient{lfsDir} } func (c *FilesystemClient) objectPath(oid string) string { - return filepath.Join(c.lfsdir, oid[0:2], oid[2:4], oid) + return filepath.Join(c.lfsDir, oid[0:2], oid[2:4], oid) } // Download reads the specific LFS object from the target path diff --git a/modules/lfs/http_client.go b/modules/lfs/http_client.go index ec0d6269bd56a..de0b1e4fede49 100644 --- a/modules/lfs/http_client.go +++ b/modules/lfs/http_client.go @@ -8,6 +8,7 @@ import ( "context" "errors" "fmt" + "io" "net/http" "net/url" "strings" @@ -17,7 +18,7 @@ import ( "code.gitea.io/gitea/modules/proxy" ) -const batchSize = 20 +const httpBatchSize = 20 // HTTPClient is used to communicate with the LFS server // https://github.com/git-lfs/git-lfs/blob/main/docs/api/batch.md @@ -29,7 +30,7 @@ type HTTPClient struct { // BatchSize returns the preferred size of batchs to process func (c *HTTPClient) BatchSize() int { - return batchSize + return httpBatchSize } func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient { @@ -43,28 +44,25 @@ func newHTTPClient(endpoint *url.URL, httpTransport *http.Transport) *HTTPClient Transport: httpTransport, } + basic := &BasicTransferAdapter{hc} client := &HTTPClient{ - client: hc, - endpoint: strings.TrimSuffix(endpoint.String(), "/"), - transfers: make(map[string]TransferAdapter), + client: hc, + endpoint: strings.TrimSuffix(endpoint.String(), "/"), + transfers: map[string]TransferAdapter{ + basic.Name(): basic, + }, } - basic := &BasicTransferAdapter{hc} - - client.transfers[basic.Name()] = basic - return client } func (c *HTTPClient) transferNames() []string { keys := make([]string, len(c.transfers)) - i := 0 for k := range c.transfers { keys[i] = k i++ } - return keys } @@ -74,7 +72,6 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin url := fmt.Sprintf("%s/objects/batch", c.endpoint) request := &BatchRequest{operation, c.transferNames(), nil, objects} - payload := new(bytes.Buffer) err := json.NewEncoder(payload).Encode(request) if err != nil { @@ -82,32 +79,17 @@ func (c *HTTPClient) batch(ctx context.Context, operation string, objects []Poin return nil, err } - log.Trace("Calling: %s", url) - - req, err := http.NewRequestWithContext(ctx, "POST", url, payload) + req, err := createRequest(ctx, http.MethodPost, url, map[string]string{"Content-Type": MediaType}, payload) if err != nil { - log.Error("Error creating request: %v", err) return nil, err } - req.Header.Set("Content-type", MediaType) - req.Header.Set("Accept", MediaType) - res, err := c.client.Do(req) + res, err := performRequest(ctx, c.client, req) if err != nil { - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - log.Error("Error while processing request: %v", err) return nil, err } defer res.Body.Close() - if res.StatusCode != http.StatusOK { - return nil, fmt.Errorf("Unexpected server response: %s", res.Status) - } - var response BatchResponse err = json.NewDecoder(res.Body).Decode(&response) if err != nil { @@ -177,7 +159,7 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc link, ok := object.Actions["upload"] if !ok { log.Debug("%+v", object) - return errors.New("Missing action 'upload'") + return errors.New("missing action 'upload'") } content, err := uc(object.Pointer, nil) @@ -187,8 +169,6 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc err = transferAdapter.Upload(ctx, link, object.Pointer, content) - content.Close() - if err != nil { return err } @@ -203,7 +183,7 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc link, ok := object.Actions["download"] if !ok { log.Debug("%+v", object) - return errors.New("Missing action 'download'") + return errors.New("missing action 'download'") } content, err := transferAdapter.Download(ctx, link) @@ -219,3 +199,59 @@ func (c *HTTPClient) performOperation(ctx context.Context, objects []Pointer, dc return nil } + +// createRequest creates a new request, and sets the headers. +func createRequest(ctx context.Context, method, url string, headers map[string]string, body io.Reader) (*http.Request, error) { + log.Trace("createRequest: %s", url) + req, err := http.NewRequestWithContext(ctx, method, url, body) + if err != nil { + log.Error("Error creating request: %v", err) + return nil, err + } + + for key, value := range headers { + req.Header.Set(key, value) + } + req.Header.Set("Accept", MediaType) + + return req, nil +} + +// performRequest sends a request, optionally performs a callback on the request and returns the response. +// If the status code is 200, the response is returned, and it will contain a non-nil Body. +// Otherwise, it will return an error, and the Body will be nil or closed. +func performRequest(ctx context.Context, client *http.Client, req *http.Request) (*http.Response, error) { + log.Trace("performRequest: %s", req.URL) + res, err := client.Do(req) + if err != nil { + select { + case <-ctx.Done(): + return res, ctx.Err() + default: + } + log.Error("Error while processing request: %v", err) + return res, err + } + + if res.StatusCode != http.StatusOK { + defer res.Body.Close() + return res, handleErrorResponse(res) + } + + return res, nil +} + +func handleErrorResponse(resp *http.Response) error { + var er ErrorResponse + err := json.NewDecoder(resp.Body).Decode(&er) + if err != nil { + if err == io.EOF { + return io.ErrUnexpectedEOF + } + log.Error("Error decoding json: %v", err) + return err + } + + log.Trace("ErrorResponse: %v", er) + return errors.New(er.Message) +} diff --git a/modules/lfs/http_client_test.go b/modules/lfs/http_client_test.go index cb71b9008a9d1..7459d9c0c97e8 100644 --- a/modules/lfs/http_client_test.go +++ b/modules/lfs/http_client_test.go @@ -177,7 +177,7 @@ func TestHTTPClientDownload(t *testing.T) { // case 0 { endpoint: "https://status-not-ok.io", - expectederror: "Unexpected server response: ", + expectederror: io.ErrUnexpectedEOF.Error(), }, // case 1 { @@ -207,7 +207,7 @@ func TestHTTPClientDownload(t *testing.T) { // case 6 { endpoint: "https://empty-actions-map.io", - expectederror: "Missing action 'download'", + expectederror: "missing action 'download'", }, // case 7 { @@ -217,27 +217,28 @@ func TestHTTPClientDownload(t *testing.T) { // case 8 { endpoint: "https://upload-actions-map.io", - expectederror: "Missing action 'download'", + expectederror: "missing action 'download'", }, // case 9 { endpoint: "https://verify-actions-map.io", - expectederror: "Missing action 'download'", + expectederror: "missing action 'download'", }, // case 10 { endpoint: "https://unknown-actions-map.io", - expectederror: "Missing action 'download'", + expectederror: "missing action 'download'", }, } for n, c := range cases { client := &HTTPClient{ - client: hc, - endpoint: c.endpoint, - transfers: make(map[string]TransferAdapter), + client: hc, + endpoint: c.endpoint, + transfers: map[string]TransferAdapter{ + "dummy": dummy, + }, } - client.transfers["dummy"] = dummy err := client.Download(context.Background(), []Pointer{p}, func(p Pointer, content io.ReadCloser, objectError error) error { if objectError != nil { @@ -284,7 +285,7 @@ func TestHTTPClientUpload(t *testing.T) { // case 0 { endpoint: "https://status-not-ok.io", - expectederror: "Unexpected server response: ", + expectederror: io.ErrUnexpectedEOF.Error(), }, // case 1 { @@ -319,7 +320,7 @@ func TestHTTPClientUpload(t *testing.T) { // case 7 { endpoint: "https://download-actions-map.io", - expectederror: "Missing action 'upload'", + expectederror: "missing action 'upload'", }, // case 8 { @@ -329,22 +330,23 @@ func TestHTTPClientUpload(t *testing.T) { // case 9 { endpoint: "https://verify-actions-map.io", - expectederror: "Missing action 'upload'", + expectederror: "missing action 'upload'", }, // case 10 { endpoint: "https://unknown-actions-map.io", - expectederror: "Missing action 'upload'", + expectederror: "missing action 'upload'", }, } for n, c := range cases { client := &HTTPClient{ - client: hc, - endpoint: c.endpoint, - transfers: make(map[string]TransferAdapter), + client: hc, + endpoint: c.endpoint, + transfers: map[string]TransferAdapter{ + "dummy": dummy, + }, } - client.transfers["dummy"] = dummy err := client.Upload(context.Background(), []Pointer{p}, func(p Pointer, objectError error) (io.ReadCloser, error) { return io.NopCloser(new(bytes.Buffer)), objectError diff --git a/modules/lfs/pointer.go b/modules/lfs/pointer.go index d7653e836c911..3e5bb8f91d2fa 100644 --- a/modules/lfs/pointer.go +++ b/modules/lfs/pointer.go @@ -29,10 +29,10 @@ const ( var ( // ErrMissingPrefix occurs if the content lacks the LFS prefix - ErrMissingPrefix = errors.New("Content lacks the LFS prefix") + ErrMissingPrefix = errors.New("content lacks the LFS prefix") // ErrInvalidStructure occurs if the content has an invalid structure - ErrInvalidStructure = errors.New("Content has an invalid structure") + ErrInvalidStructure = errors.New("content has an invalid structure") // ErrInvalidOIDFormat occurs if the oid has an invalid format ErrInvalidOIDFormat = errors.New("OID has an invalid format") diff --git a/modules/lfs/transferadapter.go b/modules/lfs/transferadapter.go index 649497aabb36d..d425b91946d17 100644 --- a/modules/lfs/transferadapter.go +++ b/modules/lfs/transferadapter.go @@ -6,8 +6,6 @@ package lfs import ( "bytes" "context" - "errors" - "fmt" "io" "net/http" @@ -15,7 +13,7 @@ import ( "code.gitea.io/gitea/modules/log" ) -// TransferAdapter represents an adapter for downloading/uploading LFS objects +// TransferAdapter represents an adapter for downloading/uploading LFS objects. type TransferAdapter interface { Name() string Download(ctx context.Context, l *Link) (io.ReadCloser, error) @@ -23,41 +21,48 @@ type TransferAdapter interface { Verify(ctx context.Context, l *Link, p Pointer) error } -// BasicTransferAdapter implements the "basic" adapter +// BasicTransferAdapter implements the "basic" adapter. type BasicTransferAdapter struct { client *http.Client } -// Name returns the name of the adapter +// Name returns the name of the adapter. func (a *BasicTransferAdapter) Name() string { return "basic" } -// Download reads the download location and downloads the data +// Download reads the download location and downloads the data. func (a *BasicTransferAdapter) Download(ctx context.Context, l *Link) (io.ReadCloser, error) { - resp, err := a.performRequest(ctx, "GET", l, nil, nil) + req, err := createRequest(ctx, http.MethodGet, l.Href, l.Header, nil) + if err != nil { + return nil, err + } + resp, err := performRequest(ctx, a.client, req) if err != nil { return nil, err } return resp.Body, nil } -// Upload sends the content to the LFS server +// Upload sends the content to the LFS server. func (a *BasicTransferAdapter) Upload(ctx context.Context, l *Link, p Pointer, r io.Reader) error { - _, err := a.performRequest(ctx, "PUT", l, r, func(req *http.Request) { - if len(req.Header.Get("Content-Type")) == 0 { - req.Header.Set("Content-Type", "application/octet-stream") - } - - if req.Header.Get("Transfer-Encoding") == "chunked" { - req.TransferEncoding = []string{"chunked"} - } + req, err := createRequest(ctx, http.MethodPut, l.Href, l.Header, r) + if err != nil { + return err + } + if req.Header.Get("Content-Type") == "" { + req.Header.Set("Content-Type", "application/octet-stream") + } + if req.Header.Get("Transfer-Encoding") == "chunked" { + req.TransferEncoding = []string{"chunked"} + } + req.ContentLength = p.Size - req.ContentLength = p.Size - }) + res, err := performRequest(ctx, a.client, req) if err != nil { return err } + defer res.Body.Close() return nil } @@ -69,66 +74,15 @@ func (a *BasicTransferAdapter) Verify(ctx context.Context, l *Link, p Pointer) e return err } - _, err = a.performRequest(ctx, "POST", l, bytes.NewReader(b), func(req *http.Request) { - req.Header.Set("Content-Type", MediaType) - }) + req, err := createRequest(ctx, http.MethodPost, l.Href, l.Header, bytes.NewReader(b)) if err != nil { return err } - return nil -} - -func (a *BasicTransferAdapter) performRequest(ctx context.Context, method string, l *Link, body io.Reader, callback func(*http.Request)) (*http.Response, error) { - log.Trace("Calling: %s %s", method, l.Href) - - req, err := http.NewRequestWithContext(ctx, method, l.Href, body) - if err != nil { - log.Error("Error creating request: %v", err) - return nil, err - } - for key, value := range l.Header { - req.Header.Set(key, value) - } - req.Header.Set("Accept", MediaType) - - if callback != nil { - callback(req) - } - - res, err := a.client.Do(req) + req.Header.Set("Content-Type", MediaType) + res, err := performRequest(ctx, a.client, req) if err != nil { - select { - case <-ctx.Done(): - return res, ctx.Err() - default: - } - log.Error("Error while processing request: %v", err) - return res, err - } - - if res.StatusCode != http.StatusOK { - return res, handleErrorResponse(res) - } - - return res, nil -} - -func handleErrorResponse(resp *http.Response) error { - defer resp.Body.Close() - - er, err := decodeResponseError(resp.Body) - if err != nil { - return fmt.Errorf("Request failed with status %s", resp.Status) - } - log.Trace("ErrorRespone: %v", er) - return errors.New(er.Message) -} - -func decodeResponseError(r io.Reader) (ErrorResponse, error) { - var er ErrorResponse - err := json.NewDecoder(r).Decode(&er) - if err != nil { - log.Error("Error decoding json: %v", err) + return err } - return er, err + defer res.Body.Close() + return nil } diff --git a/modules/util/path.go b/modules/util/path.go index 58258560dded3..e8537fb6b9b7e 100644 --- a/modules/util/path.go +++ b/modules/util/path.go @@ -225,6 +225,7 @@ func isOSWindows() bool { var driveLetterRegexp = regexp.MustCompile("/[A-Za-z]:/") // FileURLToPath extracts the path information from a file://... url. +// It returns an error only if the URL is not a file URL. func FileURLToPath(u *url.URL) (string, error) { if u.Scheme != "file" { return "", errors.New("URL scheme is not 'file': " + u.String()) diff --git a/options/license/BSD-3-Clause-Sun b/options/license/BSD-3-Clause-Sun new file mode 100644 index 0000000000000..1d86449d90a62 --- /dev/null +++ b/options/license/BSD-3-Clause-Sun @@ -0,0 +1,29 @@ +Copyright (c) 2001-2013 Oracle and/or its affiliates. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistribution in binary form must reproduct the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +Neither the name of Sun Microsystems, Inc. or the names of +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +This software is provided "AS IS," without a warranty of any kind. ALL +EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, +INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND +ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES OR LIABILITIES +SUFFERED BY LICENSEE AS A RESULT OF OR RELATING TO USE, MODIFICATION +OR DISTRIBUTION OF THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL +SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, +OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR +PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY OF +LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, +EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. diff --git a/options/license/BSD-Systemics b/options/license/BSD-Systemics new file mode 100644 index 0000000000000..6ca8a26c33066 --- /dev/null +++ b/options/license/BSD-Systemics @@ -0,0 +1,39 @@ +Copyright (C) 1995, 1996 Systemics Ltd (http://www.systemics.com/) +All rights reserved. + +This library and applications are FREE FOR COMMERCIAL AND NON-COMMERCIAL USE +as long as the following conditions are adhered to. + +Copyright remains with Systemics Ltd, and as such any Copyright notices +in the code are not to be removed. If this code is used in a product, +Systemics should be given attribution as the author of the parts used. +This can be in the form of a textual message at program startup or +in documentation (online or textual) provided with the package. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by Systemics Ltd (http://www.systemics.com/) + +THIS SOFTWARE IS PROVIDED BY SYSTEMICS LTD ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. + +The licence and distribution terms for any publically available version or +derivative of this code cannot be changed. i.e. this code cannot simply be +copied and put under another distribution licence [including the GNU Public Licence.] diff --git a/options/license/Crossword b/options/license/Crossword index 6be940c33bb37..35d95a79d7186 100644 --- a/options/license/Crossword +++ b/options/license/Crossword @@ -1,5 +1,5 @@ Copyright (C) 1995-2009 Gerd Neugebauer cwpuzzle.dtx is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY. No author or distributor accepts responsibility to anyone for the consequences of using it or for whether it serves any particular purpose or works at all, unless he says so in writing. -. + Everyone is granted permission to copy, modify and redistribute cwpuzzle.dtx, provided this copyright notice is preserved and any modifications are indicated. diff --git a/options/license/DL-DE-ZERO-2.0 b/options/license/DL-DE-ZERO-2.0 new file mode 100644 index 0000000000000..7daacde13d339 --- /dev/null +++ b/options/license/DL-DE-ZERO-2.0 @@ -0,0 +1,25 @@ +DL-DE->Zero-2.0 +Datenlizenz Deutschland – Zero – Version 2.0 + +Jede Nutzung ist ohne Einschränkungen oder Bedingungen zulässig. + +Die bereitgestellten Daten und Metadaten dürfen für die kommerzielle und nicht kommerzielle Nutzung insbesondere + + vervielfältigt, ausgedruckt, präsentiert, verändert, bearbeitet sowie an Dritte übermittelt werden; + mit eigenen Daten und Daten Anderer zusammengeführt und zu selbständigen neuen Datensätzen verbunden werden; + in interne und externe Geschäftsprozesse, Produkte und Anwendungen in öffentlichen und nicht öffentlichen elektronischen Netzwerken eingebunden werden. + + +Data licence Germany – Zero – version 2.0 + +Any use is permitted without restrictions or conditions. + +The data and meta-data provided may, for commercial and non-commercial use, in particular + + be copied, printed, presented, altered, processed and transmitted to third parties; + be merged with own data and with the data of others and be combined to form new and independent datasets; + be integrated in internal and external business processes, products and applications in public and non-public electronic networks. + + + +URL: https://www.govdata.de/dl-de/zero-2-0 diff --git a/options/license/FBM b/options/license/FBM new file mode 100644 index 0000000000000..68d9149b90327 --- /dev/null +++ b/options/license/FBM @@ -0,0 +1,6 @@ +Portions of this code Copyright (C) 1989 by Michael Mauldin. +Permission is granted to use this file in whole or in +part for any purpose, educational, recreational or commercial, +provided that this copyright notice is retained unchanged. +This software is available to all free of charge by anonymous +FTP and in the UUNET archives. diff --git a/options/license/Ferguson-Twofish b/options/license/Ferguson-Twofish new file mode 100644 index 0000000000000..43bb00c3ee7fe --- /dev/null +++ b/options/license/Ferguson-Twofish @@ -0,0 +1,15 @@ + The author hereby grants a perpetual license to everybody to + use this code for any purpose as long as the copyright message is included + in the source code of this or any derived work. + + Yes, this means that you, your company, your club, and anyone else + can use this code anywhere you want. You can change it and distribute it + under the GPL, include it in your commercial product without releasing + the source code, put it on the web, etc. + The only thing you cannot do is remove my copyright message, + or distribute any source code based on this implementation that does not + include my copyright message. + + I appreciate a mention in the documentation or credits, + but I understand if that is difficult to do. + I also appreciate it if you tell me where and why you used my code. diff --git a/options/license/MPEG-SSG b/options/license/MPEG-SSG new file mode 100644 index 0000000000000..a0b6f4ffff537 --- /dev/null +++ b/options/license/MPEG-SSG @@ -0,0 +1,23 @@ +Copyright (C) 1994, MPEG Software Simulation Group. All Rights Reserved. */ + +Disclaimer of Warranty + +These software programs are available to the user without any license fee or +royalty on an "as is" basis. The MPEG Software Simulation Group disclaims +any and all warranties, whether express, implied, or statuary, including any +implied warranties or merchantability or of fitness for a particular +purpose. In no event shall the copyright-holder be liable for any +incidental, punitive, or consequential damages of any kind whatsoever +arising from the use of these programs. + +This disclaimer of warranty extends to the user of these programs and user's +customers, employees, agents, transferees, successors, and assigns. + +The MPEG Software Simulation Group does not represent or warrant that the +programs furnished hereunder are free of infringement of any third-party +patents. + +Commercial implementations of MPEG-1 and MPEG-2 video, including shareware, +are subject to royalty fees to patent holders. Many of these patents are +general enough such that they are unavoidable regardless of implementation +design. diff --git a/options/license/NPL-1.0 b/options/license/NPL-1.0 index 7a5030e9f7124..65983791a2c39 100644 --- a/options/license/NPL-1.0 +++ b/options/license/NPL-1.0 @@ -8,20 +8,20 @@ NETSCAPE PUBLIC LICENSE Version 1.0 1.3. ``Covered Code'' means the Original Code or Modifications or the combination of the Original Code and Modifications, in each case including portions thereof. 1.4. ``Electronic Distribution Mechanism'' means a mechanism generally accepted in the software development community for the electronic transfer of data. 1.5. ``Executable'' means Covered Code in any form other than Source Code. - 1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required byExhibit A. + 1.6. ``Initial Developer'' means the individual or entity identified as the Initial Developer in the Source Code notice required by Exhibit A. 1.7. ``Larger Work'' means a work which combines Covered Code or portions thereof with code not governed by the terms of this License. 1.8. ``License'' means this document. 1.9. ``Modifications'' means any addition to or deletion from the substance or structure of either the Original Code or any previous Modifications. When Covered Code is released as a series of files, a Modification is: A. Any addition to or deletion from the contents of a file containing Original Code or previous Modifications. B. Any new file that contains any part of the Original Code or previous Modifications. - 1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required byExhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. + 1.10. ``Original Code'' means Source Code of computer software code which is described in the Source Code notice required by Exhibit A as Original Code, and which, at the time of its release under this License is not already Covered Code governed by this License. 1.11. ``Source Code'' means the preferred form of the Covered Code for making modifications to it, including all modules it contains, plus any associated interface definition files, scripts used to control compilation and installation of an Executable, or a list of source code differential comparisons against either the Original Code or another well known, available Covered Code of the Contributor's choice. The Source Code can be in a compressed or archival form, provided the appropriate decompression or de-archiving software is widely available for no charge. 1.12. ``You'' means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License or a future version of this License issued under Section 6.1. For legal entities, ``You'' includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, ``control'' means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of fifty percent (50%) or more of the outstanding shares or beneficial ownership of such entity. 2. Source Code License. 2.1. The Initial Developer Grant. The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: - a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and + (a) to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, or as part of a Larger Work; and (b) under patents now or hereafter owned or controlled by Initial Developer, to make, have made, use and sell (``Utilize'') the Original Code (or portions thereof), but solely to the extent that any such patent is reasonably necessary to enable You to Utilize the Original Code (or portions thereof) and not to any greater extent that may be necessary to Utilize further Modifications or combinations. 2.2. Contributor Grant. Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: diff --git a/options/license/pnmstitch b/options/license/pnmstitch new file mode 100644 index 0000000000000..cb9dc762d918d --- /dev/null +++ b/options/license/pnmstitch @@ -0,0 +1,23 @@ +Copyright (c) 2002 Mark Salyzyn +All rights reserved. + +TERMS AND CONDITIONS OF USE + +Redistribution and use in source form, with or without modification, are +permitted provided that redistributions of source code must retain the +above copyright notice, this list of conditions and the following +disclaimer. + +This software is provided `as is' by Mark Salyzyn and any express or implied +warranties, including, but not limited to, the implied warranties of +merchantability and fitness for a particular purpose, are disclaimed. In no +event shall Mark Salyzyn be liable for any direct, indirect, incidental, +special, exemplary or consequential damages (including, but not limited to, +procurement of substitute goods or services; loss of use, data, or profits; +or business interruptions) however caused and on any theory of liability, +whether in contract, strict liability, or tort (including negligence or +otherwise) arising in any way out of the use of this software, even if +advised of the possibility of such damage. + +Any restrictions or encumberances added to this source code or derivitives, +is prohibited. diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index e11d5167aa769..74b8931de88ae 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -123,6 +123,8 @@ unpin = Unpin artifacts = Artifacts +archived = Archived + concept_system_global = Global concept_user_individual = Individual concept_code_repository = Repository @@ -317,7 +319,6 @@ filter_by_team_repositories = Filter by team repositories feed_of = Feed of "%s" show_archived = Archived -archived = Archived show_both_archived_unarchived = Showing both archived and unarchived show_only_archived = Showing only archived show_only_unarchived = Showing only unarchived diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index ca74a23a4b89e..763d56ecd2622 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -367,6 +367,16 @@ func reqOwner() func(ctx *context.APIContext) { } } +// reqSelfOrAdmin doer should be the same as the contextUser or site admin +func reqSelfOrAdmin() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if !ctx.IsUserSiteAdmin() && ctx.ContextUser != ctx.Doer { + ctx.Error(http.StatusForbidden, "reqSelfOrAdmin", "doer should be the site admin or be same as the contextUser") + return + } + } +} + // reqAdmin user should be an owner or a collaborator with admin write of a repository, or site admin func reqAdmin() func(ctx *context.APIContext) { return func(ctx *context.APIContext) { @@ -705,7 +715,10 @@ func buildAuthGroup() *auth.Group { if setting.Service.EnableReverseProxyAuthAPI { group.Add(&auth.ReverseProxy{}) } - specialAdd(group) + + if setting.IsWindows && auth_model.IsSSPIEnabled() { + group.Add(&auth.SSPI{}) // it MUST be the last, see the comment of SSPI + } return group } @@ -907,7 +920,7 @@ func Routes() *web.Route { m.Combo("").Get(user.ListAccessTokens). Post(bind(api.CreateAccessTokenOption{}), reqToken(), user.CreateAccessToken) m.Combo("/{id}").Delete(reqToken(), user.DeleteAccessToken) - }, reqBasicOrRevProxyAuth()) + }, reqSelfOrAdmin(), reqBasicOrRevProxyAuth()) m.Get("/activities/feeds", user.ListUserActivityFeeds) }, context_service.UserAssignmentAPI()) diff --git a/routers/api/v1/auth.go b/routers/api/v1/auth.go deleted file mode 100644 index e44271ba148aa..0000000000000 --- a/routers/api/v1/auth.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build !windows - -package v1 - -import auth_service "code.gitea.io/gitea/services/auth" - -func specialAdd(group *auth_service.Group) {} diff --git a/routers/api/v1/auth_windows.go b/routers/api/v1/auth_windows.go deleted file mode 100644 index 3514e21baac49..0000000000000 --- a/routers/api/v1/auth_windows.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package v1 - -import ( - "code.gitea.io/gitea/models/auth" - auth_service "code.gitea.io/gitea/services/auth" -) - -// specialAdd registers the SSPI auth method as the last method in the list. -// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation -// fails (or if negotiation should continue), which would prevent other authentication methods -// to execute at all. -func specialAdd(group *auth_service.Group) { - if auth.IsSSPIEnabled() { - group.Add(&auth_service.SSPI{}) - } -} diff --git a/routers/api/v1/user/app.go b/routers/api/v1/user/app.go index e512ba9e4bd96..6972931abc69f 100644 --- a/routers/api/v1/user/app.go +++ b/routers/api/v1/user/app.go @@ -43,8 +43,10 @@ func ListAccessTokens(ctx *context.APIContext) { // responses: // "200": // "$ref": "#/responses/AccessTokenList" + // "403": + // "$ref": "#/responses/forbidden" - opts := auth_model.ListAccessTokensOptions{UserID: ctx.Doer.ID, ListOptions: utils.GetListOptions(ctx)} + opts := auth_model.ListAccessTokensOptions{UserID: ctx.ContextUser.ID, ListOptions: utils.GetListOptions(ctx)} count, err := auth_model.CountAccessTokens(ctx, opts) if err != nil { @@ -95,11 +97,13 @@ func CreateAccessToken(ctx *context.APIContext) { // "$ref": "#/responses/AccessToken" // "400": // "$ref": "#/responses/error" + // "403": + // "$ref": "#/responses/forbidden" form := web.GetForm(ctx).(*api.CreateAccessTokenOption) t := &auth_model.AccessToken{ - UID: ctx.Doer.ID, + UID: ctx.ContextUser.ID, Name: form.Name, } @@ -153,6 +157,8 @@ func DeleteAccessToken(ctx *context.APIContext) { // responses: // "204": // "$ref": "#/responses/empty" + // "403": + // "$ref": "#/responses/forbidden" // "404": // "$ref": "#/responses/notFound" // "422": @@ -164,7 +170,7 @@ func DeleteAccessToken(ctx *context.APIContext) { if tokenID == 0 { tokens, err := auth_model.ListAccessTokens(ctx, auth_model.ListAccessTokensOptions{ Name: token, - UserID: ctx.Doer.ID, + UserID: ctx.ContextUser.ID, }) if err != nil { ctx.Error(http.StatusInternalServerError, "ListAccessTokens", err) diff --git a/routers/web/auth.go b/routers/web/auth.go deleted file mode 100644 index 1ca860ecc8dea..0000000000000 --- a/routers/web/auth.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -//go:build !windows - -package web - -import auth_service "code.gitea.io/gitea/services/auth" - -func specialAdd(group *auth_service.Group) {} diff --git a/routers/web/auth_windows.go b/routers/web/auth_windows.go deleted file mode 100644 index 3125d7ce9a74b..0000000000000 --- a/routers/web/auth_windows.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2022 The Gitea Authors. All rights reserved. -// SPDX-License-Identifier: MIT - -package web - -import ( - "code.gitea.io/gitea/models/auth" - auth_service "code.gitea.io/gitea/services/auth" -) - -// specialAdd registers the SSPI auth method as the last method in the list. -// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation -// fails (or if negotiation should continue), which would prevent other authentication methods -// to execute at all. -func specialAdd(group *auth_service.Group) { - if auth.IsSSPIEnabled() { - group.Add(&auth_service.SSPI{}) - } -} diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 71d10ab4c1251..d9a0124020c12 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -77,8 +77,9 @@ func userProfile(ctx *context.Context) { func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGitRepo *git.Repository, profileReadme *git.Blob) { // if there is a profile readme, default to "overview" page, otherwise, default to "repositories" page + // if there is not a profile readme, the overview tab should be treated as the repositories tab tab := ctx.FormString("tab") - if tab == "" { + if tab == "" || tab == "overview" { if profileReadme != nil { tab = "overview" } else { @@ -157,10 +158,10 @@ func prepareUserProfileTabData(ctx *context.Context, showPrivate bool, profileGi switch tab { case "followers": ctx.Data["Cards"] = followers - total = int(count) + total = int(numFollowers) case "following": ctx.Data["Cards"] = following - total = int(count) + total = int(numFollowing) case "activity": date := ctx.FormString("date") pagingNum = setting.UI.FeedPagingNum diff --git a/routers/web/web.go b/routers/web/web.go index 077a07686426c..99862505b48a1 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -8,6 +8,7 @@ import ( "net/http" "strings" + auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/context" @@ -92,7 +93,10 @@ func buildAuthGroup() *auth_service.Group { if setting.Service.EnableReverseProxyAuth { group.Add(&auth_service.ReverseProxy{}) } - specialAdd(group) + + if setting.IsWindows && auth_model.IsSSPIEnabled() { + group.Add(&auth_service.SSPI{}) // it MUST be the last, see the comment of SSPI + } return group } diff --git a/services/auth/source/oauth2/providers.go b/services/auth/source/oauth2/providers.go index 7572aa20c0a13..e3a0cb0335dba 100644 --- a/services/auth/source/oauth2/providers.go +++ b/services/auth/source/oauth2/providers.go @@ -22,7 +22,7 @@ import ( type Provider interface { Name() string DisplayName() string - IconHTML() template.HTML + IconHTML(size int) template.HTML CustomURLSettings() *CustomURLSettings } @@ -54,14 +54,16 @@ func (p *AuthSourceProvider) DisplayName() string { return p.sourceName } -func (p *AuthSourceProvider) IconHTML() template.HTML { +func (p *AuthSourceProvider) IconHTML(size int) template.HTML { if p.iconURL != "" { - img := fmt.Sprintf(`%s`, + img := fmt.Sprintf(`%s`, + size, + size, html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()), ) return template.HTML(img) } - return p.GothProvider.IconHTML() + return p.GothProvider.IconHTML(size) } // Providers contains the map of registered OAuth2 providers in Gitea (based on goth) diff --git a/services/auth/source/oauth2/providers_base.go b/services/auth/source/oauth2/providers_base.go index 5ba06febaf70f..5b6694487bf98 100644 --- a/services/auth/source/oauth2/providers_base.go +++ b/services/auth/source/oauth2/providers_base.go @@ -27,7 +27,7 @@ func (b *BaseProvider) DisplayName() string { } // IconHTML returns icon HTML for this provider -func (b *BaseProvider) IconHTML() template.HTML { +func (b *BaseProvider) IconHTML(size int) template.HTML { svgName := "gitea-" + b.name switch b.name { case "gplus": @@ -35,10 +35,10 @@ func (b *BaseProvider) IconHTML() template.HTML { case "github": svgName = "octicon-mark-github" } - svgHTML := svg.RenderHTML(svgName, 20, "gt-mr-3") + svgHTML := svg.RenderHTML(svgName, size, "gt-mr-3") if svgHTML == "" { log.Error("No SVG icon for oauth2 provider %q", b.name) - svgHTML = svg.RenderHTML("gitea-openid", 20, "gt-mr-3") + svgHTML = svg.RenderHTML("gitea-openid", size, "gt-mr-3") } return svgHTML } diff --git a/services/auth/source/oauth2/providers_openid.go b/services/auth/source/oauth2/providers_openid.go index 54530ae8a85b1..a4dcfcafc7e35 100644 --- a/services/auth/source/oauth2/providers_openid.go +++ b/services/auth/source/oauth2/providers_openid.go @@ -28,8 +28,8 @@ func (o *OpenIDProvider) DisplayName() string { } // IconHTML returns icon HTML for this provider -func (o *OpenIDProvider) IconHTML() template.HTML { - return svg.RenderHTML("gitea-openid", 20, "gt-mr-3") +func (o *OpenIDProvider) IconHTML(size int) template.HTML { + return svg.RenderHTML("gitea-openid", size, "gt-mr-3") } // CreateGothProvider creates a GothProvider from this Provider diff --git a/services/auth/sspi_windows.go b/services/auth/sspi.go similarity index 90% rename from services/auth/sspi_windows.go rename to services/auth/sspi.go index e29bd715293c9..d4f7e3ec60c3b 100644 --- a/services/auth/sspi_windows.go +++ b/services/auth/sspi.go @@ -22,19 +22,21 @@ import ( "code.gitea.io/gitea/services/auth/source/sspi" gouuid "github.com/google/uuid" - "github.com/quasoft/websspi" ) const ( tplSignIn base.TplName = "user/auth/signin" ) +type SSPIAuth interface { + AppendAuthenticateHeader(w http.ResponseWriter, data string) + Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) +} + var ( - // sspiAuth is a global instance of the websspi authentication package, - // which is used to avoid acquiring the server credential handle on - // every request - sspiAuth *websspi.Authenticator - sspiAuthOnce sync.Once + sspiAuth SSPIAuth // a global instance of the websspi authenticator to avoid acquiring the server credential handle on every request + sspiAuthOnce sync.Once + sspiAuthErrInit error // Ensure the struct implements the interface. _ Method = &SSPI{} @@ -42,8 +44,9 @@ var ( // SSPI implements the SingleSignOn interface and authenticates requests // via the built-in SSPI module in Windows for SPNEGO authentication. -// On successful authentication returns a valid user object. -// Returns nil if authentication fails. +// The SSPI plugin is expected to be executed last, as it returns 401 status code if negotiation +// fails (or if negotiation should continue), which would prevent other authentication methods +// to execute at all. type SSPI struct{} // Name represents the name of auth method @@ -56,15 +59,10 @@ func (s *SSPI) Name() string { // If negotiation should continue or authentication fails, immediately returns a 401 HTTP // response code, as required by the SPNEGO protocol. func (s *SSPI) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { - var errInit error - sspiAuthOnce.Do(func() { - config := websspi.NewConfig() - sspiAuth, errInit = websspi.New(config) - }) - if errInit != nil { - return nil, errInit + sspiAuthOnce.Do(func() { sspiAuthErrInit = sspiAuthInit() }) + if sspiAuthErrInit != nil { + return nil, sspiAuthErrInit } - if !s.shouldAuthenticate(req) { return nil, nil } diff --git a/services/auth/sspiauth_posix.go b/services/auth/sspiauth_posix.go new file mode 100644 index 0000000000000..49b0ed4a52ac8 --- /dev/null +++ b/services/auth/sspiauth_posix.go @@ -0,0 +1,30 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build !windows + +package auth + +import ( + "errors" + "net/http" +) + +type SSPIUserInfo struct { + Username string // Name of user, usually in the form DOMAIN\User + Groups []string // The global groups the user is a member of +} + +type sspiAuthMock struct{} + +func (s sspiAuthMock) AppendAuthenticateHeader(w http.ResponseWriter, data string) { +} + +func (s sspiAuthMock) Authenticate(r *http.Request, w http.ResponseWriter) (userInfo *SSPIUserInfo, outToken string, err error) { + return nil, "", errors.New("not implemented") +} + +func sspiAuthInit() error { + sspiAuth = &sspiAuthMock{} // TODO: we can mock the SSPI auth in tests + return nil +} diff --git a/services/auth/sspiauth_windows.go b/services/auth/sspiauth_windows.go new file mode 100644 index 0000000000000..093caaed33c37 --- /dev/null +++ b/services/auth/sspiauth_windows.go @@ -0,0 +1,19 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +//go:build windows + +package auth + +import ( + "github.com/quasoft/websspi" +) + +type SSPIUserInfo = websspi.UserInfo + +func sspiAuthInit() error { + var err error + config := websspi.NewConfig() + sspiAuth, err = websspi.New(config) + return err +} diff --git a/services/wiki/wiki_test.go b/services/wiki/wiki_test.go index 0621456f3e82d..81a688b046a7c 100644 --- a/services/wiki/wiki_test.go +++ b/services/wiki/wiki_test.go @@ -168,7 +168,9 @@ func TestRepository_AddWikiPage(t *testing.T) { assert.NoError(t, AddWikiPage(git.DefaultContext, doer, repo, webPath, wikiContent, commitMsg)) // Now need to show that the page has been added: gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath()) - assert.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer gitRepo.Close() masterTree, err := gitRepo.GetTree(DefaultBranch) assert.NoError(t, err) @@ -238,7 +240,9 @@ func TestRepository_DeleteWikiPage(t *testing.T) { // Now need to show that the page has been added: gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath()) - assert.NoError(t, err) + if !assert.NoError(t, err) { + return + } defer gitRepo.Close() masterTree, err := gitRepo.GetTree(DefaultBranch) assert.NoError(t, err) @@ -251,8 +255,10 @@ func TestPrepareWikiFileName(t *testing.T) { unittest.PrepareTestEnv(t) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) gitRepo, err := git.OpenRepository(git.DefaultContext, repo.WikiPath()) + if !assert.NoError(t, err) { + return + } defer gitRepo.Close() - assert.NoError(t, err) tests := []struct { name string @@ -303,8 +309,10 @@ func TestPrepareWikiFileName_FirstPage(t *testing.T) { assert.NoError(t, err) gitRepo, err := git.OpenRepository(git.DefaultContext, tmpDir) + if !assert.NoError(t, err) { + return + } defer gitRepo.Close() - assert.NoError(t, err) existence, newWikiPath, err := prepareGitPath(gitRepo, "Home") assert.False(t, existence) diff --git a/templates/admin/auth/edit.tmpl b/templates/admin/auth/edit.tmpl index 6dfa86d9dd154..814bddd8a4555 100644 --- a/templates/admin/auth/edit.tmpl +++ b/templates/admin/auth/edit.tmpl @@ -428,7 +428,7 @@
- +
diff --git a/templates/admin/auth/new.tmpl b/templates/admin/auth/new.tmpl index 37d1635c11df0..31efa62e713df 100644 --- a/templates/admin/auth/new.tmpl +++ b/templates/admin/auth/new.tmpl @@ -73,7 +73,7 @@
- +
diff --git a/templates/admin/config.tmpl b/templates/admin/config.tmpl index 36d9bcb8a5e2d..c29d1dbf30ef2 100644 --- a/templates/admin/config.tmpl +++ b/templates/admin/config.tmpl @@ -238,7 +238,7 @@
- + {{end}} diff --git a/templates/admin/cron.tmpl b/templates/admin/cron.tmpl index c15461943512c..354cd18ed554d 100644 --- a/templates/admin/cron.tmpl +++ b/templates/admin/cron.tmpl @@ -20,7 +20,7 @@ {{range .Entries}} - + {{$.locale.Tr (printf "admin.dashboard.%s" .Name)}} {{.Spec}} {{DateTime "full" .Next}} diff --git a/templates/admin/dashboard.tmpl b/templates/admin/dashboard.tmpl index 8312fba039aab..69c0376d6e041 100644 --- a/templates/admin/dashboard.tmpl +++ b/templates/admin/dashboard.tmpl @@ -15,55 +15,55 @@ {{.locale.Tr "admin.dashboard.delete_inactive_accounts"}} - + {{.locale.Tr "admin.dashboard.delete_repo_archives"}} - + {{.locale.Tr "admin.dashboard.delete_missing_repos"}} - + {{.locale.Tr "admin.dashboard.git_gc_repos"}} - + {{if and (not .SSH.Disabled) (not .SSH.StartBuiltinServer)}} {{.locale.Tr "admin.dashboard.resync_all_sshkeys"}}
{{.locale.Tr "admin.dashboard.resync_all_sshkeys.desc"}} - + {{.locale.Tr "admin.dashboard.resync_all_sshprincipals"}}
{{.locale.Tr "admin.dashboard.resync_all_sshprincipals.desc"}} - + {{end}} {{.locale.Tr "admin.dashboard.resync_all_hooks"}} - + {{.locale.Tr "admin.dashboard.reinit_missing_repos"}} - + {{.locale.Tr "admin.dashboard.sync_external_users"}} - + {{.locale.Tr "admin.dashboard.repo_health_check"}} - + {{.locale.Tr "admin.dashboard.delete_generated_repository_avatars"}} - + {{.locale.Tr "admin.dashboard.sync_repo_branches"}} - + diff --git a/templates/admin/repo/unadopted.tmpl b/templates/admin/repo/unadopted.tmpl index 7b86b0defd76f..a903425b21252 100644 --- a/templates/admin/repo/unadopted.tmpl +++ b/templates/admin/repo/unadopted.tmpl @@ -23,7 +23,7 @@
{{svg "octicon-file-directory-fill"}} {{$dir}}
- +
- +
diff --git a/templates/org/team/invite.tmpl b/templates/org/team/invite.tmpl index 60332a5f4070b..1b04c0cc2a711 100644 --- a/templates/org/team/invite.tmpl +++ b/templates/org/team/invite.tmpl @@ -14,7 +14,7 @@
{{.CsrfTokenHtml}} - +
diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl index cac0c1ce94744..7c2fab4b80798 100644 --- a/templates/org/team/members.tmpl +++ b/templates/org/team/members.tmpl @@ -17,7 +17,7 @@
- + {{end}} diff --git a/templates/org/team/new.tmpl b/templates/org/team/new.tmpl index 3702198ae0932..1d3556400772b 100644 --- a/templates/org/team/new.tmpl +++ b/templates/org/team/new.tmpl @@ -133,9 +133,9 @@
{{if .PageIsOrgTeamsNew}} - + {{else}} - + {{if not (eq .Team.LowerName "owners")}} {{end}} diff --git a/templates/org/team/repositories.tmpl b/templates/org/team/repositories.tmpl index 44fb1ee083279..032a0f496a0ab 100644 --- a/templates/org/team/repositories.tmpl +++ b/templates/org/team/repositories.tmpl @@ -17,10 +17,10 @@
- +
- +
diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl index a96dd7d6be603..0d7cc06ff9f2c 100644 --- a/templates/org/team/teams.tmpl +++ b/templates/org/team/teams.tmpl @@ -5,7 +5,7 @@ {{template "base/alert" .}} {{if .IsOrganizationOwner}}
{{end}} diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index af543328f884a..d6c431de037bb 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -31,7 +31,7 @@
- +
diff --git a/templates/package/shared/cargo.tmpl b/templates/package/shared/cargo.tmpl index 8831cd8988eb6..9761cb2133c8a 100644 --- a/templates/package/shared/cargo.tmpl +++ b/templates/package/shared/cargo.tmpl @@ -8,14 +8,14 @@
{{.CsrfTokenHtml}} - +
{{.CsrfTokenHtml}} - +
diff --git a/templates/package/shared/cleanup_rules/edit.tmpl b/templates/package/shared/cleanup_rules/edit.tmpl index 295ac1a6a4d98..1bfa6260a1ca3 100644 --- a/templates/package/shared/cleanup_rules/edit.tmpl +++ b/templates/package/shared/cleanup_rules/edit.tmpl @@ -62,11 +62,11 @@
{{if .IsEditRule}} - + {{.locale.Tr "packages.owner.settings.cleanuprules.preview"}} {{else}} - + {{end}}
diff --git a/templates/projects/list.tmpl b/templates/projects/list.tmpl index 0cb619c066012..1f3668be7a8f3 100644 --- a/templates/projects/list.tmpl +++ b/templates/projects/list.tmpl @@ -11,7 +11,7 @@ {{end}} diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 8e56b435536b4..1663e76a4072a 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -75,7 +75,7 @@
- + @@ -98,7 +98,7 @@
- +
@@ -123,7 +123,7 @@
- +
diff --git a/templates/repo/create.tmpl b/templates/repo/create.tmpl index 0fcdf13f1e383..4125cb9c8fd0d 100644 --- a/templates/repo/create.tmpl +++ b/templates/repo/create.tmpl @@ -220,7 +220,7 @@
-
diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 0b5bf8637140e..2e1461226aea4 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -232,8 +232,8 @@ "DropzoneParentContainer" ".ui.form" )}}
- - + +
diff --git a/templates/repo/diff/comment_form.tmpl b/templates/repo/diff/comment_form.tmpl index 5aad8d9590267..dff030a0b41cb 100644 --- a/templates/repo/diff/comment_form.tmpl +++ b/templates/repo/diff/comment_form.tmpl @@ -24,14 +24,14 @@ {{svg "octicon-markup"}} {{$.root.locale.Tr "repo.diff.comment.markdown_info"}}
{{if $.reply}} - + {{else}} {{if $.root.CurrentReview}} - + {{else}} - + {{end}} {{end}} diff --git a/templates/repo/diff/compare.tmpl b/templates/repo/diff/compare.tmpl index e4ae7d4dc8e76..4d57fd614855c 100644 --- a/templates/repo/diff/compare.tmpl +++ b/templates/repo/diff/compare.tmpl @@ -180,7 +180,7 @@ {{if and $.IsSigned $.AllowEmptyPr (not .Repository.IsArchived)}}
{{.locale.Tr "repo.pulls.nothing_to_compare_and_allow_empty_pr"}}
- +
{{else}} {{if and $.IsSigned (not .Repository.IsArchived)}}
- +
{{else if .Repository.IsArchived}}
diff --git a/templates/repo/diff/conversation.tmpl b/templates/repo/diff/conversation.tmpl index 639dd9cd040b6..c6ada10caf0bb 100644 --- a/templates/repo/diff/conversation.tmpl +++ b/templates/repo/diff/conversation.tmpl @@ -56,7 +56,7 @@ {{end}} {{if and $.SignedUserID (not $.Repository.IsArchived)}} - {{end}} diff --git a/templates/repo/diff/new_review.tmpl b/templates/repo/diff/new_review.tmpl index 6a3fa7a823273..908f488975243 100644 --- a/templates/repo/diff/new_review.tmpl +++ b/templates/repo/diff/new_review.tmpl @@ -1,5 +1,5 @@
- + {{else}} - + {{end}} {{if $showSelfTooltip}} diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index b07059777fbc1..dd6537e1a008f 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -67,7 +67,7 @@ {{end}}
- {{.locale.Tr "repo.editor.cancel"}} diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 3b5a63f3aa854..2b303be97ccdb 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -61,11 +61,11 @@

{{.locale.Tr "repo.editor.commit_empty_file_text"}}

- - diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index 1f948fbb1904e..57126c09abf77 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -43,11 +43,11 @@

{{.locale.Tr "repo.editor.commit_empty_file_text"}}

- - diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 935060816cb22..d0f24949c9b01 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -48,7 +48,7 @@
{{$.CsrfTokenHtml}}
-
diff --git a/templates/repo/issue/choose.tmpl b/templates/repo/issue/choose.tmpl index 1bbfbbb2d1534..9cc666a4bd3e9 100644 --- a/templates/repo/issue/choose.tmpl +++ b/templates/repo/issue/choose.tmpl @@ -15,7 +15,7 @@
{{.About | RenderEmojiPlain}}
@@ -28,7 +28,7 @@
{{.About | RenderEmojiPlain}} @@ -41,7 +41,7 @@
{{.locale.Tr "repo.issues.choose.blank_about"}} diff --git a/templates/repo/issue/filters.tmpl b/templates/repo/issue/filters.tmpl index b482e471e1d4d..3bfced90d6a0f 100644 --- a/templates/repo/issue/filters.tmpl +++ b/templates/repo/issue/filters.tmpl @@ -29,7 +29,10 @@
{{end}} {{$previousExclusiveScope = $exclusiveScope}} - {{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} {{RenderLabel $.Context .}} + {{if .IsExcluded}}{{svg "octicon-circle-slash"}}{{else if .IsSelected}}{{if $exclusiveScope}}{{svg "octicon-dot-fill"}}{{else}}{{svg "octicon-check"}}{{end}}{{end}} + {{RenderLabel $.Context .}} +

{{template "repo/issue/labels/label_archived" .}}

+
{{end}} diff --git a/templates/repo/issue/labels.tmpl b/templates/repo/issue/labels.tmpl index a34ce1bfbb51b..7de1b0f0baf3d 100644 --- a/templates/repo/issue/labels.tmpl +++ b/templates/repo/issue/labels.tmpl @@ -5,7 +5,7 @@ {{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}} diff --git a/templates/repo/issue/labels/label_archived.tmpl b/templates/repo/issue/labels/label_archived.tmpl new file mode 100644 index 0000000000000..feaf77e456c7d --- /dev/null +++ b/templates/repo/issue/labels/label_archived.tmpl @@ -0,0 +1,5 @@ +{{if .IsArchived}} + + {{ctx.Locale.Tr "archived"}} + +{{end}} diff --git a/templates/repo/issue/labels/label_list.tmpl b/templates/repo/issue/labels/label_list.tmpl index b29e606baa415..d9addb439b840 100644 --- a/templates/repo/issue/labels/label_list.tmpl +++ b/templates/repo/issue/labels/label_list.tmpl @@ -33,11 +33,6 @@
  • {{RenderLabel $.Context .}} - {{if not .ArchivedUnix.IsZero}} - - {{$.locale.Tr "home.archived"}} - - {{end}} {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}}
    -
    - {{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}} - {{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} - {{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}} - {{else if $.PageIsOrgSettingsLabels}} - {{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} - {{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}} - {{end}} +
    + {{template "repo/issue/labels/label_archived" .}} +
    + {{if and (not $.PageIsOrgSettingsLabels) (not $.Repository.IsArchived) (or $.CanWriteIssues $.CanWritePulls)}} + {{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} + {{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}} + {{else if $.PageIsOrgSettingsLabels}} + {{svg "octicon-pencil"}} {{$.locale.Tr "repo.issues.label_edit"}} + {{svg "octicon-trash"}} {{$.locale.Tr "repo.issues.label_delete"}} + {{end}} +
  • {{end}} @@ -78,9 +76,11 @@ {{if .Description}}
    {{.Description | RenderEmoji $.Context}}{{end}} +
    + {{template "repo/issue/labels/label_archived" .}}
    -
    {{end}} {{end}} diff --git a/templates/repo/issue/labels/label_new.tmpl b/templates/repo/issue/labels/label_new.tmpl index 1d3069f1bb798..8edd6d4e8a2b5 100644 --- a/templates/repo/issue/labels/label_new.tmpl +++ b/templates/repo/issue/labels/label_new.tmpl @@ -36,11 +36,11 @@
    - - diff --git a/templates/repo/issue/list.tmpl b/templates/repo/issue/list.tmpl index 789ba4020f706..8087aaa68a90d 100644 --- a/templates/repo/issue/list.tmpl +++ b/templates/repo/issue/list.tmpl @@ -18,13 +18,13 @@ {{template "repo/issue/search" .}} {{if not .Repository.IsArchived}} {{if .PageIsIssueList}} - {{.locale.Tr "repo.issues.new"}} + {{.locale.Tr "repo.issues.new"}} {{else}} - {{.locale.Tr "repo.pulls.new"}} + {{.locale.Tr "repo.pulls.new"}} {{end}} {{else}} {{if not .PageIsIssueList}} - {{$.locale.Tr "action.compare_commits_general"}} + {{$.locale.Tr "action.compare_commits_general"}} {{end}} {{end}}
    @@ -40,7 +40,7 @@ {{if not .Repository.IsArchived}} {{if .IsShowClosed}} - + {{else}} {{end}} diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl index dab6ef3de6529..aa3d9b94cdbdc 100644 --- a/templates/repo/issue/milestone_issues.tmpl +++ b/templates/repo/issue/milestone_issues.tmpl @@ -8,7 +8,7 @@ @@ -44,11 +44,11 @@ {{.locale.Tr "repo.milestones.cancel"}} - {{else}} - {{end}} diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl index 3f481c95e411d..cdc372cc55e0b 100644 --- a/templates/repo/issue/milestones.tmpl +++ b/templates/repo/issue/milestones.tmpl @@ -5,7 +5,7 @@ {{template "base/alert" .}} diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index fc1a3d087f02f..9d0b666375102 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -35,7 +35,7 @@ {{template "repo/issue/comment_tab" .}} {{end}}
    - {{else}} @@ -105,7 +105,7 @@ {{end}} {{end}} -
    diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index d43979ff59bdb..cb897a692b82c 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -585,7 +585,7 @@ {{end}} {{if and $.SignedUserID (not $.Repository.IsArchived)}} - {{end}} diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl index 861e7b97a4d6d..3df85e7b81f77 100644 --- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl +++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl @@ -22,7 +22,7 @@
    - +
    diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 3d5be860bd5f6..6405a6e180e36 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -327,8 +327,8 @@
    - - + +
    {{end}} {{if not .Issue.IsPull}} - {{.locale.Tr "repo.issues.new"}} + {{.locale.Tr "repo.issues.new"}} {{end}} {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} diff --git a/templates/repo/migrate/codebase.tmpl b/templates/repo/migrate/codebase.tmpl index db607eebb4c75..394e7c2ecfbe7 100644 --- a/templates/repo/migrate/codebase.tmpl +++ b/templates/repo/migrate/codebase.tmpl @@ -104,7 +104,7 @@
    -
    diff --git a/templates/repo/migrate/git.tmpl b/templates/repo/migrate/git.tmpl index 42ae642d26679..1e449ae30e90f 100644 --- a/templates/repo/migrate/git.tmpl +++ b/templates/repo/migrate/git.tmpl @@ -78,7 +78,7 @@
    -
    diff --git a/templates/repo/migrate/gitbucket.tmpl b/templates/repo/migrate/gitbucket.tmpl index 372df0c2dba73..aa269e561ccaf 100644 --- a/templates/repo/migrate/gitbucket.tmpl +++ b/templates/repo/migrate/gitbucket.tmpl @@ -120,7 +120,7 @@
    -
    diff --git a/templates/repo/migrate/gitea.tmpl b/templates/repo/migrate/gitea.tmpl index 8022f63dc525d..e4d8c6273498d 100644 --- a/templates/repo/migrate/gitea.tmpl +++ b/templates/repo/migrate/gitea.tmpl @@ -116,7 +116,7 @@
    -
    diff --git a/templates/repo/migrate/github.tmpl b/templates/repo/migrate/github.tmpl index 0ccad3a3ea33c..d3f0e0b8bd33d 100644 --- a/templates/repo/migrate/github.tmpl +++ b/templates/repo/migrate/github.tmpl @@ -118,7 +118,7 @@
    -
    diff --git a/templates/repo/migrate/gitlab.tmpl b/templates/repo/migrate/gitlab.tmpl index af20fa38e2c79..e055cce19ed7a 100644 --- a/templates/repo/migrate/gitlab.tmpl +++ b/templates/repo/migrate/gitlab.tmpl @@ -115,7 +115,7 @@
    -
    diff --git a/templates/repo/migrate/gogs.tmpl b/templates/repo/migrate/gogs.tmpl index aeead61e412c0..2d6680060823b 100644 --- a/templates/repo/migrate/gogs.tmpl +++ b/templates/repo/migrate/gogs.tmpl @@ -118,7 +118,7 @@
    -
    diff --git a/templates/repo/migrate/migrating.tmpl b/templates/repo/migrate/migrating.tmpl index d1bdc5a90fb10..6effa05f97762 100644 --- a/templates/repo/migrate/migrating.tmpl +++ b/templates/repo/migrate/migrating.tmpl @@ -38,7 +38,7 @@ {{if .Failed}} {{else}} - + {{end}} diff --git a/templates/repo/migrate/onedev.tmpl b/templates/repo/migrate/onedev.tmpl index f5ba11ab065fe..818b23fddca64 100644 --- a/templates/repo/migrate/onedev.tmpl +++ b/templates/repo/migrate/onedev.tmpl @@ -104,7 +104,7 @@
    -
    diff --git a/templates/repo/projects/view.tmpl b/templates/repo/projects/view.tmpl index ac3d2cd77c153..3f2dd9a548b76 100644 --- a/templates/repo/projects/view.tmpl +++ b/templates/repo/projects/view.tmpl @@ -4,7 +4,7 @@
    {{template "projects/view" .}}
    diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl index 15635074accb8..66b1c3a276649 100644 --- a/templates/repo/pulls/fork.tmpl +++ b/templates/repo/pulls/fork.tmpl @@ -58,7 +58,7 @@
    -
    diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index b93134b5d9562..a717fba6510dc 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -16,19 +16,19 @@
    -

    +

    {{.Title}} {{if .IsDraft}} - {{$.locale.Tr "repo.release.draft"}} + {{$.locale.Tr "repo.release.draft"}} {{else if .IsPrerelease}} - {{$.locale.Tr "repo.release.prerelease"}} + {{$.locale.Tr "repo.release.prerelease"}} {{else if not .IsTag}} - {{$.locale.Tr "repo.release.stable"}} + {{$.locale.Tr "repo.release.stable"}} {{end}}

    -
    +
    {{if and $.CanCreateRelease (not .IsTag)}} - + {{svg "octicon-pencil"}} {{end}} @@ -38,7 +38,7 @@

    {{if gt .Publisher.ID 0}} - {{ctx.AvatarUtils.Avatar .Publisher 20}} + {{ctx.AvatarUtils.Avatar .Publisher 20 "gt-mr-2"}} {{.Publisher.Name}} @@ -55,9 +55,9 @@

    {{if .OriginalAuthor}} - {{svg "octicon-mark-github" 16 "gt-mr-2"}}{{.OriginalAuthor}} + {{svg "octicon-mark-github" 20 "gt-mr-2"}}{{.OriginalAuthor}} {{else if .Publisher}} - {{ctx.AvatarUtils.Avatar .Publisher 20}} + {{ctx.AvatarUtils.Avatar .Publisher 20 "gt-mr-2"}} {{.Publisher.GetDisplayName}} {{else}} Ghost @@ -77,8 +77,9 @@

    {{Str2html .Note}}
    -
    - +
    +
    + {{$.locale.Tr "repo.release.downloads"}}
    -   +
    {{end}} diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl index ad42b9949f55b..89358e9dd4851 100644 --- a/templates/repo/release_tag_header.tmpl +++ b/templates/repo/release_tag_header.tmpl @@ -15,7 +15,7 @@ {{end}}
    {{if and (not .PageIsTagList) .CanCreateRelease}} - + {{.locale.Tr "repo.release.new_release"}} {{end}} diff --git a/templates/repo/settings/branches.tmpl b/templates/repo/settings/branches.tmpl index ef227ff4aa050..43203a7ad36e9 100644 --- a/templates/repo/settings/branches.tmpl +++ b/templates/repo/settings/branches.tmpl @@ -26,7 +26,7 @@ {{end}}
    - + {{end}} diff --git a/templates/repo/settings/collaboration.tmpl b/templates/repo/settings/collaboration.tmpl index c40265c6a61f7..290a1ae0935f9 100644 --- a/templates/repo/settings/collaboration.tmpl +++ b/templates/repo/settings/collaboration.tmpl @@ -44,7 +44,7 @@ - + @@ -92,7 +92,7 @@ - + {{else}}
    diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index b776848a5628f..a7b7dd651105c 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -34,7 +34,7 @@ {{$.locale.Tr "repo.settings.is_writable_info" | Str2html}}
    - + {{end}} diff --git a/templates/repo/settings/lfs_pointers.tmpl b/templates/repo/settings/lfs_pointers.tmpl index b6b58a37720a8..db3fdd2f85150 100644 --- a/templates/repo/settings/lfs_pointers.tmpl +++ b/templates/repo/settings/lfs_pointers.tmpl @@ -11,7 +11,7 @@ {{end}} {{end}} - + {{end}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 78700e05b7d8f..28841de4cd69d 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -45,7 +45,7 @@
    - +
    @@ -57,7 +57,7 @@
    - +
    @@ -189,7 +189,7 @@ {{end}}
    - +
    @@ -271,7 +271,7 @@
    - +
    @@ -586,7 +586,7 @@
    - +
    @@ -632,7 +632,7 @@
    - +
    @@ -653,7 +653,7 @@
    - +
    @@ -675,7 +675,7 @@ {{end}}
    - +
    {{end}} @@ -694,7 +694,7 @@ {{end}}
    - +
    diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index eb5baff59bd14..74cb84aba3c71 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -255,7 +255,7 @@
    - +
    diff --git a/templates/repo/settings/tags.tmpl b/templates/repo/settings/tags.tmpl index c3d2fc38de013..48b67697c8ed5 100644 --- a/templates/repo/settings/tags.tmpl +++ b/templates/repo/settings/tags.tmpl @@ -57,14 +57,14 @@ {{end}}
    {{if .PageIsEditProtectedTag}} - {{$.locale.Tr "cancel"}} {{else}} - {{end}} diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl index d84c8eb110632..a669d2c9a3d5d 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -278,9 +278,9 @@
    {{if $isNew}} - + {{else}} - + {{.locale.Tr "repo.settings.delete_webhook"}} {{end}}
    diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index 2098a7675514c..d122ee5119f28 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -6,7 +6,7 @@
    {{.locale.Tr "repo.wiki.new_page"}} {{if .PageIsWikiEdit}} - {{.locale.Tr "repo.wiki.new_page_button"}} + {{.locale.Tr "repo.wiki.new_page_button"}} {{end}}
    @@ -37,7 +37,7 @@
    -
    diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl index 6169109ce921e..c778933e8b44b 100644 --- a/templates/repo/wiki/pages.tmpl +++ b/templates/repo/wiki/pages.tmpl @@ -6,7 +6,7 @@ {{.locale.Tr "repo.wiki.pages"}} {{if and .CanWriteWiki (not .Repository.IsMirror)}} - {{.locale.Tr "repo.wiki.new_page_button"}} + {{.locale.Tr "repo.wiki.new_page_button"}} {{end}} diff --git a/templates/repo/wiki/start.tmpl b/templates/repo/wiki/start.tmpl index 4885042fd852e..dbe625c568b35 100644 --- a/templates/repo/wiki/start.tmpl +++ b/templates/repo/wiki/start.tmpl @@ -7,7 +7,7 @@

    {{.locale.Tr "repo.wiki.welcome"}}

    {{.locale.Tr "repo.wiki.welcome_desc"}}

    {{if and .CanWriteWiki (not .Repository.IsMirror)}} - {{.locale.Tr "repo.wiki.create_first_page"}} + {{.locale.Tr "repo.wiki.create_first_page"}} {{end}} diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index e25d82a257467..97a98e8c8f564 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -52,8 +52,8 @@ {{if and .CanWriteWiki (not .Repository.IsMirror)}} {{end}} diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl index e69583b9e499e..ec3c25ab87b24 100644 --- a/templates/shared/actions/runner_edit.tmpl +++ b/templates/shared/actions/runner_edit.tmpl @@ -39,7 +39,7 @@
    - +
    diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl index 44797ff4a75cc..e328c26e1f61c 100644 --- a/templates/shared/user/profile_big_avatar.tmpl +++ b/templates/shared/user/profile_big_avatar.tmpl @@ -112,7 +112,7 @@ {{svg "octicon-person"}} {{.locale.Tr "user.unfollow"}} {{else}} - {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 88dc9ea1ce551..39c5a7fe117df 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -16359,6 +16359,9 @@ "responses": { "200": { "$ref": "#/responses/AccessTokenList" + }, + "403": { + "$ref": "#/responses/forbidden" } } }, @@ -16396,6 +16399,9 @@ }, "400": { "$ref": "#/responses/error" + }, + "403": { + "$ref": "#/responses/forbidden" } } } @@ -16430,6 +16436,9 @@ "204": { "$ref": "#/responses/empty" }, + "403": { + "$ref": "#/responses/forbidden" + }, "404": { "$ref": "#/responses/notFound" }, diff --git a/templates/user/auth/activate.tmpl b/templates/user/auth/activate.tmpl index 9a2abd3cdfee6..3261836ee02e2 100644 --- a/templates/user/auth/activate.tmpl +++ b/templates/user/auth/activate.tmpl @@ -25,7 +25,7 @@
    - +
    {{else if .IsSendRegisterMail}} diff --git a/templates/user/auth/change_passwd_inner.tmpl b/templates/user/auth/change_passwd_inner.tmpl index 40281f8578e20..317a031f6b9f7 100644 --- a/templates/user/auth/change_passwd_inner.tmpl +++ b/templates/user/auth/change_passwd_inner.tmpl @@ -17,7 +17,7 @@
    - +
    diff --git a/templates/user/auth/finalize_openid.tmpl b/templates/user/auth/finalize_openid.tmpl index 2b833d9b3269b..e81a565507d70 100644 --- a/templates/user/auth/finalize_openid.tmpl +++ b/templates/user/auth/finalize_openid.tmpl @@ -29,7 +29,7 @@
    - + {{.locale.Tr "auth.forget_password"}}
    {{if .ShowRegistrationButton}} diff --git a/templates/user/auth/signin_inner.tmpl b/templates/user/auth/signin_inner.tmpl index 6b118b7a261f2..2b72f119cbbd8 100644 --- a/templates/user/auth/signin_inner.tmpl +++ b/templates/user/auth/signin_inner.tmpl @@ -35,7 +35,7 @@
    -
    - +
    diff --git a/templates/user/auth/signup_inner.tmpl b/templates/user/auth/signup_inner.tmpl index 184b8b88fe0b6..e3dd337837952 100644 --- a/templates/user/auth/signup_inner.tmpl +++ b/templates/user/auth/signup_inner.tmpl @@ -39,7 +39,7 @@
    -
    - + {{.locale.Tr "auth.forgot_password"}}
    diff --git a/templates/user/auth/signup_openid_register.tmpl b/templates/user/auth/signup_openid_register.tmpl index a490ea5a74d8f..9736c53ed830b 100644 --- a/templates/user/auth/signup_openid_register.tmpl +++ b/templates/user/auth/signup_openid_register.tmpl @@ -29,7 +29,7 @@
    - +
    diff --git a/templates/user/auth/twofa.tmpl b/templates/user/auth/twofa.tmpl index 4002266054e2d..e303580956581 100644 --- a/templates/user/auth/twofa.tmpl +++ b/templates/user/auth/twofa.tmpl @@ -16,7 +16,7 @@
    - + {{.locale.Tr "auth.use_scratch_code" | Str2html}}
    diff --git a/templates/user/auth/twofa_scratch.tmpl b/templates/user/auth/twofa_scratch.tmpl index 4ed7fce55e9d3..58942050f5d09 100644 --- a/templates/user/auth/twofa_scratch.tmpl +++ b/templates/user/auth/twofa_scratch.tmpl @@ -16,7 +16,7 @@
    - +
    diff --git a/templates/user/dashboard/issues.tmpl b/templates/user/dashboard/issues.tmpl index 00c604bfc4a76..a973eb59e4bf8 100644 --- a/templates/user/dashboard/issues.tmpl +++ b/templates/user/dashboard/issues.tmpl @@ -101,9 +101,9 @@ {{if .SingleRepoLink}} {{if eq .SingleRepoAction "issue"}} - {{.locale.Tr "repo.issues.new"}} + {{.locale.Tr "repo.issues.new"}} {{else if eq .SingleRepoAction "pull"}} - {{.locale.Tr "repo.pulls.new"}} + {{.locale.Tr "repo.pulls.new"}} {{end}} {{end}} diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 0b57720c96820..173cd5e991585 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -24,7 +24,7 @@
    - + {{.locale.Tr "auth.forgot_password"}}
    @@ -58,7 +58,7 @@
    {{$.locale.Tr "settings.email_notifications.disable"}}
    - + @@ -118,7 +118,7 @@ - diff --git a/templates/user/settings/appearance.tmpl b/templates/user/settings/appearance.tmpl index 129fca26577f9..ca7ef15de5fe7 100644 --- a/templates/user/settings/appearance.tmpl +++ b/templates/user/settings/appearance.tmpl @@ -35,7 +35,7 @@
    - +
    @@ -61,7 +61,7 @@
    - +
    @@ -162,7 +162,7 @@
    - +
    diff --git a/templates/user/settings/applications.tmpl b/templates/user/settings/applications.tmpl index f142af3876048..bc7755eb77c62 100644 --- a/templates/user/settings/applications.tmpl +++ b/templates/user/settings/applications.tmpl @@ -86,7 +86,7 @@ > - {{/* Fomantic ".ui.form .warning.message" is hidden by default, so put the warning message out of the form*/}} diff --git a/templates/user/settings/applications_oauth2_edit_form.tmpl b/templates/user/settings/applications_oauth2_edit_form.tmpl index 74a0e13918879..1437277009546 100644 --- a/templates/user/settings/applications_oauth2_edit_form.tmpl +++ b/templates/user/settings/applications_oauth2_edit_form.tmpl @@ -45,7 +45,7 @@ - diff --git a/templates/user/settings/applications_oauth2_list.tmpl b/templates/user/settings/applications_oauth2_list.tmpl index 348bc82b4e8b9..a92909f77f47b 100644 --- a/templates/user/settings/applications_oauth2_list.tmpl +++ b/templates/user/settings/applications_oauth2_list.tmpl @@ -65,7 +65,7 @@ - diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl index 2ecebcd7c01b6..467b275d35515 100644 --- a/templates/user/settings/keys_gpg.tmpl +++ b/templates/user/settings/keys_gpg.tmpl @@ -31,7 +31,7 @@ {{end}} - diff --git a/templates/user/settings/keys_principal.tmpl b/templates/user/settings/keys_principal.tmpl index 42c21373d53c1..d9cc8fb8237e8 100644 --- a/templates/user/settings/keys_principal.tmpl +++ b/templates/user/settings/keys_principal.tmpl @@ -49,7 +49,7 @@ - diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl index 8419f72ff547c..626d34fd33c12 100644 --- a/templates/user/settings/keys_ssh.tmpl +++ b/templates/user/settings/keys_ssh.tmpl @@ -19,7 +19,7 @@ - diff --git a/templates/user/settings/packages.tmpl b/templates/user/settings/packages.tmpl index 304940feb83ca..f4933d3dae480 100644 --- a/templates/user/settings/packages.tmpl +++ b/templates/user/settings/packages.tmpl @@ -13,7 +13,7 @@
    {{.CsrfTokenHtml}} - +
    diff --git a/templates/user/settings/profile.tmpl b/templates/user/settings/profile.tmpl index 6db58fe4db6c8..34c790065a018 100644 --- a/templates/user/settings/profile.tmpl +++ b/templates/user/settings/profile.tmpl @@ -88,7 +88,7 @@
    - +
    @@ -125,7 +125,7 @@
    - +
    diff --git a/templates/user/settings/repos.tmpl b/templates/user/settings/repos.tmpl index caee1135c5137..db912770d2979 100644 --- a/templates/user/settings/repos.tmpl +++ b/templates/user/settings/repos.tmpl @@ -34,7 +34,7 @@ {{$.ContextUser.Name}}/{{$dir}}
    {{if $.allowAdopt}} - +
    - +
    diff --git a/templates/user/settings/security/webauthn.tmpl b/templates/user/settings/security/webauthn.tmpl index 676754df5973c..6a74b8770a95e 100644 --- a/templates/user/settings/security/webauthn.tmpl +++ b/templates/user/settings/security/webauthn.tmpl @@ -25,7 +25,7 @@ - +