mirror of
https://github.com/go-gitea/gitea.git
synced 2025-10-24 13:53:42 +09:00
Compare commits
5 Commits
6de2151607
...
f4512426a1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f4512426a1 | ||
|
|
a3458c669a | ||
|
|
609d88f029 | ||
|
|
3c78598217 | ||
|
|
b7bb0fa538 |
@@ -173,7 +173,7 @@ func GetReviewsByIssueID(ctx context.Context, issueID int64) (latestReviews, mig
|
||||
reviewersMap := make(map[int64][]*Review) // key is reviewer id
|
||||
originalReviewersMap := make(map[int64][]*Review) // key is original author id
|
||||
reviewTeamsMap := make(map[int64][]*Review) // key is reviewer team id
|
||||
countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest}
|
||||
countedReivewTypes := []ReviewType{ReviewTypeApprove, ReviewTypeReject, ReviewTypeRequest, ReviewTypeComment}
|
||||
for _, review := range reviews {
|
||||
if review.ReviewerTeamID == 0 && slices.Contains(countedReivewTypes, review.Type) && !review.Dismissed {
|
||||
if review.OriginalAuthorID != 0 {
|
||||
|
||||
@@ -122,6 +122,7 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 3})
|
||||
user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2})
|
||||
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3})
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4})
|
||||
@@ -129,6 +130,12 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
|
||||
expectedReviews := []*issues_model.Review{}
|
||||
expectedReviews = append(expectedReviews,
|
||||
&issues_model.Review{
|
||||
ID: 5,
|
||||
Reviewer: user1,
|
||||
Type: issues_model.ReviewTypeComment,
|
||||
UpdatedUnix: 946684810,
|
||||
},
|
||||
&issues_model.Review{
|
||||
ID: 7,
|
||||
Reviewer: org3,
|
||||
@@ -167,8 +174,9 @@ func TestGetReviewersByIssueID(t *testing.T) {
|
||||
for _, review := range allReviews {
|
||||
assert.NoError(t, review.LoadReviewer(t.Context()))
|
||||
}
|
||||
if assert.Len(t, allReviews, 5) {
|
||||
if assert.Len(t, allReviews, 6) {
|
||||
for i, review := range allReviews {
|
||||
assert.Equal(t, expectedReviews[i].ID, review.ID)
|
||||
assert.Equal(t, expectedReviews[i].Reviewer, review.Reviewer)
|
||||
assert.Equal(t, expectedReviews[i].Type, review.Type)
|
||||
assert.Equal(t, expectedReviews[i].UpdatedUnix, review.UpdatedUnix)
|
||||
|
||||
@@ -4,7 +4,10 @@
|
||||
package hcaptcha
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
@@ -21,6 +24,33 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
type mockTransport struct{}
|
||||
|
||||
func (mockTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if req.URL.String() != verifyURL {
|
||||
return nil, errors.New("unsupported url")
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bodyValues, err := url.ParseQuery(string(body))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var responseText string
|
||||
if bodyValues.Get("response") == dummyToken {
|
||||
responseText = `{"success":true,"credit":false,"hostname":"dummy-key-pass","challenge_ts":"2025-10-08T16:02:56.136Z"}`
|
||||
} else {
|
||||
responseText = `{"success":false,"error-codes":["invalid-input-response"]}`
|
||||
}
|
||||
|
||||
return &http.Response{Request: req, Body: io.NopCloser(strings.NewReader(responseText))}, nil
|
||||
}
|
||||
|
||||
func TestCaptcha(t *testing.T) {
|
||||
tt := []struct {
|
||||
Name string
|
||||
@@ -54,7 +84,8 @@ func TestCaptcha(t *testing.T) {
|
||||
for _, tc := range tt {
|
||||
t.Run(tc.Name, func(t *testing.T) {
|
||||
client, err := New(tc.Secret, WithHTTP(&http.Client{
|
||||
Timeout: time.Second * 5,
|
||||
Timeout: time.Second * 5,
|
||||
Transport: mockTransport{},
|
||||
}))
|
||||
if err != nil {
|
||||
// The only error that can be returned from creating a client
|
||||
|
||||
1
public/assets/img/svg/gitea-running.svg
generated
Normal file
1
public/assets/img/svg/gitea-running.svg
generated
Normal file
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" class="svg gitea-running" width="16" height="16" aria-hidden="true"><path fill="none" stroke="currentColor" stroke-width="2" d="M3.05 3.05a7 7 0 1 1 9.9 9.9 7 7 0 0 1-9.9-9.9Z" opacity=".5"/><path fill="currentColor" fill-rule="evenodd" d="M8 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8" clip-rule="evenodd"/><path fill="currentColor" d="M14 8a6 6 0 0 0-6-6V0a8 8 0 0 1 8 8z"/></svg>
|
||||
|
After Width: | Height: | Size: 429 B |
@@ -1423,6 +1423,7 @@ func Routes() *web.Router {
|
||||
m.Get("/tags/{sha}", repo.GetAnnotatedTag)
|
||||
m.Get("/notes/{sha}", repo.GetNote)
|
||||
}, context.ReferencesGitRepo(true), reqRepoReader(unit.TypeCode))
|
||||
m.Post("/diffpatch", mustEnableEditor, reqToken(), bind(api.ApplyDiffPatchFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.ApplyDiffPatch)
|
||||
m.Group("/contents", func() {
|
||||
m.Get("", repo.GetContentsList)
|
||||
m.Get("/*", repo.GetContents)
|
||||
@@ -1434,7 +1435,6 @@ func Routes() *web.Router {
|
||||
m.Put("", bind(api.UpdateFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.UpdateFile)
|
||||
m.Delete("", bind(api.DeleteFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.DeleteFile)
|
||||
})
|
||||
m.Post("/diffpatch", bind(api.ApplyDiffPatchFileOptions{}), repo.ReqChangeRepoFileOptionsAndCheck, repo.ApplyDiffPatch)
|
||||
}, mustEnableEditor, reqToken())
|
||||
}, reqRepoReader(unit.TypeCode), context.ReferencesGitRepo())
|
||||
m.Group("/contents-ext", func() {
|
||||
|
||||
@@ -36,7 +36,7 @@ func ApplyDiffPatch(ctx *context.APIContext) {
|
||||
// in: body
|
||||
// required: true
|
||||
// schema:
|
||||
// "$ref": "#/definitions/UpdateFileOptions"
|
||||
// "$ref": "#/definitions/ApplyDiffPatchFileOptions"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/FileResponse"
|
||||
|
||||
@@ -121,6 +121,9 @@ type swaggerParameterBodies struct {
|
||||
// in:body
|
||||
GetFilesOptions api.GetFilesOptions
|
||||
|
||||
// in:body
|
||||
ApplyDiffPatchFileOptions api.ApplyDiffPatchFileOptions
|
||||
|
||||
// in:body
|
||||
ChangeFilesOptions api.ChangeFilesOptions
|
||||
|
||||
|
||||
@@ -270,8 +270,7 @@ func LFSFileGet(ctx *context.Context) {
|
||||
// FIXME: there is no IsPlainText set, but template uses it
|
||||
ctx.Data["IsTextFile"] = st.IsText()
|
||||
ctx.Data["FileSize"] = meta.Size
|
||||
// FIXME: the last field is the URL-base64-encoded filename, it should not be "direct"
|
||||
ctx.Data["RawFileLink"] = fmt.Sprintf("%s%s/%s.git/info/lfs/objects/%s/%s", setting.AppURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid), "direct")
|
||||
ctx.Data["RawFileLink"] = fmt.Sprintf("%s/%s/%s.git/info/lfs/objects/%s", setting.AppSubURL, url.PathEscape(ctx.Repo.Repository.OwnerName), url.PathEscape(ctx.Repo.Repository.Name), url.PathEscape(meta.Oid))
|
||||
switch {
|
||||
case st.IsRepresentableAsText():
|
||||
if meta.Size >= setting.UI.MaxDisplayFileSize {
|
||||
|
||||
@@ -252,9 +252,9 @@
|
||||
{{end}}
|
||||
{{if .CacheConn}}
|
||||
<dt>{{ctx.Locale.Tr "admin.config.cache_conn"}}</dt>
|
||||
<dd><code>{{.CacheConn}}</code></dd>
|
||||
<dd>{{.CacheConn}}</dd>
|
||||
<dt>{{ctx.Locale.Tr "admin.config.cache_item_ttl"}}</dt>
|
||||
<dd><code>{{.CacheItemTTL}}</code></dd>
|
||||
<dd>{{.CacheItemTTL}}</dd>
|
||||
{{end}}
|
||||
<div class="divider"></div>
|
||||
<dt class="tw-py-1 tw-flex tw-items-center">{{ctx.Locale.Tr "admin.config.cache_test"}}</dt>
|
||||
@@ -275,7 +275,7 @@
|
||||
<dt>{{ctx.Locale.Tr "admin.config.session_provider"}}</dt>
|
||||
<dd>{{.SessionConfig.Provider}}</dd>
|
||||
<dt>{{ctx.Locale.Tr "admin.config.provider_config"}}</dt>
|
||||
<dd><code>{{if .SessionConfig.ProviderConfig}}{{.SessionConfig.ProviderConfig}}{{else}}-{{end}}</code></dd>
|
||||
<dd>{{if .SessionConfig.ProviderConfig}}{{.SessionConfig.ProviderConfig}}{{else}}-{{end}}</dd>
|
||||
<dt>{{ctx.Locale.Tr "admin.config.cookie_name"}}</dt>
|
||||
<dd>{{.SessionConfig.CookieName}}</dd>
|
||||
<dt>{{ctx.Locale.Tr "admin.config.gc_interval_time"}}</dt>
|
||||
@@ -301,7 +301,7 @@
|
||||
<dt>{{ctx.Locale.Tr "admin.config.git_max_diff_files"}}</dt>
|
||||
<dd>{{.Git.MaxGitDiffFiles}}</dd>
|
||||
<dt>{{ctx.Locale.Tr "admin.config.git_gc_args"}}</dt>
|
||||
<dd><code>{{.Git.GCArgs}}</code></dd>
|
||||
<dd>{{.Git.GCArgs}}</dd>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
@@ -330,7 +330,7 @@
|
||||
|
||||
{{if .Loggers.access.IsEnabled}}
|
||||
<dt>{{ctx.Locale.Tr "admin.config.access_log_template"}}</dt>
|
||||
<dd><code>{{$.AccessLogTemplate}}</code></dd>
|
||||
<dd>{{$.AccessLogTemplate}}</dd>
|
||||
{{end}}
|
||||
|
||||
{{range $loggerName, $loggerDetail := .Loggers}}
|
||||
|
||||
@@ -46,8 +46,8 @@
|
||||
<div class="item tw-flex tw-items-center">
|
||||
<span class="icon tw-mr-4">{{svg "octicon-dot-fill" 16}}</span>
|
||||
<div class="content tw-flex-1">
|
||||
<div class="header"><code>{{.Function}}</code></div>
|
||||
<div class="description"><code>{{.File}}:{{.Line}}</code></div>
|
||||
<div class="header">{{.Function}}</div>
|
||||
<div class="description">{{.File}}:{{.Line}}</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
{{if .Details}}
|
||||
<details>
|
||||
<summary>{{.Summary}}</summary>
|
||||
<code>{{.Details | SanitizeHTML}}</code>
|
||||
{{.Details | SanitizeHTML}}
|
||||
</details>
|
||||
{{else}}
|
||||
<div>
|
||||
|
||||
@@ -65,7 +65,9 @@
|
||||
</div>
|
||||
|
||||
<div class="ui container project-description">
|
||||
{{$.Project.RenderedContent}}
|
||||
<div class="render-content markup">
|
||||
{{$.Project.RenderedContent}}
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,11 +12,11 @@
|
||||
{{else if eq .status "cancelled"}}
|
||||
{{svg "octicon-stop" $size (printf "text grey %s" $className)}}
|
||||
{{else if eq .status "waiting"}}
|
||||
{{svg "octicon-clock" $size (printf "text yellow %s" $className)}}
|
||||
{{svg "octicon-circle" $size (printf "text grey %s" $className)}}
|
||||
{{else if eq .status "blocked"}}
|
||||
{{svg "octicon-blocked" $size (printf "text yellow %s" $className)}}
|
||||
{{else if eq .status "running"}}
|
||||
{{svg "octicon-meter" $size (printf "text yellow circular-spin %s" $className)}}
|
||||
{{svg "gitea-running" $size (printf "text yellow circular-spin %s" $className)}}
|
||||
{{else}}{{/*failure, unknown*/}}
|
||||
{{svg "octicon-x-circle-fill" $size (printf "text red %s" $className)}}
|
||||
{{end}}
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="lines-num">{{.LineNums}}</td>
|
||||
<td class="lines-code"><pre><code class="{{.HighlightClass}}"><ol>{{.FileContent}}</ol></code></pre></td>
|
||||
<td class="lines-code"><pre>{{.FileContent}}</pre></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -68,7 +68,7 @@
|
||||
{{range $key, $val := .RequestInfo.Headers}}<strong>{{$key}}:</strong> {{$val}}
|
||||
{{end}}</pre>
|
||||
<h5>{{ctx.Locale.Tr "repo.settings.webhook.payload"}}</h5>
|
||||
<pre class="webhook-info"><code class="json">{{or .RequestInfo.Body .PayloadContent}}</code></pre>
|
||||
<pre class="webhook-info">{{or .RequestInfo.Body .PayloadContent}}</pre>
|
||||
{{else}}
|
||||
-
|
||||
{{end}}
|
||||
@@ -79,7 +79,7 @@
|
||||
<pre class="webhook-info">{{range $key, $val := .ResponseInfo.Headers}}<strong>{{$key}}:</strong> {{$val}}
|
||||
{{end}}</pre>
|
||||
<h5>{{ctx.Locale.Tr "repo.settings.webhook.body"}}</h5>
|
||||
<pre class="webhook-info"><code>{{.ResponseInfo.Body}}</code></pre>
|
||||
<pre class="webhook-info">{{.ResponseInfo.Body}}</pre>
|
||||
{{else}}
|
||||
-
|
||||
{{end}}
|
||||
|
||||
45
templates/swagger/v1_json.tmpl
generated
45
templates/swagger/v1_json.tmpl
generated
@@ -7844,7 +7844,7 @@
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"$ref": "#/definitions/UpdateFileOptions"
|
||||
"$ref": "#/definitions/ApplyDiffPatchFileOptions"
|
||||
}
|
||||
}
|
||||
],
|
||||
@@ -21645,6 +21645,49 @@
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"ApplyDiffPatchFileOptions": {
|
||||
"description": "ApplyDiffPatchFileOptions options for applying a diff patch\nNote: `author` and `committer` are optional (if only one is given, it will be used for the other, otherwise the authenticated user will be used)",
|
||||
"type": "object",
|
||||
"required": [
|
||||
"content"
|
||||
],
|
||||
"properties": {
|
||||
"author": {
|
||||
"$ref": "#/definitions/Identity"
|
||||
},
|
||||
"branch": {
|
||||
"description": "branch (optional) to base this file from. if not given, the default branch is used",
|
||||
"type": "string",
|
||||
"x-go-name": "BranchName"
|
||||
},
|
||||
"committer": {
|
||||
"$ref": "#/definitions/Identity"
|
||||
},
|
||||
"content": {
|
||||
"type": "string",
|
||||
"x-go-name": "Content"
|
||||
},
|
||||
"dates": {
|
||||
"$ref": "#/definitions/CommitDateOptions"
|
||||
},
|
||||
"message": {
|
||||
"description": "message (optional) for the commit of this file. if not supplied, a default message will be used",
|
||||
"type": "string",
|
||||
"x-go-name": "Message"
|
||||
},
|
||||
"new_branch": {
|
||||
"description": "new_branch (optional) will make a new branch from `branch` before creating the file",
|
||||
"type": "string",
|
||||
"x-go-name": "NewBranchName"
|
||||
},
|
||||
"signoff": {
|
||||
"description": "Add a Signed-off-by trailer by the committer at the end of the commit log message.",
|
||||
"type": "boolean",
|
||||
"x-go-name": "Signoff"
|
||||
}
|
||||
},
|
||||
"x-go-package": "code.gitea.io/gitea/modules/structs"
|
||||
},
|
||||
"Attachment": {
|
||||
"description": "Attachment a generic attachment",
|
||||
"type": "object",
|
||||
|
||||
@@ -21,8 +21,8 @@
|
||||
<label for="token">{{ctx.Locale.Tr "settings.gpg_token"}}</label>
|
||||
<input readonly="" value="{{.TokenToSign}}">
|
||||
<div class="help">
|
||||
<p>{{ctx.Locale.Tr "settings.gpg_token_help"}}</p>
|
||||
<p><code>{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` .TokenToSign .PaddedKeyID}}</code></p>
|
||||
{{ctx.Locale.Tr "settings.gpg_token_help"}}
|
||||
<pre class="command-block">{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` .TokenToSign .PaddedKeyID}}</pre>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
@@ -89,8 +89,8 @@
|
||||
<label for="token">{{ctx.Locale.Tr "settings.gpg_token"}}</label>
|
||||
<input readonly="" value="{{$.TokenToSign}}">
|
||||
<div class="help">
|
||||
<p>{{ctx.Locale.Tr "settings.gpg_token_help"}}</p>
|
||||
<p><code>{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` $.TokenToSign .PaddedKeyID}}</code></p>
|
||||
{{ctx.Locale.Tr "settings.gpg_token_help"}}
|
||||
<pre class="command-block">{{printf `echo "%s" | gpg -a --default-key %s --detach-sig` $.TokenToSign .PaddedKeyID}}</pre>
|
||||
</div>
|
||||
<br>
|
||||
</div>
|
||||
|
||||
@@ -77,16 +77,15 @@
|
||||
<label for="token">{{ctx.Locale.Tr "settings.ssh_token"}}</label>
|
||||
<input readonly="" value="{{$.TokenToSign}}">
|
||||
<div class="help">
|
||||
<p>{{ctx.Locale.Tr "settings.ssh_token_help"}}</p>
|
||||
<p><code>echo -n '{{$.TokenToSign}}' | ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey</code></p>
|
||||
{{ctx.Locale.Tr "settings.ssh_token_help"}}
|
||||
<pre class="command-block">echo -n '{{$.TokenToSign}}' | ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey</pre>
|
||||
<details>
|
||||
<summary>Windows PowerShell</summary>
|
||||
<p><code>cmd /c "<NUL set /p=`"{{$.TokenToSign}}`"| ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey"</code></p>
|
||||
<pre class="command-block">cmd /c "<NUL set /p=`"{{$.TokenToSign}}`"| ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey"</pre>
|
||||
</details>
|
||||
<br>
|
||||
<details>
|
||||
<summary>Windows CMD</summary>
|
||||
<p><code>set /p={{$.TokenToSign}}| ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey</code></p>
|
||||
<pre class="command-block">set /p={{$.TokenToSign}}| ssh-keygen -Y sign -n gitea -f /path_to_PrivateKey_or_RelatedPublicKey</pre>
|
||||
</details>
|
||||
</div>
|
||||
<br>
|
||||
|
||||
89
tests/integration/api_repo_file_diffpatch_test.go
Normal file
89
tests/integration/api_repo_file_diffpatch_test.go
Normal file
@@ -0,0 +1,89 @@
|
||||
// Copyright 2025 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package integration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
auth_model "code.gitea.io/gitea/models/auth"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func getApplyDiffPatchFileOptions() *api.ApplyDiffPatchFileOptions {
|
||||
return &api.ApplyDiffPatchFileOptions{
|
||||
FileOptions: api.FileOptions{
|
||||
BranchName: "master",
|
||||
},
|
||||
Content: `diff --git a/patch-file-1.txt b/patch-file-1.txt
|
||||
new file mode 100644
|
||||
index 0000000000..aaaaaaaaaa
|
||||
--- /dev/null
|
||||
+++ b/patch-file-1.txt
|
||||
@@ -0,0 +1 @@
|
||||
+File 1
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIApplyDiffPatchFileOptions(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, u *url.URL) {
|
||||
user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // owner of the repo1 & repo16
|
||||
org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 3}) // owner of the repo3, is an org
|
||||
user4 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) // owner of neither repos
|
||||
repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // public repo
|
||||
repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) // public repo
|
||||
repo16 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 16}) // private repo
|
||||
|
||||
session2 := loginUser(t, user2.Name)
|
||||
token2 := getTokenForLoggedInUser(t, session2, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
session4 := loginUser(t, user4.Name)
|
||||
token4 := getTokenForLoggedInUser(t, session4, auth_model.AccessTokenScopeWriteRepository, auth_model.AccessTokenScopeWriteUser)
|
||||
|
||||
req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/diffpatch", getApplyDiffPatchFileOptions()).AddTokenAuth(token2)
|
||||
resp := MakeRequest(t, req, http.StatusCreated)
|
||||
var fileResponse api.FileResponse
|
||||
DecodeJSON(t, resp, &fileResponse)
|
||||
assert.Nil(t, fileResponse.Content)
|
||||
assert.NotEmpty(t, fileResponse.Commit.HTMLURL)
|
||||
req = NewRequest(t, "GET", "/api/v1/repos/user2/repo1/raw/patch-file-1.txt")
|
||||
resp = MakeRequest(t, req, http.StatusOK)
|
||||
assert.Equal(t, "File 1\n", resp.Body.String())
|
||||
|
||||
// Test creating a file in repo1 by user4 who does not have write access
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/diffpatch", user2.Name, repo16.Name), getApplyDiffPatchFileOptions()).
|
||||
AddTokenAuth(token4)
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Tests a repo with no token given so will fail
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/diffpatch", user2.Name, repo16.Name), getApplyDiffPatchFileOptions())
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test using access token for a private repo that the user of the token owns
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/diffpatch", user2.Name, repo16.Name), getApplyDiffPatchFileOptions()).
|
||||
AddTokenAuth(token2)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
// Test using org repo "org3/repo3" where user2 is a collaborator
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/diffpatch", org3.Name, repo3.Name), getApplyDiffPatchFileOptions()).
|
||||
AddTokenAuth(token2)
|
||||
MakeRequest(t, req, http.StatusCreated)
|
||||
|
||||
// Test using org repo "org3/repo3" with no user token
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/diffpatch", org3.Name, repo3.Name), getApplyDiffPatchFileOptions())
|
||||
MakeRequest(t, req, http.StatusNotFound)
|
||||
|
||||
// Test using repo "user2/repo1" where user4 is a NOT collaborator
|
||||
req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/repos/%s/%s/diffpatch", user2.Name, repo1.Name), getApplyDiffPatchFileOptions()).
|
||||
AddTokenAuth(token4)
|
||||
MakeRequest(t, req, http.StatusForbidden)
|
||||
})
|
||||
}
|
||||
@@ -101,11 +101,13 @@ samp,
|
||||
font-size: 0.95em; /* compensate for monospace fonts being usually slightly larger */
|
||||
}
|
||||
|
||||
code {
|
||||
/* there are many <code> blocks in non-markup(.markup code) / non-code-diff(code.code-inner) containers (for example: translation strings, etc),
|
||||
so we need to make <code> have default global styles, ".markup code" has its own styles and doesn't conflict, but `.code-inner` is special.
|
||||
TODO: in the future, we should use `div` instead of `code` for `.code-inner` because it is a container for highlighted code line */
|
||||
code:not(.code-inner) {
|
||||
padding: 1px 4px;
|
||||
border-radius: var(--border-radius);
|
||||
background-color: var(--color-label-bg);
|
||||
color: var(--color-label-text);
|
||||
}
|
||||
|
||||
b,
|
||||
|
||||
@@ -228,6 +228,12 @@ textarea:focus,
|
||||
color: var(--color-text-light-1);
|
||||
}
|
||||
|
||||
.form .help pre.command-block {
|
||||
white-space: pre-wrap;
|
||||
overflow-wrap: anywhere;
|
||||
margin: 0.25em 0 0.25em 1em;
|
||||
}
|
||||
|
||||
.m-captcha-style {
|
||||
width: 100%;
|
||||
height: 5em;
|
||||
|
||||
@@ -21,10 +21,10 @@ withDefaults(defineProps<{
|
||||
<span :data-tooltip-content="localeStatus ?? status" v-if="status">
|
||||
<SvgIcon name="octicon-check-circle-fill" class="text green" :size="size" :class="className" v-if="status === 'success'"/>
|
||||
<SvgIcon name="octicon-skip" class="text grey" :size="size" :class="className" v-else-if="status === 'skipped'"/>
|
||||
<SvgIcon name="octicon-stop" class="text yellow" :size="size" :class="className" v-else-if="status === 'cancelled'"/>
|
||||
<SvgIcon name="octicon-clock" class="text yellow" :size="size" :class="className" v-else-if="status === 'waiting'"/>
|
||||
<SvgIcon name="octicon-stop" class="text grey" :size="size" :class="className" v-else-if="status === 'cancelled'"/>
|
||||
<SvgIcon name="octicon-circle" class="text grey" :size="size" :class="className" v-else-if="status === 'waiting'"/>
|
||||
<SvgIcon name="octicon-blocked" class="text yellow" :size="size" :class="className" v-else-if="status === 'blocked'"/>
|
||||
<SvgIcon name="octicon-meter" class="text yellow" :size="size" :class="'circular-spin ' + className" v-else-if="status === 'running'"/>
|
||||
<SvgIcon name="gitea-running" class="text yellow" :size="size" :class="'circular-spin ' + className" v-else-if="status === 'running'"/>
|
||||
<SvgIcon name="octicon-x-circle-fill" class="text red" :size="size" v-else/><!-- failure, unknown -->
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -5,6 +5,7 @@ import giteaDoubleChevronLeft from '../../public/assets/img/svg/gitea-double-che
|
||||
import giteaDoubleChevronRight from '../../public/assets/img/svg/gitea-double-chevron-right.svg';
|
||||
import giteaEmptyCheckbox from '../../public/assets/img/svg/gitea-empty-checkbox.svg';
|
||||
import giteaExclamation from '../../public/assets/img/svg/gitea-exclamation.svg';
|
||||
import giteaRunning from '../../public/assets/img/svg/gitea-running.svg';
|
||||
import octiconArchive from '../../public/assets/img/svg/octicon-archive.svg';
|
||||
import octiconArrowSwitch from '../../public/assets/img/svg/octicon-arrow-switch.svg';
|
||||
import octiconBlocked from '../../public/assets/img/svg/octicon-blocked.svg';
|
||||
@@ -15,6 +16,7 @@ import octiconCheckCircleFill from '../../public/assets/img/svg/octicon-check-ci
|
||||
import octiconChevronDown from '../../public/assets/img/svg/octicon-chevron-down.svg';
|
||||
import octiconChevronLeft from '../../public/assets/img/svg/octicon-chevron-left.svg';
|
||||
import octiconChevronRight from '../../public/assets/img/svg/octicon-chevron-right.svg';
|
||||
import octiconCircle from '../../public/assets/img/svg/octicon-circle.svg';
|
||||
import octiconClock from '../../public/assets/img/svg/octicon-clock.svg';
|
||||
import octiconCode from '../../public/assets/img/svg/octicon-code.svg';
|
||||
import octiconColumns from '../../public/assets/img/svg/octicon-columns.svg';
|
||||
@@ -84,6 +86,7 @@ const svgs = {
|
||||
'gitea-double-chevron-right': giteaDoubleChevronRight,
|
||||
'gitea-empty-checkbox': giteaEmptyCheckbox,
|
||||
'gitea-exclamation': giteaExclamation,
|
||||
'gitea-running': giteaRunning,
|
||||
'octicon-archive': octiconArchive,
|
||||
'octicon-arrow-switch': octiconArrowSwitch,
|
||||
'octicon-blocked': octiconBlocked,
|
||||
@@ -94,6 +97,7 @@ const svgs = {
|
||||
'octicon-chevron-down': octiconChevronDown,
|
||||
'octicon-chevron-left': octiconChevronLeft,
|
||||
'octicon-chevron-right': octiconChevronRight,
|
||||
'octicon-circle': octiconCircle,
|
||||
'octicon-clock': octiconClock,
|
||||
'octicon-code': octiconCode,
|
||||
'octicon-columns': octiconColumns,
|
||||
|
||||
5
web_src/svg/gitea-running.svg
Normal file
5
web_src/svg/gitea-running.svg
Normal file
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" width="16" height="16">
|
||||
<path fill="none" stroke="currentColor" stroke-width="2" d="M3.05 3.05a7 7 0 1 1 9.9 9.9 7 7 0 0 1-9.9-9.9Z" opacity=".5"></path>
|
||||
<path fill="currentColor" fill-rule="evenodd" d="M8 4a4 4 0 1 0 0 8 4 4 0 0 0 0-8Z" clip-rule="evenodd"></path>
|
||||
<path fill="currentColor" d="M14 8a6 6 0 0 0-6-6V0a8 8 0 0 1 8 8h-2Z"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 416 B |
Reference in New Issue
Block a user