Followup to pinned Issues (#24945)

This addressees some things from #24406 that came up after the PR was
merged. Mostly from @delvh.

---------

Co-authored-by: silverwind <me@silverwind.io>
Co-authored-by: delvh <dev.lh@web.de>
This commit is contained in:
JakobDev 2023-05-30 17:26:51 +02:00 committed by GitHub
parent faae819f5d
commit 1b115296d3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 39 additions and 22 deletions

View file

@ -1044,7 +1044,7 @@ LEVEL = Info
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; List of reasons why a Pull Request or Issue can be locked ;; List of reasons why a Pull Request or Issue can be locked
;LOCK_REASONS = Too heated,Off-topic,Resolved,Spam ;LOCK_REASONS = Too heated,Off-topic,Resolved,Spam
;; Maximum number of pinned Issues ;; Maximum number of pinned Issues per repo
;; Set to 0 to disable pinning Issues ;; Set to 0 to disable pinning Issues
;MAX_PINNED = 3 ;MAX_PINNED = 3

View file

@ -141,7 +141,7 @@ In addition there is _`StaticRootPath`_ which can be set as a built-in at build
### Repository - Issue (`repository.issue`) ### Repository - Issue (`repository.issue`)
- `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked - `LOCK_REASONS`: **Too heated,Off-topic,Resolved,Spam**: A list of reasons why a Pull Request or Issue can be locked
- `MAX_PINNED`: **3**: Maximum number of pinned Issues. Set to 0 to disable pinning Issues. - `MAX_PINNED`: **3**: Maximum number of pinned Issues per Repo. Set to 0 to disable pinning Issues.
### Repository - Upload (`repository.upload`) ### Repository - Upload (`repository.upload`)

View file

@ -687,6 +687,8 @@ func (issue *Issue) HasOriginalAuthor() bool {
return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0 return issue.OriginalAuthor != "" && issue.OriginalAuthorID != 0
} }
var ErrIssueMaxPinReached = util.NewInvalidArgumentErrorf("the max number of pinned issues has been readched")
// IsPinned returns if a Issue is pinned // IsPinned returns if a Issue is pinned
func (issue *Issue) IsPinned() bool { func (issue *Issue) IsPinned() bool {
return issue.PinOrder != 0 return issue.PinOrder != 0
@ -707,7 +709,7 @@ func (issue *Issue) Pin(ctx context.Context, user *user_model.User) error {
// Check if the maximum allowed Pins reached // Check if the maximum allowed Pins reached
if maxPin >= setting.Repository.Issue.MaxPinned { if maxPin >= setting.Repository.Issue.MaxPinned {
return fmt.Errorf("You have reached the max number of pinned Issues") return ErrIssueMaxPinReached
} }
_, err = db.GetEngine(ctx).Table("issue"). _, err = db.GetEngine(ctx).Table("issue").
@ -856,10 +858,15 @@ func GetPinnedIssues(ctx context.Context, repoID int64, isPull bool) ([]*Issue,
// IsNewPinnedAllowed returns if a new Issue or Pull request can be pinned // IsNewPinnedAllowed returns if a new Issue or Pull request can be pinned
func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) { func IsNewPinAllowed(ctx context.Context, repoID int64, isPull bool) (bool, error) {
var maxPin int var maxPin int
_, err := db.GetEngine(ctx).SQL("SELECT MAX(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ?", repoID, isPull).Get(&maxPin) _, err := db.GetEngine(ctx).SQL("SELECT COUNT(pin_order) FROM issue WHERE repo_id = ? AND is_pull = ? AND pin_order > 0", repoID, isPull).Get(&maxPin)
if err != nil { if err != nil {
return false, err return false, err
} }
return maxPin < setting.Repository.Issue.MaxPinned, nil return maxPin < setting.Repository.Issue.MaxPinned, nil
} }
// IsErrIssueMaxPinReached returns if the error is, that the User can't pin more Issues
func IsErrIssueMaxPinReached(err error) bool {
return err == ErrIssueMaxPinReached
}

View file

@ -45,6 +45,8 @@ func PinIssue(ctx *context.APIContext) {
if err != nil { if err != nil {
if issues_model.IsErrIssueNotExist(err) { if issues_model.IsErrIssueNotExist(err) {
ctx.NotFound() ctx.NotFound()
} else if issues_model.IsErrIssueMaxPinReached(err) {
ctx.Error(http.StatusBadRequest, "MaxPinReached", err)
} else { } else {
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err) ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
} }
@ -55,11 +57,13 @@ func PinIssue(ctx *context.APIContext) {
err = issue.LoadRepo(ctx) err = issue.LoadRepo(ctx)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadRepo", err) ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
return
} }
err = issue.Pin(ctx, ctx.Doer) err = issue.Pin(ctx, ctx.Doer)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "PinIssue", err) ctx.Error(http.StatusInternalServerError, "PinIssue", err)
return
} }
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusNoContent)
@ -108,11 +112,13 @@ func UnpinIssue(ctx *context.APIContext) {
err = issue.LoadRepo(ctx) err = issue.LoadRepo(ctx)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadRepo", err) ctx.Error(http.StatusInternalServerError, "LoadRepo", err)
return
} }
err = issue.Unpin(ctx, ctx.Doer) err = issue.Unpin(ctx, ctx.Doer)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "UnpinIssue", err) ctx.Error(http.StatusInternalServerError, "UnpinIssue", err)
return
} }
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusNoContent)
@ -166,6 +172,7 @@ func MoveIssuePin(ctx *context.APIContext) {
err = issue.MovePin(ctx, int(ctx.ParamsInt64(":position"))) err = issue.MovePin(ctx, int(ctx.ParamsInt64(":position")))
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "MovePin", err) ctx.Error(http.StatusInternalServerError, "MovePin", err)
return
} }
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusNoContent)
@ -193,12 +200,12 @@ func ListPinnedIssues(ctx *context.APIContext) {
// "200": // "200":
// "$ref": "#/responses/IssueList" // "$ref": "#/responses/IssueList"
issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, false) issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, false)
if err != nil {
if err == nil {
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
} else {
ctx.Error(http.StatusInternalServerError, "LoadPinnedIssues", err) ctx.Error(http.StatusInternalServerError, "LoadPinnedIssues", err)
return
} }
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
} }
// ListPinnedPullRequests returns a list of all pinned PRs // ListPinnedPullRequests returns a list of all pinned PRs
@ -225,6 +232,7 @@ func ListPinnedPullRequests(ctx *context.APIContext) {
issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, true) issues, err := issues_model.GetPinnedIssues(ctx, ctx.Repo.Repository.ID, true)
if err != nil { if err != nil {
ctx.Error(http.StatusInternalServerError, "LoadPinnedPullRequests", err) ctx.Error(http.StatusInternalServerError, "LoadPinnedPullRequests", err)
return
} }
apiPrs := make([]*api.PullRequest, len(issues)) apiPrs := make([]*api.PullRequest, len(issues))

View file

@ -9,6 +9,7 @@ import (
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
"code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/context"
"code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/json"
"code.gitea.io/gitea/modules/log"
) )
// IssuePinOrUnpin pin or unpin a Issue // IssuePinOrUnpin pin or unpin a Issue
@ -19,12 +20,14 @@ func IssuePinOrUnpin(ctx *context.Context) {
err := issue.LoadRepo(ctx) err := issue.LoadRepo(ctx)
if err != nil { if err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }
err = issue.PinOrUnpin(ctx, ctx.Doer) err = issue.PinOrUnpin(ctx, ctx.Doer)
if err != nil { if err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }
@ -33,9 +36,10 @@ func IssuePinOrUnpin(ctx *context.Context) {
// IssueUnpin unpins a Issue // IssueUnpin unpins a Issue
func IssueUnpin(ctx *context.Context) { func IssueUnpin(ctx *context.Context) {
issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":id")) issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil { if err != nil {
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }
@ -43,12 +47,15 @@ func IssueUnpin(ctx *context.Context) {
err = issue.LoadRepo(ctx) err = issue.LoadRepo(ctx)
if err != nil { if err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }
err = issue.Unpin(ctx, ctx.Doer) err = issue.Unpin(ctx, ctx.Doer)
if err != nil { if err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return
} }
ctx.Status(http.StatusNoContent) ctx.Status(http.StatusNoContent)
@ -69,18 +76,21 @@ func IssuePinMove(ctx *context.Context) {
form := &movePinIssueForm{} form := &movePinIssueForm{}
if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil { if err := json.NewDecoder(ctx.Req.Body).Decode(&form); err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }
issue, err := issues_model.GetIssueByID(ctx, form.ID) issue, err := issues_model.GetIssueByID(ctx, form.ID)
if err != nil { if err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }
err = issue.MovePin(ctx, form.Position) err = issue.MovePin(ctx, form.Position)
if err != nil { if err != nil {
ctx.Status(http.StatusInternalServerError) ctx.Status(http.StatusInternalServerError)
log.Error(err.Error())
return return
} }

View file

@ -1025,8 +1025,8 @@ func registerRoutes(m *web.Route) {
m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation) m.Post("/resolve_conversation", reqRepoIssuesOrPullsReader, repo.UpdateResolveConversation)
m.Post("/attachments", repo.UploadIssueAttachment) m.Post("/attachments", repo.UploadIssueAttachment)
m.Post("/attachments/remove", repo.DeleteAttachment) m.Post("/attachments/remove", repo.DeleteAttachment)
m.Delete("/unpin/{id}", reqRepoAdmin, repo.IssueUnpin) m.Delete("/unpin/{index}", reqRepoAdmin, repo.IssueUnpin)
m.Post("/pin_move", reqRepoAdmin, repo.IssuePinMove) m.Post("/move_pin", reqRepoAdmin, repo.IssuePinMove)
}, context.RepoMustNotBeArchived()) }, context.RepoMustNotBeArchived())
m.Group("/comments/{id}", func() { m.Group("/comments/{id}", func() {
m.Post("", repo.UpdateCommentContent) m.Post("", repo.UpdateCommentContent)

View file

@ -6,7 +6,7 @@
{{if .PinnedIssues}} {{if .PinnedIssues}}
<div id="issue-pins" {{if .IsRepoAdmin}}data-is-repo-admin{{end}}> <div id="issue-pins" {{if .IsRepoAdmin}}data-is-repo-admin{{end}}>
{{range .PinnedIssues}} {{range .PinnedIssues}}
<div class="pinned-issue-card gt-word-break" data-move-url="{{$.Link}}/pin_move" data-issue-id="{{.ID}}"> <div class="pinned-issue-card gt-word-break" data-move-url="{{$.Link}}/move_pin" data-issue-id="{{.ID}}">
{{if eq $.Project.CardType 1}} {{if eq $.Project.CardType 1}}
<div class="card-attachment-images"> <div class="card-attachment-images">
{{range (index $.issuesAttachmentMap .ID)}} {{range (index $.issuesAttachmentMap .ID)}}
@ -21,7 +21,7 @@
</div> </div>
<a class="pinned-issue-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a> <a class="pinned-issue-title muted issue-title" href="{{.Link}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
{{if $.IsRepoAdmin}} {{if $.IsRepoAdmin}}
<a role="button" class="pinned-issue-unpin muted gt-df gt-ac" data-tooltip-content={{$.locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Link}}/unpin/{{.ID}}"> <a role="button" class="pinned-issue-unpin muted gt-df gt-ac" data-tooltip-content={{$.locale.Tr "repo.issues.unpin_issue"}} data-issue-id="{{.ID}}" data-unpin-url="{{$.Link}}/unpin/{{.Index}}">
{{svg "octicon-x" 16}} {{svg "octicon-x" 16}}
</a> </a>
{{end}} {{end}}

View file

@ -3419,14 +3419,6 @@ tbody.commit-list {
background: var(--color-card); background: var(--color-card);
} }
.pinned-issue-card .meta a {
color: inherit;
}
.pinned-issue-card .meta a:hover {
color: var(--color-primary);
}
.pinned-issue-icon, .pinned-issue-icon,
.pinned-issue-unpin { .pinned-issue-unpin {
margin-top: 1px; margin-top: 1px;