Compare commits

...

8 Commits

Author SHA1 Message Date
a1012112796 c4f850c50b
Merge 9207a4e7bf into 8b8b48ef5f 2024-04-28 09:15:34 +08:00
Chongyi Zheng 8b8b48ef5f
Use `ProtonMail/go-crypto` for `opengpg` in tests (#30736) 2024-04-27 19:21:33 -04:00
Chongyi Zheng 7b8e418da1
Replace deprecated `math/rand` functions (#30733)
Suggested by logs in #30729

- Remove `math/rand.Seed`
`rand.Seed is deprecated: As of Go 1.20 there is no reason to call Seed
with a random value.`
- Replace `math/rand.Read`
`rand.Read is deprecated: For almost all use cases, [crypto/rand.Read]
is more appropriate.`
- Replace `math/rand` with `math/rand/v2`, which is available since Go
1.22
2024-04-27 18:50:35 +02:00
a1012112796 9207a4e7bf
remove not used code
Signed-off-by: a1012112796 <1012112796@qq.com>
2024-04-23 10:11:38 +00:00
a1012112796 092c46d18e
Merge remote-tracking branch 'origin/main' into zzc/dev/sidebar_board_option 2024-04-23 09:44:07 +00:00
a1012112796 64701bdc42
fix lint
Signed-off-by: a1012112796 <1012112796@qq.com>
2024-04-22 03:36:11 +00:00
a1012112796 dd52d2e157
Merge remote-tracking branch 'origin/main' into zzc/dev/sidebar_board_option 2024-04-22 03:21:06 +00:00
a1012112796 6524bd51ca
add project board choice option on issue sidebar
Signed-off-by: a1012112796 <1012112796@qq.com>
2024-04-20 11:33:54 +00:00
17 changed files with 222 additions and 25 deletions

2
go.mod
View File

@ -16,6 +16,7 @@ require (
gitea.com/lunny/levelqueue v0.4.2-0.20230414023320-3c0159fe0fe4
github.com/42wim/sshsig v0.0.0-20211121163825-841cf5bbc121
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358
github.com/ProtonMail/go-crypto v1.0.0
github.com/PuerkitoBio/goquery v1.9.1
github.com/alecthomas/chroma/v2 v2.13.0
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb
@ -135,7 +136,6 @@ require (
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/sprig/v3 v3.2.3 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/RoaringBitmap/roaring v1.9.0 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/andybalholm/cascadia v1.3.2 // indirect

View File

@ -140,6 +140,8 @@ type Issue struct {
// For view issue page.
ShowRole RoleDescriptor `xorm:"-"`
ProjectIssue *project_model.ProjectIssue `xorm:"-"`
}
var (
@ -315,6 +317,10 @@ func (issue *Issue) LoadAttributes(ctx context.Context) (err error) {
return err
}
if err = issue.LoadProjectIssue(ctx); err != nil {
return err
}
if err = issue.LoadAssignees(ctx); err != nil {
return err
}

View File

@ -28,6 +28,23 @@ func (issue *Issue) LoadProject(ctx context.Context) (err error) {
return err
}
func (issue *Issue) LoadProjectIssue(ctx context.Context) (err error) {
if issue.Project == nil {
return nil
}
if issue.ProjectIssue != nil {
return nil
}
issue.ProjectIssue, err = project_model.GetProjectIssueByIssueID(ctx, issue.ID)
if err != nil {
return err
}
return issue.ProjectIssue.LoadProjectBoard(ctx)
}
func (issue *Issue) projectID(ctx context.Context) int64 {
var ip project_model.ProjectIssue
has, err := db.GetEngine(ctx).Where("issue_id=?", issue.ID).Get(&ip)

View File

@ -69,7 +69,7 @@ func (Board) TableName() string {
}
// NumIssues return counter of all issues assigned to the board
func (b *Board) NumIssues(ctx context.Context) int {
func (b *Board) NumIssues(ctx context.Context) (int64, error) {
c, err := db.GetEngine(ctx).Table("project_issue").
Where("project_id=?", b.ProjectID).
And("project_board_id=?", b.ID).
@ -77,9 +77,9 @@ func (b *Board) NumIssues(ctx context.Context) int {
Cols("issue_id").
Count()
if err != nil {
return 0
return 0, err
}
return int(c)
return c, nil
}
func init() {

View File

@ -18,7 +18,8 @@ type ProjectIssue struct { //revive:disable-line:exported
ProjectID int64 `xorm:"INDEX"`
// If 0, then it has not been added to a specific board in the project
ProjectBoardID int64 `xorm:"INDEX"`
ProjectBoardID int64 `xorm:"INDEX"`
ProjectBoard *Board `xorm:"-"`
// the sorting order on the board
Sorting int64 `xorm:"NOT NULL DEFAULT 0"`
@ -33,6 +34,45 @@ func deleteProjectIssuesByProjectID(ctx context.Context, projectID int64) error
return err
}
type ErrProjectIssueNotExist struct {
IssueID int64
}
func (e ErrProjectIssueNotExist) Error() string {
return fmt.Sprintf("can't find project issue [issue_id: %d]", e.IssueID)
}
func IsErrProjectIssueNotExist(e error) bool {
_, ok := e.(ErrProjectIssueNotExist)
return ok
}
func GetProjectIssueByIssueID(ctx context.Context, issueID int64) (*ProjectIssue, error) {
issue := &ProjectIssue{}
has, err := db.GetEngine(ctx).Where("issue_id = ?", issueID).Get(issue)
if err != nil {
return nil, err
}
if !has {
return nil, ErrProjectIssueNotExist{IssueID: issueID}
}
return issue, nil
}
func (issue *ProjectIssue) LoadProjectBoard(ctx context.Context) error {
if issue.ProjectBoard != nil {
return nil
}
var err error
issue.ProjectBoard, err = GetBoard(ctx, issue.ProjectBoardID)
return err
}
// NumIssues return counter of all issues assigned to a project
func (p *Project) NumIssues(ctx context.Context) int {
c, err := db.GetEngine(ctx).Table("project_issue").
@ -102,6 +142,27 @@ func MoveIssuesOnProjectBoard(ctx context.Context, board *Board, sortedIssueIDs
})
}
func MoveIssueToBoardTail(ctx context.Context, issue *ProjectIssue, toBoard *Board) error {
ctx, committer, err := db.TxContext(ctx)
if err != nil {
return err
}
defer committer.Close()
num, err := toBoard.NumIssues(ctx)
if err != nil {
return err
}
_, err = db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id=?, sorting=? WHERE issue_id=?",
toBoard.ID, num, issue.IssueID)
if err != nil {
return err
}
return committer.Commit()
}
func (b *Board) removeIssues(ctx context.Context) error {
_, err := db.GetEngine(ctx).Exec("UPDATE `project_issue` SET project_board_id = 0 WHERE project_board_id = ? ", b.ID)
return err

View File

@ -5,8 +5,8 @@ package user_test
import (
"context"
"crypto/rand"
"fmt"
"math/rand"
"strings"
"testing"
"time"

View File

@ -4,9 +4,8 @@
package pwn
import (
"math/rand"
"math/rand/v2"
"net/http"
"os"
"strings"
"testing"
"time"
@ -18,11 +17,6 @@ var client = New(WithHTTP(&http.Client{
Timeout: time.Second * 2,
}))
func TestMain(m *testing.M) {
rand.Seed(time.Now().Unix())
os.Exit(m.Run())
}
func TestPassword(t *testing.T) {
// Check input error
_, err := client.CheckPassword("", false)
@ -81,24 +75,24 @@ func testPassword() string {
// Set special character
for i := 0; i < 5; i++ {
random := rand.Intn(len(specialCharSet))
random := rand.IntN(len(specialCharSet))
password.WriteString(string(specialCharSet[random]))
}
// Set numeric
for i := 0; i < 5; i++ {
random := rand.Intn(len(numberSet))
random := rand.IntN(len(numberSet))
password.WriteString(string(numberSet[random]))
}
// Set uppercase
for i := 0; i < 5; i++ {
random := rand.Intn(len(upperCharSet))
random := rand.IntN(len(upperCharSet))
password.WriteString(string(upperCharSet[random]))
}
for i := 0; i < 5; i++ {
random := rand.Intn(len(allCharSet))
random := rand.IntN(len(allCharSet))
password.WriteString(string(allCharSet[random]))
}
inRune := []rune(password.String())

View File

@ -1751,6 +1751,7 @@ issues.content_history.delete_from_history = Delete from history
issues.content_history.delete_from_history_confirm = Delete from history?
issues.content_history.options = Options
issues.reference_link = Reference: %s
issues.move_project_boad = Status
compare.compare_base = base
compare.compare_head = compare

View File

@ -2042,6 +2042,17 @@ func ViewIssue(ctx *context.Context) {
return user_service.CanBlockUser(ctx, ctx.Doer, blocker, blockee)
}
canWriteProjects := ctx.Repo.Permission.CanWrite(unit.TypeProjects)
ctx.Data["CanWriteProjects"] = canWriteProjects
if canWriteProjects && issue.Project != nil {
ctx.Data["ProjectBoards"], err = issue.Project.GetBoards(ctx)
if err != nil {
ctx.ServerError("Project.GetBoards", err)
return
}
}
ctx.HTML(http.StatusOK, tplIssueView)
}

View File

@ -574,6 +574,72 @@ func SetDefaultProjectBoard(ctx *context.Context) {
ctx.JSONOK()
}
// MoveBoardForIssue move a issue to other board
func MoveBoardForIssue(ctx *context.Context) {
if ctx.Doer == nil {
ctx.JSON(http.StatusForbidden, map[string]string{
"message": "Only signed in users are allowed to perform this action.",
})
return
}
if !ctx.Repo.IsOwner() && !ctx.Repo.IsAdmin() && !ctx.Repo.CanAccess(perm.AccessModeWrite, unit.TypeProjects) {
ctx.JSON(http.StatusForbidden, map[string]string{
"message": "Only authorized users are allowed to perform this action.",
})
return
}
issue, err := issues_model.GetIssueByIndex(ctx, ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
if err != nil {
if issues_model.IsErrIssueNotExist(err) {
ctx.NotFound("GetIssueByIndex", err)
} else {
ctx.ServerError("GetIssueByIndex", err)
}
return
}
if err := issue.LoadProject(ctx); err != nil {
ctx.ServerError("LoadProject", err)
return
}
if issue.Project == nil {
ctx.NotFound("Project not found", nil)
return
}
if err = issue.LoadProjectIssue(ctx); err != nil {
ctx.ServerError("LoadProjectIssue", err)
return
}
board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
if err != nil {
if project_model.IsErrProjectBoardNotExist(err) {
ctx.NotFound("ProjectBoardNotExist", nil)
} else {
ctx.ServerError("GetProjectBoard", err)
}
return
}
if board.ProjectID != issue.Project.ID {
ctx.NotFound("BoardNotInProject", nil)
return
}
err = project_model.MoveIssueToBoardTail(ctx, issue.ProjectIssue, board)
if err != nil {
ctx.NotFound("MoveIssueToBoardTail", nil)
return
}
issue.Repo = ctx.Repo.Repository
ctx.JSONRedirect(issue.HTMLURL())
}
// MoveIssues moves or keeps issues in a column and sorts them inside that column
func MoveIssues(ctx *context.Context) {
if ctx.Doer == nil {

View File

@ -1206,6 +1206,7 @@ func registerRoutes(m *web.Route) {
m.Post("/lock", reqRepoIssuesOrPullsWriter, web.Bind(forms.IssueLockForm{}), repo.LockIssue)
m.Post("/unlock", reqRepoIssuesOrPullsWriter, repo.UnlockIssue)
m.Post("/delete", reqRepoAdmin, repo.DeleteIssue)
m.Post("/move_project_board/{boardID}", repo.MoveBoardForIssue)
}, context.RepoMustNotBeArchived())
m.Group("/{index}", func() {

View File

@ -193,13 +193,25 @@
{{end}}
</div>
</div>
<div class="ui select-project list">
<div class="ui select-project-current list">
<span class="no-select item {{if .Issue.Project}}tw-hidden{{end}}">{{ctx.Locale.Tr "repo.issues.new.no_projects"}}</span>
<div class="selected">
{{if .Issue.Project}}
<a class="item muted sidebar-item-link" href="{{.Issue.Project.Link ctx}}">
<a class="item muted sidebar-item-link tw-block" href="{{.Issue.Project.Link ctx}}">
{{svg .Issue.Project.IconName 18 "tw-mr-2"}}{{.Issue.Project.Title}}
</a>
<div class="ui dropdown jump {{if not .CanWriteProjects}}disabled{{end}} select-issue-project-board item tw-mx-0 tw-pr-2" data-url="{{$.Issue.Link}}/move_project_board/">
<span class="text">
{{ctx.Locale.Tr "repo.issues.move_project_boad"}}: {{.Issue.ProjectIssue.ProjectBoard.Title}}
</span>
<div class="menu">
{{if .ProjectBoards}}
{{range .ProjectBoards}}
<div class="item no-select" data-project-id="{{.ProjectID}}" data-board-id="{{.ID}}">{{.Title}}</div>
{{end}}
{{end}}
</div>
</div>
{{end}}
</div>
</div>

View File

@ -4,7 +4,7 @@
package integration
import (
"math/rand"
"math/rand/v2"
"net/http"
"net/url"
"testing"
@ -18,7 +18,7 @@ import (
func StringWithCharset(length int, charset string) string {
b := make([]byte, length)
for i := range b {
b[i] = charset[rand.Intn(len(charset))]
b[i] = charset[rand.IntN(len(charset))]
}
return string(b)
}
@ -37,7 +37,7 @@ func BenchmarkRepoBranchCommit(b *testing.B) {
b.ResetTimer()
b.Run("CreateBranch", func(b *testing.B) {
b.StopTimer()
branchName := StringWithCharset(5+rand.Intn(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
branchName := StringWithCharset(5+rand.IntN(10), "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
b.StartTimer()
for i := 0; i < b.N; i++ {
b.Run("new_"+branchName, func(b *testing.B) {

View File

@ -5,9 +5,9 @@ package integration
import (
"bytes"
"crypto/rand"
"encoding/hex"
"fmt"
"math/rand"
"net/http"
"net/url"
"os"

View File

@ -19,9 +19,9 @@ import (
"code.gitea.io/gitea/modules/test"
"code.gitea.io/gitea/tests"
"github.com/ProtonMail/go-crypto/openpgp"
"github.com/ProtonMail/go-crypto/openpgp/armor"
"github.com/stretchr/testify/assert"
"golang.org/x/crypto/openpgp"
"golang.org/x/crypto/openpgp/armor"
)
func TestGPGGit(t *testing.T) {

View File

@ -746,3 +746,29 @@ export function initArchivedLabelHandler() {
toggleElem(label, label.classList.contains('checked'));
}
}
export function initIssueProjectBoardSelector() {
const root = document.querySelector('.select-issue-project-board');
if (!root) return;
const link = root.getAttribute('data-url');
for (const board of document.querySelectorAll('.select-issue-project-board .item')) {
board.addEventListener('click', async (e) => {
e.preventDefault();
e.stopImmediatePropagation();
try {
const response = await POST(`${link}${board.getAttribute('data-board-id')}`);
if (response.ok) {
const data = await response.json();
window.location.href = data.redirect;
}
} catch (error) {
console.error(error);
}
return false;
});
}
}

View File

@ -4,6 +4,7 @@ import {
initRepoIssueComments, initRepoIssueDependencyDelete, initRepoIssueReferenceIssue,
initRepoIssueTitleEdit, initRepoIssueWipToggle,
initRepoPullRequestUpdate, updateIssuesMeta, initIssueTemplateCommentEditors, initSingleCommentEditor,
initIssueProjectBoardSelector,
} from './repo-issue.js';
import {initUnicodeEscapeButton} from './repo-unicode-escape.js';
import {svg} from '../svg.js';
@ -394,6 +395,7 @@ export function initRepository() {
initRepoIssueCodeCommentCancel();
initRepoPullRequestUpdate();
initCompReactionSelector();
initIssueProjectBoardSelector();
initRepoPullRequestMergeForm();
initRepoPullRequestCommitStatus();