From b30b7df9f4b4e1ae7ec55750bca7577bf88abd0b Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 3 May 2024 03:48:24 +0200 Subject: [PATCH 01/14] Fix body margin shifting with modals, fix error on project column edit (#30831) Fixes: https://github.com/go-gitea/gitea/issues/30816, regression from https://github.com/go-gitea/gitea/pull/30723. Fixes: https://github.com/go-gitea/gitea/pull/30815, regression from https://github.com/go-gitea/gitea/pull/30723. Fomantic [expects a callback](https://github.com/fomantic/Fomantic-UI/blob/59d9b409879ad9413ea0a3efa4ab2e51017ad9b9/src/definitions/modules/modal.js#L530-L534) to be called during `hide` which we did not do, so it could never remove the margin it added to `body`. I do observe the body content shifting to right by 1px when modal opens, but this is a bug that existed on v1.21 as well, so not a regression. --------- Co-authored-by: wxiaoguang --- web_src/js/modules/fomantic/dimmer.js | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/web_src/js/modules/fomantic/dimmer.js b/web_src/js/modules/fomantic/dimmer.js index f434e1ca59..e027838d4a 100644 --- a/web_src/js/modules/fomantic/dimmer.js +++ b/web_src/js/modules/fomantic/dimmer.js @@ -3,11 +3,12 @@ import {queryElemChildren} from '../../utils/dom.js'; export function initFomanticDimmer() { // stand-in for removed dimmer module - $.fn.dimmer = function (arg0, $el) { + $.fn.dimmer = function (arg0, arg1) { if (arg0 === 'add content') { + const $el = arg1; const existingDimmer = document.querySelector('body > .ui.dimmer'); if (existingDimmer) { - queryElemChildren(existingDimmer, '*', (el) => el.remove()); + queryElemChildren(existingDimmer, '*', (el) => el.classList.add('hidden')); this._dimmer = existingDimmer; } else { this._dimmer = document.createElement('div'); @@ -21,8 +22,10 @@ export function initFomanticDimmer() { this._dimmer.classList.add('active'); document.body.classList.add('tw-overflow-hidden'); } else if (arg0 === 'hide') { + const cb = arg1; this._dimmer.classList.remove('active'); document.body.classList.remove('tw-overflow-hidden'); + cb(); } return this; }; From c4e875402bd8e787c21bbd4ea07cbb69a8ceef27 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 3 May 2024 04:12:10 +0200 Subject: [PATCH 02/14] Fix JS error on pull request page (#30838) Fix this error seen on PR page, regression from https://github.com/go-gitea/gitea/pull/30803: Co-authored-by: wxiaoguang --- web_src/js/features/repo-legacy.js | 1 + 1 file changed, 1 insertion(+) diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 670e60def0..b65938b045 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -57,6 +57,7 @@ export function initRepoCommentForm() { function initBranchSelector() { const elSelectBranch = document.querySelector('.ui.dropdown.select-branch'); + if (!elSelectBranch) return; const isForNewIssue = elSelectBranch.getAttribute('data-for-new-issue') === 'true'; const $selectBranch = $(elSelectBranch); From 53b55223d167c3fc996dd0278a656f421408ace7 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 3 May 2024 10:39:36 +0800 Subject: [PATCH 03/14] Ignore useless error message "broken pipe" (#30801) Fix #30792 --- routers/api/packages/maven/maven.go | 4 +--- services/context/base.go | 4 +--- services/context/context_response.go | 3 ++- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/routers/api/packages/maven/maven.go b/routers/api/packages/maven/maven.go index 27f0578db7..cb15eae682 100644 --- a/routers/api/packages/maven/maven.go +++ b/routers/api/packages/maven/maven.go @@ -140,9 +140,7 @@ func serveMavenMetadata(ctx *context.Context, params parameters) { ctx.Resp.Header().Set("Content-Length", strconv.Itoa(len(xmlMetadataWithHeader))) ctx.Resp.Header().Set("Content-Type", contentTypeXML) - if _, err := ctx.Resp.Write(xmlMetadataWithHeader); err != nil { - log.Error("write bytes failed: %v", err) - } + _, _ = ctx.Resp.Write(xmlMetadataWithHeader) } func servePackageFile(ctx *context.Context, params parameters, serveContent bool) { diff --git a/services/context/base.go b/services/context/base.go index 62fb743714..05b8ab1b9b 100644 --- a/services/context/base.go +++ b/services/context/base.go @@ -234,9 +234,7 @@ func (b *Base) plainTextInternal(skip, status int, bs []byte) { b.Resp.Header().Set("Content-Type", "text/plain;charset=utf-8") b.Resp.Header().Set("X-Content-Type-Options", "nosniff") b.Resp.WriteHeader(status) - if _, err := b.Resp.Write(bs); err != nil { - log.ErrorWithSkip(skip, "plainTextInternal (status=%d): write bytes failed: %v", status, err) - } + _, _ = b.Resp.Write(bs) } // PlainTextBytes renders bytes as plain text diff --git a/services/context/context_response.go b/services/context/context_response.go index d7fd18acac..87c34c35ed 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -13,6 +13,7 @@ import ( "path" "strconv" "strings" + "syscall" "time" user_model "code.gitea.io/gitea/models/user" @@ -77,7 +78,7 @@ func (ctx *Context) HTML(status int, name base.TplName) { } err := ctx.Render.HTML(ctx.Resp, status, string(name), ctx.Data, ctx.TemplateContext) - if err == nil { + if err == nil || errors.Is(err, syscall.EPIPE) { return } From a50026e2f30897904704895362da0fb12c7e5b26 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Fri, 3 May 2024 15:11:51 +0900 Subject: [PATCH 04/14] Fix no edit history after editing issue's title and content (#30814) Fix #30807 reuse functions in services --- models/issues/issue_update.go | 56 ----------------------------- modules/structs/pull.go | 2 +- routers/api/v1/repo/issue.go | 36 +++++++++---------- routers/api/v1/repo/pull.go | 37 +++++++++---------- tests/integration/api_issue_test.go | 4 +++ tests/integration/api_pull_test.go | 26 +++++++++----- 6 files changed, 56 insertions(+), 105 deletions(-) diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index ef96e1ee50..147b7eb3b9 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -429,62 +429,6 @@ func UpdateIssueMentions(ctx context.Context, issueID int64, mentions []*user_mo return nil } -// UpdateIssueByAPI updates all allowed fields of given issue. -// If the issue status is changed a statusChangeComment is returned -// similarly if the title is changed the titleChanged bool is set to true -func UpdateIssueByAPI(ctx context.Context, issue *Issue, doer *user_model.User) (statusChangeComment *Comment, titleChanged bool, err error) { - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, false, err - } - defer committer.Close() - - if err := issue.LoadRepo(ctx); err != nil { - return nil, false, fmt.Errorf("loadRepo: %w", err) - } - - // Reload the issue - currentIssue, err := GetIssueByID(ctx, issue.ID) - if err != nil { - return nil, false, err - } - - if _, err := db.GetEngine(ctx).ID(issue.ID).Cols( - "name", "content", "milestone_id", "priority", - "deadline_unix", "updated_unix", "is_locked"). - Update(issue); err != nil { - return nil, false, err - } - - titleChanged = currentIssue.Title != issue.Title - if titleChanged { - opts := &CreateCommentOptions{ - Type: CommentTypeChangeTitle, - Doer: doer, - Repo: issue.Repo, - Issue: issue, - OldTitle: currentIssue.Title, - NewTitle: issue.Title, - } - _, err := CreateComment(ctx, opts) - if err != nil { - return nil, false, fmt.Errorf("createComment: %w", err) - } - } - - if currentIssue.IsClosed != issue.IsClosed { - statusChangeComment, err = doChangeIssueStatus(ctx, issue, doer, false) - if err != nil { - return nil, false, err - } - } - - if err := issue.AddCrossReferences(ctx, doer, true); err != nil { - return nil, false, err - } - return statusChangeComment, titleChanged, committer.Commit() -} - // UpdateIssueDeadline updates an issue deadline and adds comments. Setting a deadline to 0 means deleting it. func UpdateIssueDeadline(ctx context.Context, issue *Issue, deadlineUnix timeutil.TimeStamp, doer *user_model.User) (err error) { // if the deadline hasn't changed do nothing diff --git a/modules/structs/pull.go b/modules/structs/pull.go index 05a8d59633..b04def52b8 100644 --- a/modules/structs/pull.go +++ b/modules/structs/pull.go @@ -85,7 +85,7 @@ type CreatePullRequestOption struct { // EditPullRequestOption options when modify pull request type EditPullRequestOption struct { Title string `json:"title"` - Body string `json:"body"` + Body *string `json:"body"` Base string `json:"base"` Assignee string `json:"assignee"` Assignees []string `json:"assignees"` diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index dfe6d31f74..b91fbc33bf 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -29,7 +29,6 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" issue_service "code.gitea.io/gitea/services/issue" - notify_service "code.gitea.io/gitea/services/notify" ) // SearchIssues searches for issues across the repositories that the user has access to @@ -803,12 +802,19 @@ func EditIssue(ctx *context.APIContext) { return } - oldTitle := issue.Title if len(form.Title) > 0 { - issue.Title = form.Title + err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeTitle", err) + return + } } if form.Body != nil { - issue.Content = *form.Body + err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeContent", err) + return + } } if form.Ref != nil { err = issue_service.ChangeIssueRef(ctx, issue, ctx.Doer, *form.Ref) @@ -880,24 +886,14 @@ func EditIssue(ctx *context.APIContext) { return } } - issue.IsClosed = api.StateClosed == api.StateType(*form.State) - } - statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer) - if err != nil { - if issues_model.IsErrDependenciesLeft(err) { - ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") + if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil { + if issues_model.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this issue because it still has open dependencies") + return + } + ctx.Error(http.StatusInternalServerError, "ChangeStatus", err) return } - ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err) - return - } - - if titleChanged { - notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle) - } - - if statusChangeComment != nil { - notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed) } // Refetch from database to assign some automatic values diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 4129f94ac3..8bd4ddf64b 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -602,12 +602,19 @@ func EditPullRequest(ctx *context.APIContext) { return } - oldTitle := issue.Title if len(form.Title) > 0 { - issue.Title = form.Title + err = issue_service.ChangeTitle(ctx, issue, ctx.Doer, form.Title) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeTitle", err) + return + } } - if len(form.Body) > 0 { - issue.Content = form.Body + if form.Body != nil { + err = issue_service.ChangeContent(ctx, issue, ctx.Doer, *form.Body) + if err != nil { + ctx.Error(http.StatusInternalServerError, "ChangeContent", err) + return + } } // Update or remove deadline if set @@ -686,24 +693,14 @@ func EditPullRequest(ctx *context.APIContext) { ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged") return } - issue.IsClosed = api.StateClosed == api.StateType(*form.State) - } - statusChangeComment, titleChanged, err := issues_model.UpdateIssueByAPI(ctx, issue, ctx.Doer) - if err != nil { - if issues_model.IsErrDependenciesLeft(err) { - ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies") + if err := issue_service.ChangeStatus(ctx, issue, ctx.Doer, "", api.StateClosed == api.StateType(*form.State)); err != nil { + if issues_model.IsErrDependenciesLeft(err) { + ctx.Error(http.StatusPreconditionFailed, "DependenciesLeft", "cannot close this pull request because it still has open dependencies") + return + } + ctx.Error(http.StatusInternalServerError, "ChangeStatus", err) return } - ctx.Error(http.StatusInternalServerError, "UpdateIssueByAPI", err) - return - } - - if titleChanged { - notify_service.IssueChangeTitle(ctx, ctx.Doer, issue, oldTitle) - } - - if statusChangeComment != nil { - notify_service.IssueChangeStatus(ctx, ctx.Doer, "", issue, statusChangeComment, issue.IsClosed) } // change pull target branch diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 17b4e5bd71..8bfb6fabe2 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -194,6 +194,10 @@ func TestAPIEditIssue(t *testing.T) { issueAfter := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 10}) repoAfter := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: issueBefore.RepoID}) + // check comment history + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: issueAfter.ID, OldTitle: issueBefore.Title, NewTitle: title}) + unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: issueAfter.ID, ContentText: body, IsFirstCreated: false}) + // check deleted user assert.Equal(t, int64(500), issueAfter.PosterID) assert.NoError(t, issueAfter.LoadAttributes(db.DefaultContext)) diff --git a/tests/integration/api_pull_test.go b/tests/integration/api_pull_test.go index bb479caf89..9bf0d3d745 100644 --- a/tests/integration/api_pull_test.go +++ b/tests/integration/api_pull_test.go @@ -223,23 +223,33 @@ func TestAPIEditPull(t *testing.T) { session := loginUser(t, owner10.Name) token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + title := "create a success pr" req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", owner10.Name, repo10.Name), &api.CreatePullRequestOption{ Head: "develop", Base: "master", - Title: "create a success pr", + Title: title, }).AddTokenAuth(token) - pull := new(api.PullRequest) + apiPull := new(api.PullRequest) resp := MakeRequest(t, req, http.StatusCreated) - DecodeJSON(t, resp, pull) - assert.EqualValues(t, "master", pull.Base.Name) + DecodeJSON(t, resp, apiPull) + assert.EqualValues(t, "master", apiPull.Base.Name) - req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{ + newTitle := "edit a this pr" + newBody := "edited body" + req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, apiPull.Index), &api.EditPullRequestOption{ Base: "feature/1", - Title: "edit a this pr", + Title: newTitle, + Body: &newBody, }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusCreated) - DecodeJSON(t, resp, pull) - assert.EqualValues(t, "feature/1", pull.Base.Name) + DecodeJSON(t, resp, apiPull) + assert.EqualValues(t, "feature/1", apiPull.Base.Name) + // check comment history + pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: apiPull.ID}) + err := pull.LoadIssue(db.DefaultContext) + assert.NoError(t, err) + unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{IssueID: pull.Issue.ID, OldTitle: title, NewTitle: newTitle}) + unittest.AssertExistsAndLoadBean(t, &issues_model.ContentHistory{IssueID: pull.Issue.ID, ContentText: newBody, IsFirstCreated: false}) req = NewRequestWithJSON(t, http.MethodPatch, fmt.Sprintf("/api/v1/repos/%s/%s/pulls/%d", owner10.Name, repo10.Name, pull.Index), &api.EditPullRequestOption{ Base: "not-exist", From 9f0ef3621a3b63ccbe93f302a446b67dc54ad725 Mon Sep 17 00:00:00 2001 From: Kemal Zebari <60799661+kemzeb@users.noreply.github.com> Date: Fri, 3 May 2024 00:58:31 -0700 Subject: [PATCH 05/14] Don't only list code-enabled repositories when using repository API (#30817) We should be listing all repositories by default. Fixes #28483. --- routers/api/v1/user/repo.go | 4 +--- tests/integration/api_repo_test.go | 34 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/routers/api/v1/user/repo.go b/routers/api/v1/user/repo.go index 81f8e0f3fe..d0264d6b5a 100644 --- a/routers/api/v1/user/repo.go +++ b/routers/api/v1/user/repo.go @@ -6,10 +6,8 @@ package user import ( "net/http" - "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" - unit_model "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" @@ -44,7 +42,7 @@ func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) { ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err) return } - if ctx.IsSigned && ctx.Doer.IsAdmin || permission.UnitAccessMode(unit_model.TypeCode) >= perm.AccessModeRead { + if ctx.IsSigned && ctx.Doer.IsAdmin || permission.HasAnyUnitAccess() { apiRepos = append(apiRepos, convert.ToRepo(ctx, repos[i], permission)) } } diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index f33827e58b..716da762e5 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -13,6 +13,7 @@ import ( "code.gitea.io/gitea/models/db" access_model "code.gitea.io/gitea/models/perm/access" repo_model "code.gitea.io/gitea/models/repo" + unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" @@ -326,6 +327,39 @@ func TestAPIOrgRepos(t *testing.T) { } } +// See issue #28483. Tests to make sure we consider more than just code unit-enabled repositories. +func TestAPIOrgReposWithCodeUnitDisabled(t *testing.T) { + defer tests.PrepareTestEnv(t)() + repo21 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: "repo21"}) + org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo21.OwnerID}) + + // Disable code repository unit. + var units []unit_model.Type + units = append(units, unit_model.TypeCode) + + if err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo21, nil, units); err != nil { + assert.Fail(t, "should have been able to delete code repository unit; failed to %v", err) + } + assert.False(t, repo21.UnitEnabled(db.DefaultContext, unit_model.TypeCode)) + + session := loginUser(t, "user2") + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadOrganization) + + req := NewRequestf(t, "GET", "/api/v1/orgs/%s/repos", org3.Name). + AddTokenAuth(token) + + resp := MakeRequest(t, req, http.StatusOK) + var apiRepos []*api.Repository + DecodeJSON(t, resp, &apiRepos) + + var repoNames []string + for _, r := range apiRepos { + repoNames = append(repoNames, r.Name) + } + + assert.Contains(t, repoNames, repo21.Name) +} + func TestAPIGetRepoByIDUnauthorized(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 4}) From 0f3e717a1abb2b2161b87dac557beb6475224a2e Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 3 May 2024 17:13:48 +0800 Subject: [PATCH 06/14] Improve grep search (#30843) Reduce the context line number to 1, make "git grep" search respect the include/exclude patter, and fix #30785 --- modules/git/grep.go | 2 ++ modules/git/grep_test.go | 20 ++++++++++++++++++++ modules/setting/glob.go | 32 ++++++++++++++++++++++++++++++++ modules/setting/indexer.go | 12 +++++------- routers/web/repo/search.go | 18 +++++++++++++++++- routers/web/repo/search_test.go | 19 +++++++++++++++++++ 6 files changed, 95 insertions(+), 8 deletions(-) create mode 100644 modules/setting/glob.go create mode 100644 routers/web/repo/search_test.go diff --git a/modules/git/grep.go b/modules/git/grep.go index e7d238e586..bf6b41a886 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -29,6 +29,7 @@ type GrepOptions struct { ContextLineNumber int IsFuzzy bool MaxLineLength int // the maximum length of a line to parse, exceeding chars will be truncated + PathspecList []string } func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepOptions) ([]*GrepResult, error) { @@ -62,6 +63,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO cmd.AddOptionValues("-e", strings.TrimLeft(search, "-")) } cmd.AddDynamicArguments(util.IfZero(opts.RefName, "HEAD")) + cmd.AddDashesAndList(opts.PathspecList...) opts.MaxResultLimit = util.IfZero(opts.MaxResultLimit, 50) stderr := bytes.Buffer{} err = cmd.Run(&RunOpts{ diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index 7f4ded478f..6a99f80407 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -31,6 +31,26 @@ func TestGrepSearch(t *testing.T) { }, }, res) + res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob)java-hello/*"}}) + assert.NoError(t, err) + assert.Equal(t, []*GrepResult{ + { + Filename: "java-hello/main.java", + LineNumbers: []int{3}, + LineCodes: []string{" public static void main(String[] args)"}, + }, + }, res) + + res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{PathspecList: []string{":(glob,exclude)java-hello/*"}}) + assert.NoError(t, err) + assert.Equal(t, []*GrepResult{ + { + Filename: "main.vendor.java", + LineNumbers: []int{3}, + LineCodes: []string{" public static void main(String[] args)"}, + }, + }, res) + res, err = GrepSearch(context.Background(), repo, "void", GrepOptions{MaxResultLimit: 1}) assert.NoError(t, err) assert.Equal(t, []*GrepResult{ diff --git a/modules/setting/glob.go b/modules/setting/glob.go new file mode 100644 index 0000000000..8f1d24dea4 --- /dev/null +++ b/modules/setting/glob.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import "github.com/gobwas/glob" + +type GlobMatcher struct { + compiledGlob glob.Glob + patternString string +} + +var _ glob.Glob = (*GlobMatcher)(nil) + +func (g *GlobMatcher) Match(s string) bool { + return g.compiledGlob.Match(s) +} + +func (g *GlobMatcher) PatternString() string { + return g.patternString +} + +func GlobMatcherCompile(pattern string, separators ...rune) (*GlobMatcher, error) { + g, err := glob.Compile(pattern, separators...) + if err != nil { + return nil, err + } + return &GlobMatcher{ + compiledGlob: g, + patternString: pattern, + }, nil +} diff --git a/modules/setting/indexer.go b/modules/setting/indexer.go index 6877d70e3c..18585602c3 100644 --- a/modules/setting/indexer.go +++ b/modules/setting/indexer.go @@ -10,8 +10,6 @@ import ( "time" "code.gitea.io/gitea/modules/log" - - "github.com/gobwas/glob" ) // Indexer settings @@ -30,8 +28,8 @@ var Indexer = struct { RepoConnStr string RepoIndexerName string MaxIndexerFileSize int64 - IncludePatterns []glob.Glob - ExcludePatterns []glob.Glob + IncludePatterns []*GlobMatcher + ExcludePatterns []*GlobMatcher ExcludeVendored bool }{ IssueType: "bleve", @@ -93,12 +91,12 @@ func loadIndexerFrom(rootCfg ConfigProvider) { } // IndexerGlobFromString parses a comma separated list of patterns and returns a glob.Glob slice suited for repo indexing -func IndexerGlobFromString(globstr string) []glob.Glob { - extarr := make([]glob.Glob, 0, 10) +func IndexerGlobFromString(globstr string) []*GlobMatcher { + extarr := make([]*GlobMatcher, 0, 10) for _, expr := range strings.Split(strings.ToLower(globstr), ",") { expr = strings.TrimSpace(expr) if expr != "" { - if g, err := glob.Compile(expr, '.', '/'); err != nil { + if g, err := GlobMatcherCompile(expr, '.', '/'); err != nil { log.Info("Invalid glob expression '%s' (skipped): %v", expr, err) } else { extarr = append(extarr, g) diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index d7854b2499..920a865555 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -17,6 +17,16 @@ import ( const tplSearch base.TplName = "repo/search" +func indexSettingToGitGrepPathspecList() (list []string) { + for _, expr := range setting.Indexer.IncludePatterns { + list = append(list, ":(glob)"+expr.PatternString()) + } + for _, expr := range setting.Indexer.ExcludePatterns { + list = append(list, ":(glob,exclude)"+expr.PatternString()) + } + return list +} + // Search render repository search page func Search(ctx *context.Context) { language := ctx.FormTrim("l") @@ -65,8 +75,14 @@ func Search(ctx *context.Context) { ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) } } else { - res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{ContextLineNumber: 3, IsFuzzy: isFuzzy}) + res, err := git.GrepSearch(ctx, ctx.Repo.GitRepo, keyword, git.GrepOptions{ + ContextLineNumber: 1, + IsFuzzy: isFuzzy, + RefName: git.RefNameFromBranch(ctx.Repo.BranchName).String(), // BranchName should be default branch or the first existing branch + PathspecList: indexSettingToGitGrepPathspecList(), + }) if err != nil { + // TODO: if no branch exists, it reports: exit status 128, fatal: this operation must be run in a work tree. ctx.ServerError("GrepSearch", err) return } diff --git a/routers/web/repo/search_test.go b/routers/web/repo/search_test.go new file mode 100644 index 0000000000..33a1610384 --- /dev/null +++ b/routers/web/repo/search_test.go @@ -0,0 +1,19 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "testing" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + + "github.com/stretchr/testify/assert" +) + +func TestIndexSettingToGitGrepPathspecList(t *testing.T) { + defer test.MockVariableValue(&setting.Indexer.IncludePatterns, setting.IndexerGlobFromString("a"))() + defer test.MockVariableValue(&setting.Indexer.ExcludePatterns, setting.IndexerGlobFromString("b"))() + assert.Equal(t, []string{":(glob)a", ":(glob,exclude)b"}, indexSettingToGitGrepPathspecList()) +} From c7bb3aa03436314e20d43e0ae44e791dd3e64909 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sat, 4 May 2024 09:48:16 +0800 Subject: [PATCH 07/14] Fix markdown URL parsing for commit ID (#30812) --- modules/markup/html.go | 122 +++++++++++++++------------ modules/markup/html_codepreview.go | 3 +- modules/markup/html_internal_test.go | 59 +++++++++---- modules/markup/html_test.go | 7 +- 4 files changed, 116 insertions(+), 75 deletions(-) diff --git a/modules/markup/html.go b/modules/markup/html.go index 5ae0cc8755..2958dc9646 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -10,6 +10,7 @@ import ( "path" "path/filepath" "regexp" + "slices" "strings" "sync" @@ -54,7 +55,7 @@ var ( shortLinkPattern = regexp.MustCompile(`\[\[(.*?)\]\](\w*)`) // anyHashPattern splits url containing SHA into parts - anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~_%.a-zA-Z0-9/]+)?(#[-+~_%.a-zA-Z0-9]+)?`) + anyHashPattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{40,64})(/[-+~%./\w]+)?(\?[-+~%.\w&=]+)?(#[-+~%.\w]+)?`) // comparePattern matches "http://domain/org/repo/compare/COMMIT1...COMMIT2#hash" comparePattern = regexp.MustCompile(`https?://(?:\S+/){4,5}([0-9a-f]{7,64})(\.\.\.?)([0-9a-f]{7,64})?(#[-+~_%.a-zA-Z0-9]+)?`) @@ -591,7 +592,8 @@ func replaceContentList(node *html.Node, i, j int, newNodes []*html.Node) { func mentionProcessor(ctx *RenderContext, node *html.Node) { start := 0 - for node != nil { + nodeStop := node.NextSibling + for node != nodeStop { found, loc := references.FindFirstMentionBytes(util.UnsafeStringToBytes(node.Data[start:])) if !found { node = node.NextSibling @@ -962,57 +964,68 @@ func commitCrossReferencePatternProcessor(ctx *RenderContext, node *html.Node) { } } +type anyHashPatternResult struct { + PosStart int + PosEnd int + FullURL string + CommitID string + SubPath string + QueryHash string +} + +func anyHashPatternExtract(s string) (ret anyHashPatternResult, ok bool) { + m := anyHashPattern.FindStringSubmatchIndex(s) + if m == nil { + return ret, false + } + + ret.PosStart, ret.PosEnd = m[0], m[1] + ret.FullURL = s[ret.PosStart:ret.PosEnd] + if strings.HasSuffix(ret.FullURL, ".") { + // if url ends in '.', it's very likely that it is not part of the actual url but used to finish a sentence. + ret.PosEnd-- + ret.FullURL = ret.FullURL[:len(ret.FullURL)-1] + for i := 0; i < len(m); i++ { + m[i] = min(m[i], ret.PosEnd) + } + } + + ret.CommitID = s[m[2]:m[3]] + if m[5] > 0 { + ret.SubPath = s[m[4]:m[5]] + } + + lastStart, lastEnd := m[len(m)-2], m[len(m)-1] + if lastEnd > 0 { + ret.QueryHash = s[lastStart:lastEnd][1:] + } + return ret, true +} + // fullHashPatternProcessor renders SHA containing URLs func fullHashPatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - - next := node.NextSibling - for node != nil && node != next { - m := anyHashPattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return + nodeStop := node.NextSibling + for node != nodeStop { + if node.Type != html.TextNode { + node = node.NextSibling + continue } - - urlFull := node.Data[m[0]:m[1]] - text := base.ShortSha(node.Data[m[2]:m[3]]) - - // 3rd capture group matches a optional path - subpath := "" - if m[5] > 0 { - subpath = node.Data[m[4]:m[5]] + ret, ok := anyHashPatternExtract(node.Data) + if !ok { + node = node.NextSibling + continue } - - // 4th capture group matches a optional url hash - hash := "" - if m[7] > 0 { - hash = node.Data[m[6]:m[7]][1:] + text := base.ShortSha(ret.CommitID) + if ret.SubPath != "" { + text += ret.SubPath } - - start := m[0] - end := m[1] - - // If url ends in '.', it's very likely that it is not part of the - // actual url but used to finish a sentence. - if strings.HasSuffix(urlFull, ".") { - end-- - urlFull = urlFull[:len(urlFull)-1] - if hash != "" { - hash = hash[:len(hash)-1] - } else if subpath != "" { - subpath = subpath[:len(subpath)-1] - } + if ret.QueryHash != "" { + text += " (" + ret.QueryHash + ")" } - - if subpath != "" { - text += subpath - } - - if hash != "" { - text += " (" + hash + ")" - } - replaceContent(node, start, end, createCodeLink(urlFull, text, "commit")) + replaceContent(node, ret.PosStart, ret.PosEnd, createCodeLink(ret.FullURL, text, "commit")) node = node.NextSibling.NextSibling } } @@ -1021,19 +1034,16 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) { if ctx.Metas == nil { return } - - next := node.NextSibling - for node != nil && node != next { - m := comparePattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return + nodeStop := node.NextSibling + for node != nodeStop { + if node.Type != html.TextNode { + node = node.NextSibling + continue } - - // Ensure that every group (m[0]...m[7]) has a match - for i := 0; i < 8; i++ { - if m[i] == -1 { - return - } + m := comparePattern.FindStringSubmatchIndex(node.Data) + if m == nil || slices.Contains(m[:8], -1) { // ensure that every group (m[0]...m[7]) has a match + node = node.NextSibling + continue } urlFull := node.Data[m[0]:m[1]] diff --git a/modules/markup/html_codepreview.go b/modules/markup/html_codepreview.go index d9da24ea34..5ef2217e3d 100644 --- a/modules/markup/html_codepreview.go +++ b/modules/markup/html_codepreview.go @@ -60,7 +60,8 @@ func renderCodeBlock(ctx *RenderContext, node *html.Node) (urlPosStart, urlPosSt } func codePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { - for node != nil { + nodeStop := node.NextSibling + for node != nodeStop { if node.Type != html.TextNode { node = node.NextSibling continue diff --git a/modules/markup/html_internal_test.go b/modules/markup/html_internal_test.go index e313be7040..3ff0597851 100644 --- a/modules/markup/html_internal_test.go +++ b/modules/markup/html_internal_test.go @@ -399,36 +399,61 @@ func TestRegExp_sha1CurrentPattern(t *testing.T) { } func TestRegExp_anySHA1Pattern(t *testing.T) { - testCases := map[string][]string{ + testCases := map[string]anyHashPatternResult{ "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js#L2703": { - "a644101ed04d0beacea864ce805e0c4f86ba1cd1", - "/test/unit/event.js", - "#L2703", + CommitID: "a644101ed04d0beacea864ce805e0c4f86ba1cd1", + SubPath: "/test/unit/event.js", + QueryHash: "L2703", }, "https://github.com/jquery/jquery/blob/a644101ed04d0beacea864ce805e0c4f86ba1cd1/test/unit/event.js": { - "a644101ed04d0beacea864ce805e0c4f86ba1cd1", - "/test/unit/event.js", - "", + CommitID: "a644101ed04d0beacea864ce805e0c4f86ba1cd1", + SubPath: "/test/unit/event.js", }, "https://github.com/jquery/jquery/commit/0705be475092aede1eddae01319ec931fb9c65fc": { - "0705be475092aede1eddae01319ec931fb9c65fc", - "", - "", + CommitID: "0705be475092aede1eddae01319ec931fb9c65fc", }, "https://github.com/jquery/jquery/tree/0705be475092aede1eddae01319ec931fb9c65fc/src": { - "0705be475092aede1eddae01319ec931fb9c65fc", - "/src", - "", + CommitID: "0705be475092aede1eddae01319ec931fb9c65fc", + SubPath: "/src", }, "https://try.gogs.io/gogs/gogs/commit/d8a994ef243349f321568f9e36d5c3f444b99cae#diff-2": { - "d8a994ef243349f321568f9e36d5c3f444b99cae", - "", - "#diff-2", + CommitID: "d8a994ef243349f321568f9e36d5c3f444b99cae", + QueryHash: "diff-2", + }, + "non-url": {}, + "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b#L1-L2": { + CommitID: "1234567812345678123456781234567812345678123456781234567812345678", + QueryHash: "L1-L2", + }, + "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678.": { + CommitID: "1234567812345678123456781234567812345678123456781234567812345678", + }, + "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678/sub.": { + CommitID: "1234567812345678123456781234567812345678123456781234567812345678", + SubPath: "/sub", + }, + "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b.": { + CommitID: "1234567812345678123456781234567812345678123456781234567812345678", + }, + "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678?a=b&c=d": { + CommitID: "1234567812345678123456781234567812345678123456781234567812345678", + }, + "http://a/b/c/d/e/1234567812345678123456781234567812345678123456781234567812345678#hash.": { + CommitID: "1234567812345678123456781234567812345678123456781234567812345678", + QueryHash: "hash", }, } for k, v := range testCases { - assert.Equal(t, anyHashPattern.FindStringSubmatch(k)[1:], v) + ret, ok := anyHashPatternExtract(k) + if v.CommitID == "" { + assert.False(t, ok) + } else { + assert.EqualValues(t, strings.TrimSuffix(k, "."), ret.FullURL) + assert.EqualValues(t, v.CommitID, ret.CommitID) + assert.EqualValues(t, v.SubPath, ret.SubPath) + assert.EqualValues(t, v.QueryHash, ret.QueryHash) + } } } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 916e74fb62..a2ae18d777 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -124,6 +124,11 @@ func TestRender_CrossReferences(t *testing.T) { test( util.URLJoin(markup.TestAppURL, "gogitea", "some-repo-name", "issues", "12345"), `

gogitea/some-repo-name#12345

`) + + inputURL := "https://host/a/b/commit/0123456789012345678901234567890123456789/foo.txt?a=b#L2-L3" + test( + inputURL, + `

0123456789/foo.txt (L2-L3)

`) } func TestMisc_IsSameDomain(t *testing.T) { @@ -695,7 +700,7 @@ func TestIssue18471(t *testing.T) { }, strings.NewReader(data), &res) assert.NoError(t, err) - assert.Equal(t, "783b039...da951ce", res.String()) + assert.Equal(t, `783b039...da951ce`, res.String()) } func TestIsFullURL(t *testing.T) { From bb0e4ce581721afabbfcdf8d5a894ba124465577 Mon Sep 17 00:00:00 2001 From: Neal Caffery Date: Sat, 4 May 2024 11:53:18 +0800 Subject: [PATCH 08/14] Update README.md (#30856) fix typo for the Docker README --- docker/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/README.md b/docker/README.md index a6d7c9a843..b014f42367 100644 --- a/docker/README.md +++ b/docker/README.md @@ -1,7 +1,7 @@ # Gitea - Docker -Dockerfile is found in root of repository. +Dockerfile is found in the root of the repository. -Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea) +Docker image can be found on [docker hub](https://hub.docker.com/r/gitea/gitea). -Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.com/installation/install-with-docker-rootless) +Documentation on using docker image can be found on [Gitea Docs site](https://docs.gitea.com/installation/install-with-docker-rootless). From ecd1d96f494d2400f7659165ff9376354edda395 Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 5 May 2024 11:10:20 +0900 Subject: [PATCH 09/14] Add result check in TestAPIEditUser (#29674) Fix #29514 There are too many usage of `NewRequestWithValues`, so there's no need to check all of them. Just one is enough I think. --- tests/integration/api_admin_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index e8954f5b20..92da7ce041 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -195,14 +195,17 @@ func TestAPIEditUser(t *testing.T) { token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2") + fullNameToChange := "Full Name User 2" req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ // required "login_name": "user2", "source_id": "0", // to change - "full_name": "Full Name User 2", + "full_name": fullNameToChange, }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}) + assert.Equal(t, fullNameToChange, user2.FullName) empty := "" req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ @@ -216,7 +219,7 @@ func TestAPIEditUser(t *testing.T) { json.Unmarshal(resp.Body.Bytes(), &errMap) assert.EqualValues(t, "e-mail invalid [email: ]", errMap["message"].(string)) - user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}) + user2 = unittest.AssertExistsAndLoadBean(t, &user_model.User{LoginName: "user2"}) assert.False(t, user2.IsRestricted) bTrue := true req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ From 5c236bd4c024dbe4a71516b10aa812893651983a Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 5 May 2024 21:09:41 +0800 Subject: [PATCH 10/14] Fix issue/PR title edit (#30858) 1. "enter" doesn't work (I think it is the last enter support for #14843) 2. if a branch name contains something like `&`, then the branch selector doesn't update --- templates/repo/issue/view_title.tmpl | 43 ++++---- tests/integration/issue_test.go | 2 +- tests/integration/pull_create_test.go | 2 +- web_src/css/repo.css | 75 +++++++------- web_src/js/features/common-global.js | 16 ++- web_src/js/features/comp/QuickSubmit.js | 13 +-- web_src/js/features/repo-issue.js | 129 +++++++++++------------- 7 files changed, 141 insertions(+), 139 deletions(-) diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index fccf8cca91..4415ad79f5 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -4,29 +4,36 @@ {{end}}
-
+ {{$canEditIssueTitle := and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} +

- {{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} #{{.Issue.Index}} - -
- -
+ {{RenderIssueTitle $.Context .Issue.Title ($.Repository.ComposeMetas ctx) | RenderCodeBlock}} + #{{.Issue.Index}}

- {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} - + {{if $canEditIssueTitle}} + {{end}} {{if not .Issue.IsPull}} - {{ctx.Locale.Tr "repo.issues.new"}} + {{ctx.Locale.Tr "repo.issues.new"}} {{end}}
- {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} -
- - -
- {{end}}
+ {{if $canEditIssueTitle}} +
+
+ +
+
+ + +
+
+ {{end}}
{{if .HasMerged}}
{{svg "octicon-git-merge" 16 "tw-mr-1"}} {{if eq .Issue.PullRequest.Status 3}}{{ctx.Locale.Tr "repo.pulls.manually_merged"}}{{else}}{{ctx.Locale.Tr "repo.pulls.merged"}}{{end}}
@@ -63,14 +70,14 @@ {{end}} {{else}} {{if .Issue.OriginalAuthor}} - {{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} + {{.Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} {{else}} - + {{.Issue.Poster.GetDisplayName}} {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits $headHref $baseHref}} {{end}} - + {{end}} -
These configuration options will be written into: {{.CustomConfFile}}
-
+
diff --git a/web_src/css/install.css b/web_src/css/install.css index ee2395e6c5..7ab729405e 100644 --- a/web_src/css/install.css +++ b/web_src/css/install.css @@ -13,8 +13,7 @@ .page-content.install .ui.form .field > .help, .page-content.install .ui.form .field > .ui.checkbox:first-child, .page-content.install .ui.form .field > .right-content { - margin-left: 30%; - padding-left: 5px; + margin-left: calc(30% + 5px); width: auto; } @@ -24,10 +23,11 @@ } .page-content.install form.ui.form details.optional.field[open] { - border-bottom: 1px dashed var(--color-secondary); padding-bottom: 10px; } - +.page-content.install form.ui.form details.optional.field[open]:not(:last-child) { + border-bottom: 1px dashed var(--color-secondary); +} .page-content.install form.ui.form details.optional.field[open] summary { margin-bottom: 10px; } diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css index 8d73573bfa..0a3a71acaa 100644 --- a/web_src/css/modules/checkbox.css +++ b/web_src/css/modules/checkbox.css @@ -41,7 +41,7 @@ input[type="radio"] { .ui.checkbox label, .ui.radio.checkbox label { - margin-left: 1.85714em; + margin-left: 20px; } .ui.checkbox + label { diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 39c364ca50..d10d4dab8d 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -299,23 +299,23 @@ export function initRepoPullRequestMergeInstruction() { export function initRepoPullRequestAllowMaintainerEdit() { const wrapper = document.getElementById('allow-edits-from-maintainers'); if (!wrapper) return; - - wrapper.querySelector('input[type="checkbox"]')?.addEventListener('change', async (e) => { - const checked = e.target.checked; + const checkbox = wrapper.querySelector('input[type="checkbox"]'); + checkbox.addEventListener('input', async () => { const url = `${wrapper.getAttribute('data-url')}/set_allow_maintainer_edit`; wrapper.classList.add('is-loading'); - e.target.disabled = true; try { - const response = await POST(url, {data: {allow_maintainer_edit: checked}}); - if (!response.ok) { + const resp = await POST(url, {data: new URLSearchParams({allow_maintainer_edit: checkbox.checked})}); + if (!resp.ok) { throw new Error('Failed to update maintainer edit permission'); } + const data = await resp.json(); + checkbox.checked = data.allow_maintainer_edit; } catch (error) { + checkbox.checked = !checkbox.checked; console.error(error); showTemporaryTooltip(wrapper, wrapper.getAttribute('data-prompt-error')); } finally { wrapper.classList.remove('is-loading'); - e.target.disabled = false; } }); } From eda10cc2bb229a6b13ace76caea118384b381429 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 6 May 2024 15:17:22 +0800 Subject: [PATCH 14/14] Fix some UI problems (dropdown/container) (#30849) Follow #30345 Follow #30547 `ellipsis` / `white-space` shouldn't be put on the general dropdown components. --- templates/devtest/fomantic-dropdown.tmpl | 109 +++++++++++ templates/devtest/gitea-ui.tmpl | 88 --------- templates/repo/branch_dropdown.tmpl | 2 +- templates/repo/header.tmpl | 184 +++++++++--------- .../repo/issue/branch_selector_field.tmpl | 2 +- .../view_content/reference_issue_dialog.tmpl | 2 +- templates/repo/settings/options.tmpl | 2 +- web_src/css/base.css | 22 ++- web_src/css/form.css | 4 + web_src/css/modules/container.css | 22 +-- web_src/css/repo.css | 6 + .../js/components/RepoBranchTagSelector.vue | 4 +- web_src/js/features/repo-issue.js | 4 +- 13 files changed, 246 insertions(+), 205 deletions(-) create mode 100644 templates/devtest/fomantic-dropdown.tmpl diff --git a/templates/devtest/fomantic-dropdown.tmpl b/templates/devtest/fomantic-dropdown.tmpl new file mode 100644 index 0000000000..57a7c1313e --- /dev/null +++ b/templates/devtest/fomantic-dropdown.tmpl @@ -0,0 +1,109 @@ +{{template "base/head" .}} + +
+
+

Dropdown

+
+ + + + +
+ + +
+
+ +

Selection

+
+ {{/* the "selection" class is optional, it will be added by JS automatically */}} + + +
+

Dropdown Button (demo only without menu)

+
+ + + +
+ +
+ + + +
+ +
+
+
Other button align with ...
+ +
+
+
+{{template "base/footer" .}} diff --git a/templates/devtest/gitea-ui.tmpl b/templates/devtest/gitea-ui.tmpl index 3b13c13be8..ea293fd3b4 100644 --- a/templates/devtest/gitea-ui.tmpl +++ b/templates/devtest/gitea-ui.tmpl @@ -180,94 +180,6 @@
- -

Dropdown with SVG

-
- - - - -
- - -
-
- -
- - - -
- -
- - - -
- -
-
-
Button align with ...
- -
diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl index 8f58826c6a..c4f73875f2 100644 --- a/templates/repo/branch_dropdown.tmpl +++ b/templates/repo/branch_dropdown.tmpl @@ -69,7 +69,7 @@
{{/* show dummy elements before Vue componment is mounted, this code must match the code in BranchTagSelector.vue */}} -