mirror of https://github.com/go-gitea/gitea.git
Compare commits
18 Commits
f5e6c32e66
...
a4ed48b311
Author | SHA1 | Date |
---|---|---|
silverwind | a4ed48b311 | |
wxiaoguang | 8de2992ffb | |
wxiaoguang | 6d2a307ad8 | |
silverwind | b93c87b6fe | |
Yarden Shoham | 51c28d9683 | |
wxiaoguang | d3cdef88ad | |
Kemal Zebari | dd301cae1c | |
silverwind | 238eb3ff9f | |
silverwind | b2abac5e5f | |
Chongyi Zheng | 4ae6b1a553 | |
silverwind | 9b2536b78f | |
silverwind | dcc3c17e5c | |
silverwind | 41b1a390c0 | |
silverwind | 72c09e46ad | |
silverwind | 59a75d2f8d | |
silverwind | 623da66687 | |
silverwind | ea07720a9b | |
silverwind | 0ea852c32b |
11
Makefile
11
Makefile
|
@ -30,7 +30,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che
|
|||
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0
|
||||
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2
|
||||
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1
|
||||
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.5.1
|
||||
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@db51e79a0e37c572d8b59ae0c58bf2bbbbe53285
|
||||
XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest
|
||||
GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1
|
||||
|
@ -397,11 +397,11 @@ lint-md: node_modules
|
|||
|
||||
.PHONY: lint-spell
|
||||
lint-spell:
|
||||
@go run $(MISSPELL_PACKAGE) -error $(SPELLCHECK_FILES)
|
||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -error $(SPELLCHECK_FILES)
|
||||
|
||||
.PHONY: lint-spell-fix
|
||||
lint-spell-fix:
|
||||
@go run $(MISSPELL_PACKAGE) -w $(SPELLCHECK_FILES)
|
||||
@go run $(MISSPELL_PACKAGE) -dict tools/misspellings.csv -w $(SPELLCHECK_FILES)
|
||||
|
||||
.PHONY: lint-go
|
||||
lint-go:
|
||||
|
@ -908,8 +908,9 @@ webpack: $(WEBPACK_DEST)
|
|||
|
||||
$(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
||||
@$(MAKE) -s node-check node_modules
|
||||
rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
npx webpack
|
||||
@rm -rf $(WEBPACK_DEST_ENTRIES)
|
||||
@echo "Running webpack..."
|
||||
@BROWSERSLIST_IGNORE_OLD_DATA=true npx webpack
|
||||
@touch $(WEBPACK_DEST)
|
||||
|
||||
.PHONY: svg
|
||||
|
|
|
@ -35,7 +35,7 @@ var microcmdUserChangePassword = &cli.Command{
|
|||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "must-change-password",
|
||||
Usage: "User must change password",
|
||||
Usage: "User must change password (can be disabled by --must-change-password=false)",
|
||||
Value: true,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
|
@ -48,7 +49,7 @@ var microcmdUserCreate = &cli.Command{
|
|||
},
|
||||
&cli.BoolFlag{
|
||||
Name: "must-change-password",
|
||||
Usage: "Set to false to prevent forcing the user to change their password after initial login",
|
||||
Usage: "User must change password after initial login, defaults to true for all users except the first one (can be disabled by --must-change-password=false)",
|
||||
DisableDefaultText: true,
|
||||
},
|
||||
&cli.IntFlag{
|
||||
|
@ -91,11 +92,16 @@ func runCreateUser(c *cli.Context) error {
|
|||
_, _ = fmt.Fprintf(c.App.ErrWriter, "--name flag is deprecated. Use --username instead.\n")
|
||||
}
|
||||
|
||||
ctx, cancel := installSignals()
|
||||
defer cancel()
|
||||
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
ctx := c.Context
|
||||
if !setting.IsInTesting {
|
||||
// FIXME: need to refactor the "installSignals/initDB" related code later
|
||||
// it doesn't make sense to call it in (almost) every command action function
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = installSignals()
|
||||
defer cancel()
|
||||
if err := initDB(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var password string
|
||||
|
@ -123,8 +129,8 @@ func runCreateUser(c *cli.Context) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("IsTableNotEmpty: %w", err)
|
||||
}
|
||||
if !hasUserRecord && isAdmin {
|
||||
// if this is the first admin being created, don't force to change password (keep the old behavior)
|
||||
if !hasUserRecord {
|
||||
// if this is the first one being created, don't force to change password (keep the old behavior)
|
||||
mustChangePassword = false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAdminUserCreate(t *testing.T) {
|
||||
app := NewMainApp(AppVersion{})
|
||||
|
||||
reset := func() {
|
||||
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.User{}))
|
||||
assert.NoError(t, db.TruncateBeans(db.DefaultContext, &user_model.EmailAddress{}))
|
||||
}
|
||||
|
||||
type createCheck struct{ IsAdmin, MustChangePassword bool }
|
||||
createUser := func(name, args string) createCheck {
|
||||
assert.NoError(t, app.Run(strings.Fields(fmt.Sprintf("./gitea admin user create --username %s --email %s@gitea.local %s --password foobar", name, name, args))))
|
||||
u := unittest.AssertExistsAndLoadBean(t, &user_model.User{LowerName: name})
|
||||
return createCheck{u.IsAdmin, u.MustChangePassword}
|
||||
}
|
||||
reset()
|
||||
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u", ""), "first non-admin user doesn't need to change password")
|
||||
|
||||
reset()
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u", "--admin"), "first admin user doesn't need to change password")
|
||||
|
||||
reset()
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u", "--admin --must-change-password"))
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: true}, createUser("u2", "--admin"))
|
||||
assert.Equal(t, createCheck{IsAdmin: true, MustChangePassword: false}, createUser("u3", "--admin --must-change-password=false"))
|
||||
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: true}, createUser("u4", ""))
|
||||
assert.Equal(t, createCheck{IsAdmin: false, MustChangePassword: false}, createUser("u5", "--must-change-password=false"))
|
||||
}
|
|
@ -112,13 +112,18 @@ func prepareWorkPathAndCustomConf(action cli.ActionFunc) func(ctx *cli.Context)
|
|||
}
|
||||
}
|
||||
|
||||
func NewMainApp(version, versionExtra string) *cli.App {
|
||||
type AppVersion struct {
|
||||
Version string
|
||||
Extra string
|
||||
}
|
||||
|
||||
func NewMainApp(appVer AppVersion) *cli.App {
|
||||
app := cli.NewApp()
|
||||
app.Name = "Gitea"
|
||||
app.HelpName = "gitea"
|
||||
app.Usage = "A painless self-hosted Git service"
|
||||
app.Description = `Gitea program contains "web" and other subcommands. If no subcommand is given, it starts the web server by default. Use "web" subcommand for more web server arguments, use other subcommands for other purposes.`
|
||||
app.Version = version + versionExtra
|
||||
app.Version = appVer.Version + appVer.Extra
|
||||
app.EnableBashCompletion = true
|
||||
|
||||
// these sub-commands need to use config file
|
||||
|
|
|
@ -28,7 +28,7 @@ func makePathOutput(workPath, customPath, customConf string) string {
|
|||
}
|
||||
|
||||
func newTestApp(testCmdAction func(ctx *cli.Context) error) *cli.App {
|
||||
app := NewMainApp("version", "version-extra")
|
||||
app := NewMainApp(AppVersion{})
|
||||
testCmd := &cli.Command{Name: "test-cmd", Action: testCmdAction}
|
||||
prepareSubcommandWithConfig(testCmd, appGlobalFlags())
|
||||
app.Commands = append(app.Commands, testCmd)
|
||||
|
|
|
@ -1322,7 +1322,7 @@ Defaultly every storage has their default base path like below
|
|||
| actions_log | actions_log/ |
|
||||
| actions_artifacts | actions_artifacts/ |
|
||||
|
||||
And bucket, basepath or `SERVE_DIRECT` could be special or overrided, if you want to use a different you can:
|
||||
And bucket, basepath or `SERVE_DIRECT` could be special or overridden, if you want to use a different you can:
|
||||
|
||||
```ini
|
||||
[storage.actions_log]
|
||||
|
|
2
main.go
2
main.go
|
@ -42,7 +42,7 @@ func main() {
|
|||
log.GetManager().Close()
|
||||
os.Exit(code)
|
||||
}
|
||||
app := cmd.NewMainApp(Version, formatBuiltWith())
|
||||
app := cmd.NewMainApp(cmd.AppVersion{Version: Version, Extra: formatBuiltWith()})
|
||||
_ = cmd.RunMainApp(app, os.Args...) // all errors should have been handled by the RunMainApp
|
||||
log.GetManager().Close()
|
||||
}
|
||||
|
|
|
@ -262,11 +262,11 @@ func CancelPreviousJobs(ctx context.Context, repoID int64, ref, workflowID strin
|
|||
|
||||
// InsertRun inserts a run
|
||||
func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWorkflow) error {
|
||||
ctx, commiter, err := db.TxContext(ctx)
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer commiter.Close()
|
||||
defer committer.Close()
|
||||
|
||||
index, err := db.GetNextResourceIndex(ctx, "action_run_index", run.RepoID)
|
||||
if err != nil {
|
||||
|
@ -331,7 +331,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork
|
|||
}
|
||||
}
|
||||
|
||||
return commiter.Commit()
|
||||
return committer.Commit()
|
||||
}
|
||||
|
||||
func GetRunByID(ctx context.Context, id int64) (*ActionRun, error) {
|
||||
|
|
|
@ -216,11 +216,11 @@ func GetRunningTaskByToken(ctx context.Context, token string) (*ActionTask, erro
|
|||
}
|
||||
|
||||
func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask, bool, error) {
|
||||
ctx, commiter, err := db.TxContext(ctx)
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
defer commiter.Close()
|
||||
defer committer.Close()
|
||||
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
|
@ -322,7 +322,7 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask
|
|||
|
||||
task.Job = job
|
||||
|
||||
if err := commiter.Commit(); err != nil {
|
||||
if err := committer.Commit(); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
|
||||
|
@ -347,11 +347,11 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
|
|||
stepStates[v.Id] = v
|
||||
}
|
||||
|
||||
ctx, commiter, err := db.TxContext(ctx)
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer commiter.Close()
|
||||
defer committer.Close()
|
||||
|
||||
e := db.GetEngine(ctx)
|
||||
|
||||
|
@ -412,7 +412,7 @@ func UpdateTaskByState(ctx context.Context, state *runnerv1.TaskState) (*ActionT
|
|||
}
|
||||
}
|
||||
|
||||
if err := commiter.Commit(); err != nil {
|
||||
if err := committer.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ import (
|
|||
|
||||
// ActionTasksVersion
|
||||
// If both ownerID and repoID is zero, its scope is global.
|
||||
// If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currrently).
|
||||
// If ownerID is not zero and repoID is zero, its scope is org (there is no user-level runner currently).
|
||||
// If ownerID is zero and repoID is not zero, its scope is repo.
|
||||
type ActionTasksVersion struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
|
@ -73,11 +73,11 @@ func increaseTasksVersionByScope(ctx context.Context, ownerID, repoID int64) err
|
|||
}
|
||||
|
||||
func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
|
||||
ctx, commiter, err := db.TxContext(ctx)
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer commiter.Close()
|
||||
defer committer.Close()
|
||||
|
||||
// 1. increase global
|
||||
if err := increaseTasksVersionByScope(ctx, 0, 0); err != nil {
|
||||
|
@ -101,5 +101,5 @@ func IncreaseTaskVersion(ctx context.Context, ownerID, repoID int64) error {
|
|||
}
|
||||
}
|
||||
|
||||
return commiter.Commit()
|
||||
return committer.Commit()
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
-
|
||||
id: 1
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 2
|
||||
index: 2
|
||||
head_repo_id: 1
|
||||
|
@ -16,7 +16,7 @@
|
|||
-
|
||||
id: 2
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 3
|
||||
index: 3
|
||||
head_repo_id: 1
|
||||
|
@ -29,7 +29,7 @@
|
|||
-
|
||||
id: 3
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 8
|
||||
index: 1
|
||||
head_repo_id: 11
|
||||
|
@ -42,7 +42,7 @@
|
|||
-
|
||||
id: 4
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 9
|
||||
index: 1
|
||||
head_repo_id: 48
|
||||
|
@ -55,7 +55,7 @@
|
|||
-
|
||||
id: 5 # this PR is outdated (one commit behind branch1 )
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 11
|
||||
index: 5
|
||||
head_repo_id: 1
|
||||
|
@ -68,7 +68,7 @@
|
|||
-
|
||||
id: 6
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 12
|
||||
index: 2
|
||||
head_repo_id: 3
|
||||
|
@ -81,7 +81,7 @@
|
|||
-
|
||||
id: 7
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 19
|
||||
index: 1
|
||||
head_repo_id: 58
|
||||
|
@ -94,7 +94,7 @@
|
|||
-
|
||||
id: 8
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 20
|
||||
index: 1
|
||||
head_repo_id: 23
|
||||
|
@ -103,7 +103,7 @@
|
|||
-
|
||||
id: 9
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 21
|
||||
index: 1
|
||||
head_repo_id: 60
|
||||
|
@ -112,7 +112,7 @@
|
|||
-
|
||||
id: 10
|
||||
type: 0 # gitea pull request
|
||||
status: 2 # mergable
|
||||
status: 2 # mergeable
|
||||
issue_id: 22
|
||||
index: 1
|
||||
head_repo_id: 61
|
||||
|
|
|
@ -807,7 +807,7 @@ func UpdateAllowEdits(ctx context.Context, pr *PullRequest) error {
|
|||
|
||||
// Mergeable returns if the pullrequest is mergeable.
|
||||
func (pr *PullRequest) Mergeable(ctx context.Context) bool {
|
||||
// If a pull request isn't mergable if it's:
|
||||
// If a pull request isn't mergeable if it's:
|
||||
// - Being conflict checked.
|
||||
// - Has a conflict.
|
||||
// - Received a error while being conflict checked.
|
||||
|
|
|
@ -187,8 +187,8 @@ func AddTime(ctx context.Context, user *user_model.User, issue *Issue, amount in
|
|||
Issue: issue,
|
||||
Repo: issue.Repo,
|
||||
Doer: user,
|
||||
// Content before v1.21 did store the formated string instead of seconds,
|
||||
// so use "|" as delimeter to mark the new format
|
||||
// Content before v1.21 did store the formatted string instead of seconds,
|
||||
// so use "|" as delimiter to mark the new format
|
||||
Content: fmt.Sprintf("|%d", amount),
|
||||
Type: CommentTypeAddTimeManual,
|
||||
TimeID: t.ID,
|
||||
|
@ -267,8 +267,8 @@ func DeleteIssueUserTimes(ctx context.Context, issue *Issue, user *user_model.Us
|
|||
Issue: issue,
|
||||
Repo: issue.Repo,
|
||||
Doer: user,
|
||||
// Content before v1.21 did store the formated string instead of seconds,
|
||||
// so use "|" as delimeter to mark the new format
|
||||
// Content before v1.21 did store the formatted string instead of seconds,
|
||||
// so use "|" as delimiter to mark the new format
|
||||
Content: fmt.Sprintf("|%d", removedTime),
|
||||
Type: CommentTypeDeleteTimeManual,
|
||||
}); err != nil {
|
||||
|
@ -298,8 +298,8 @@ func DeleteTime(ctx context.Context, t *TrackedTime) error {
|
|||
Issue: t.Issue,
|
||||
Repo: t.Issue.Repo,
|
||||
Doer: t.User,
|
||||
// Content before v1.21 did store the formated string instead of seconds,
|
||||
// so use "|" as delimeter to mark the new format
|
||||
// Content before v1.21 did store the formatted string instead of seconds,
|
||||
// so use "|" as delimiter to mark the new format
|
||||
Content: fmt.Sprintf("|%d", t.Time),
|
||||
Type: CommentTypeDeleteTimeManual,
|
||||
}); err != nil {
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
"code.gitea.io/gitea/models/migrations/v1_20"
|
||||
"code.gitea.io/gitea/models/migrations/v1_21"
|
||||
"code.gitea.io/gitea/models/migrations/v1_22"
|
||||
"code.gitea.io/gitea/models/migrations/v1_23"
|
||||
"code.gitea.io/gitea/models/migrations/v1_6"
|
||||
"code.gitea.io/gitea/models/migrations/v1_7"
|
||||
"code.gitea.io/gitea/models/migrations/v1_8"
|
||||
|
@ -574,18 +573,20 @@ var migrations = []Migration{
|
|||
// v293 -> v294
|
||||
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
|
||||
|
||||
// Gitea 1.22.0 ends at 294
|
||||
// Gitea 1.22.0-rc0 ends at 294
|
||||
|
||||
// v294 -> v295
|
||||
NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue),
|
||||
NewMigration("Add unique index for project issue table", v1_22.AddUniqueIndexForProjectIssue),
|
||||
// v295 -> v296
|
||||
NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary),
|
||||
NewMigration("Add commit status summary table", v1_22.AddCommitStatusSummary),
|
||||
// v296 -> v297
|
||||
NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2),
|
||||
NewMigration("Add missing field of commit status summary table", v1_22.AddCommitStatusSummary2),
|
||||
// v297 -> v298
|
||||
NewMigration("Add everyone_access_mode for repo_unit", v1_23.AddRepoUnitEveryoneAccessMode),
|
||||
NewMigration("Add everyone_access_mode for repo_unit", v1_22.AddRepoUnitEveryoneAccessMode),
|
||||
// v298 -> v299
|
||||
NewMigration("Drop wrongly created table o_auth2_application", v1_23.DropWronglyCreatedTable),
|
||||
NewMigration("Drop wrongly created table o_auth2_application", v1_22.DropWronglyCreatedTable),
|
||||
|
||||
// Gitea 1.22.0-rc1 ends at 299
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
package v1_17 //nolint
|
||||
|
||||
// This migration added non-ideal indices to the action table which on larger datasets slowed things down
|
||||
// it has been superceded by v218.go
|
||||
// it has been superseded by v218.go
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"fmt"
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"slices"
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
package v1_22 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
package v1_22 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models/perm"
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_23 //nolint
|
||||
package v1_22 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
|
@ -6,7 +6,6 @@ package unittest
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
@ -18,6 +17,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/cache"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/setting/config"
|
||||
"code.gitea.io/gitea/modules/storage"
|
||||
|
@ -46,6 +46,14 @@ func fatalTestError(fmtStr string, args ...any) {
|
|||
|
||||
// InitSettings initializes config provider and load common settings for tests
|
||||
func InitSettings() {
|
||||
setting.IsInTesting = true
|
||||
log.OsExiter = func(code int) {
|
||||
if code != 0 {
|
||||
// non-zero exit code (log.Fatal) shouldn't occur during testing, if it happens, show a full stacktrace for more details
|
||||
panic(fmt.Errorf("non-zero exit code during testing: %d", code))
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
if setting.CustomConf == "" {
|
||||
setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini")
|
||||
_ = os.Remove(setting.CustomConf)
|
||||
|
@ -54,7 +62,7 @@ func InitSettings() {
|
|||
setting.LoadCommonSettings()
|
||||
|
||||
if err := setting.PrepareAppDataPath(); err != nil {
|
||||
log.Fatalf("Can not prepare APP_DATA_PATH: %v", err)
|
||||
log.Fatal("Can not prepare APP_DATA_PATH: %v", err)
|
||||
}
|
||||
// register the dummy hash algorithm function used in the test fixtures
|
||||
_ = hash.Register("dummy", hash.NewDummyHasher)
|
||||
|
|
|
@ -184,7 +184,7 @@ func (ref RefName) RefGroup() string {
|
|||
}
|
||||
|
||||
// RefType returns the simple ref type of the reference, e.g. branch, tag
|
||||
// It's differrent from RefGroup, which is using the name of the directory under .git/refs
|
||||
// It's different from RefGroup, which is using the name of the directory under .git/refs
|
||||
// Here we using branch but not heads, using tag but not tags
|
||||
func (ref RefName) RefType() string {
|
||||
var refType string
|
||||
|
|
|
@ -57,11 +57,13 @@ func Critical(format string, v ...any) {
|
|||
Log(1, ERROR, format, v...)
|
||||
}
|
||||
|
||||
var OsExiter = os.Exit
|
||||
|
||||
// Fatal records fatal log and exit process
|
||||
func Fatal(format string, v ...any) {
|
||||
Log(1, FATAL, format, v...)
|
||||
GetManager().Close()
|
||||
os.Exit(1)
|
||||
OsExiter(1)
|
||||
}
|
||||
|
||||
func GetLogger(name string) Logger {
|
||||
|
|
|
@ -134,7 +134,7 @@ func (pm *Manager) AddTypedContext(parent context.Context, description, processT
|
|||
//
|
||||
// Most processes will not need to use the cancel function but there will be cases whereby you want to cancel the process but not immediately remove it from the
|
||||
// process table.
|
||||
func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finshed FinishedFunc) {
|
||||
func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Duration, description string) (ctx context.Context, cancel context.CancelFunc, finished FinishedFunc) {
|
||||
if timeout <= 0 {
|
||||
// it's meaningless to use timeout <= 0, and it must be a bug! so we must panic here to tell developers to make the timeout correct
|
||||
panic("the timeout must be greater than zero, otherwise the context will be cancelled immediately")
|
||||
|
@ -142,9 +142,9 @@ func (pm *Manager) AddContextTimeout(parent context.Context, timeout time.Durati
|
|||
|
||||
ctx, cancel = context.WithTimeout(parent, timeout)
|
||||
|
||||
ctx, _, finshed = pm.Add(ctx, description, cancel, NormalProcessType, true)
|
||||
ctx, _, finished = pm.Add(ctx, description, cancel, NormalProcessType, true)
|
||||
|
||||
return ctx, cancel, finshed
|
||||
return ctx, cancel, finished
|
||||
}
|
||||
|
||||
// Add create a new process
|
||||
|
|
|
@ -49,9 +49,9 @@ func TestSubjectBodySeparator(t *testing.T) {
|
|||
test("Multiple\n---\n-------\n---\nSeparators",
|
||||
"Multiple\n",
|
||||
"\n-------\n---\nSeparators")
|
||||
test("Insuficient\n--\nSeparators",
|
||||
test("Insufficient\n--\nSeparators",
|
||||
"",
|
||||
"Insuficient\n--\nSeparators")
|
||||
"Insufficient\n--\nSeparators")
|
||||
}
|
||||
|
||||
func TestJSEscapeSafe(t *testing.T) {
|
||||
|
|
File diff suppressed because it is too large
Load Diff
26
package.json
26
package.json
|
@ -4,9 +4,9 @@
|
|||
"node": ">= 18.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@citation-js/core": "0.7.9",
|
||||
"@citation-js/plugin-bibtex": "0.7.9",
|
||||
"@citation-js/plugin-csl": "0.7.9",
|
||||
"@citation-js/core": "0.7.11",
|
||||
"@citation-js/plugin-bibtex": "0.7.11",
|
||||
"@citation-js/plugin-csl": "0.7.11",
|
||||
"@citation-js/plugin-software-formats": "0.6.1",
|
||||
"@github/markdown-toolbar-element": "2.2.3",
|
||||
"@github/relative-time-element": "4.4.0",
|
||||
|
@ -33,17 +33,17 @@
|
|||
"katex": "0.16.10",
|
||||
"license-checker-webpack-plugin": "0.2.1",
|
||||
"mermaid": "10.9.0",
|
||||
"mini-css-extract-plugin": "2.8.1",
|
||||
"mini-css-extract-plugin": "2.9.0",
|
||||
"minimatch": "9.0.4",
|
||||
"monaco-editor": "0.47.0",
|
||||
"monaco-editor": "0.48.0",
|
||||
"monaco-editor-webpack-plugin": "7.1.0",
|
||||
"pdfobject": "2.3.0",
|
||||
"postcss": "8.4.38",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-nesting": "12.1.1",
|
||||
"postcss-nesting": "12.1.2",
|
||||
"pretty-ms": "9.0.0",
|
||||
"sortablejs": "1.15.2",
|
||||
"swagger-ui-dist": "5.15.1",
|
||||
"swagger-ui-dist": "5.17.2",
|
||||
"tailwindcss": "3.4.3",
|
||||
"temporal-polyfill": "0.2.4",
|
||||
"throttle-debounce": "5.0.0",
|
||||
|
@ -53,7 +53,7 @@
|
|||
"tributejs": "5.1.3",
|
||||
"uint8-to-base64": "0.2.0",
|
||||
"vanilla-colorful": "0.7.2",
|
||||
"vue": "3.4.21",
|
||||
"vue": "3.4.25",
|
||||
"vue-bar-graph": "2.0.0",
|
||||
"vue-chartjs": "5.3.1",
|
||||
"vue-loader": "17.4.2",
|
||||
|
@ -66,7 +66,7 @@
|
|||
"@eslint-community/eslint-plugin-eslint-comments": "4.3.0",
|
||||
"@playwright/test": "1.43.1",
|
||||
"@stoplight/spectral-cli": "6.11.1",
|
||||
"@stylistic/eslint-plugin-js": "1.7.0",
|
||||
"@stylistic/eslint-plugin-js": "1.7.2",
|
||||
"@stylistic/stylelint-plugin": "2.1.1",
|
||||
"@vitejs/plugin-vue": "5.0.4",
|
||||
"eslint": "8.57.0",
|
||||
|
@ -81,20 +81,20 @@
|
|||
"eslint-plugin-unicorn": "52.0.0",
|
||||
"eslint-plugin-vitest": "0.4.1",
|
||||
"eslint-plugin-vitest-globals": "1.5.0",
|
||||
"eslint-plugin-vue": "9.24.1",
|
||||
"eslint-plugin-vue": "9.25.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.8.0",
|
||||
"eslint-plugin-wc": "2.1.0",
|
||||
"happy-dom": "14.7.1",
|
||||
"markdownlint-cli": "0.39.0",
|
||||
"postcss-html": "1.6.0",
|
||||
"stylelint": "16.3.1",
|
||||
"stylelint": "16.4.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||
"stylelint-declaration-strict-value": "1.10.4",
|
||||
"stylelint-value-no-unknown-custom-properties": "6.0.1",
|
||||
"svgo": "3.2.0",
|
||||
"updates": "16.0.1",
|
||||
"vite-string-plugin": "1.1.5",
|
||||
"vitest": "1.5.0"
|
||||
"vite-string-plugin": "1.2.0",
|
||||
"vitest": "1.5.2"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
|
|
|
@ -301,7 +301,7 @@ func (ar artifactRoutes) uploadArtifact(ctx *ArtifactContext) {
|
|||
})
|
||||
}
|
||||
|
||||
// comfirmUploadArtifact comfirm upload artifact.
|
||||
// comfirmUploadArtifact confirm upload artifact.
|
||||
// if all chunks are uploaded, merge them to one file.
|
||||
func (ar artifactRoutes) comfirmUploadArtifact(ctx *ArtifactContext) {
|
||||
_, runID, ok := validateRunID(ctx)
|
||||
|
|
|
@ -36,7 +36,7 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
|
|||
uuid := request.Header().Get(uuidHeaderKey)
|
||||
token := request.Header().Get(tokenHeaderKey)
|
||||
// TODO: version will be removed from request header after Gitea 1.20 released.
|
||||
// And Gitea will not try to read version from reuqest header
|
||||
// And Gitea will not try to read version from request header
|
||||
version := request.Header().Get(versionHeaderKey)
|
||||
|
||||
runner, err := actions_model.GetRunnerByUUID(ctx, uuid)
|
||||
|
@ -53,7 +53,7 @@ var withRunner = connect.WithInterceptors(connect.UnaryInterceptorFunc(func(unar
|
|||
cols := []string{"last_online"}
|
||||
|
||||
// TODO: version will be removed from request header after Gitea 1.20 released.
|
||||
// And Gitea will not try to read version from reuqest header
|
||||
// And Gitea will not try to read version from request header
|
||||
version, _ = util.SplitStringAtByteN(version, 64)
|
||||
if !util.IsEmptyString(version) && runner.Version != version {
|
||||
runner.Version = version
|
||||
|
|
|
@ -19,7 +19,7 @@ The package registry code is divided into multiple modules to split the function
|
|||
|
||||
## Models
|
||||
|
||||
Every package registry implementation uses the same underlaying models:
|
||||
Every package registry implementation uses the same underlying models:
|
||||
|
||||
| Model | Description |
|
||||
| - | - |
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
package repo
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
@ -372,7 +373,11 @@ func CreatePullReview(ctx *context.APIContext) {
|
|||
// create review and associate all pending review comments
|
||||
review, _, err := pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, opts.CommitID, nil)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
|
||||
if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -460,7 +465,11 @@ func SubmitPullReview(ctx *context.APIContext) {
|
|||
// create review and associate all pending review comments
|
||||
review, _, err = pull_service.SubmitReview(ctx, ctx.Doer, ctx.Repo.GitRepo, pr.Issue, reviewType, opts.Body, headCommitID, nil)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
|
||||
if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
|
||||
ctx.Error(http.StatusUnprocessableEntity, "", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "SubmitReview", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
// RegistrationToken is response related to registeration token
|
||||
// RegistrationToken is response related to registration token
|
||||
// swagger:response RegistrationToken
|
||||
type RegistrationToken struct {
|
||||
Token string `json:"token"`
|
||||
|
|
|
@ -359,7 +359,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r
|
|||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to check if mergable: protected branch %s in %-v and pr #%d. Error: %v", ctx.opts.UserID, branchName, repo, pr.Index, err)
|
||||
log.Error("Unable to check if mergeable: protected branch %s in %-v and pr #%d. Error: %v", ctx.opts.UserID, branchName, repo, pr.Index, err)
|
||||
ctx.JSON(http.StatusInternalServerError, private.Response{
|
||||
Err: fmt.Sprintf("Unable to get status of pull request %d. Error: %v", ctx.opts.PullRequestID, err),
|
||||
})
|
||||
|
|
|
@ -30,7 +30,7 @@ func Organizations(ctx *context.Context) {
|
|||
explore.RenderUserSearch(ctx, &user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Type: user_model.UserTypeOrganization,
|
||||
IncludeReserved: true, // administrator needs to list all acounts include reserved
|
||||
IncludeReserved: true, // administrator needs to list all accounts include reserved
|
||||
ListOptions: db.ListOptions{
|
||||
PageSize: setting.UI.Admin.OrgPagingNum,
|
||||
},
|
||||
|
|
|
@ -81,7 +81,7 @@ func Users(ctx *context.Context) {
|
|||
IsRestricted: util.OptionalBoolParse(statusFilterMap["is_restricted"]),
|
||||
IsTwoFactorEnabled: util.OptionalBoolParse(statusFilterMap["is_2fa_enabled"]),
|
||||
IsProhibitLogin: util.OptionalBoolParse(statusFilterMap["is_prohibit_login"]),
|
||||
IncludeReserved: true, // administrator needs to list all acounts include reserved, bot, remote ones
|
||||
IncludeReserved: true, // administrator needs to list all accounts include reserved, bot, remote ones
|
||||
ExtraParamStrings: extraParamStrings,
|
||||
}, tplUsers)
|
||||
}
|
||||
|
|
|
@ -812,7 +812,7 @@ func CompareDiff(ctx *context.Context) {
|
|||
// applicable if you have one commit to compare and that commit has a message.
|
||||
// In that case the commit message will be prepend to the template body.
|
||||
if templateContent, ok := ctx.Data[pullRequestTemplateKey].(string); ok && templateContent != "" {
|
||||
// Re-use the same key as that's priortized over the "content" key.
|
||||
// Re-use the same key as that's prioritized over the "content" key.
|
||||
// Add two new lines between the content to ensure there's always at least
|
||||
// one empty line between them.
|
||||
ctx.Data[pullRequestTemplateKey] = content + "\n\n" + templateContent
|
||||
|
|
|
@ -1760,8 +1760,8 @@ func ViewIssue(ctx *context.Context) {
|
|||
// drop error since times could be pruned from DB..
|
||||
_ = comment.LoadTime(ctx)
|
||||
if comment.Content != "" {
|
||||
// Content before v1.21 did store the formated string instead of seconds,
|
||||
// so "|" is used as delimeter to mark the new format
|
||||
// Content before v1.21 did store the formatted string instead of seconds,
|
||||
// so "|" is used as delimiter to mark the new format
|
||||
if comment.Content[0] != '|' {
|
||||
// handle old time comments that have formatted text stored
|
||||
comment.RenderedContent = templates.SanitizeHTML(comment.Content)
|
||||
|
|
|
@ -264,6 +264,8 @@ func SubmitReview(ctx *context.Context) {
|
|||
if issues_model.IsContentEmptyErr(err) {
|
||||
ctx.Flash.Error(ctx.Tr("repo.issues.review.content.empty"))
|
||||
ctx.JSONRedirect(fmt.Sprintf("%s/pulls/%d/files", ctx.Repo.RepoLink, issue.Index))
|
||||
} else if errors.Is(err, pull_service.ErrSubmitReviewOnClosedPR) {
|
||||
ctx.Status(http.StatusUnprocessableEntity)
|
||||
} else {
|
||||
ctx.ServerError("SubmitReview", err)
|
||||
}
|
||||
|
|
|
@ -72,8 +72,8 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu
|
|||
c.Type == issues_model.CommentTypeStopTracking ||
|
||||
c.Type == issues_model.CommentTypeDeleteTimeManual) &&
|
||||
c.Content[0] == '|' {
|
||||
// TimeTracking Comments from v1.21 on store the seconds instead of an formated string
|
||||
// so we check for the "|" delimeter and convert new to legacy format on demand
|
||||
// TimeTracking Comments from v1.21 on store the seconds instead of an formatted string
|
||||
// so we check for the "|" delimiter and convert new to legacy format on demand
|
||||
c.Content = util.SecToTime(c.Content[1:])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -229,12 +229,12 @@ func TeamReviewRequest(ctx context.Context, issue *issues_model.Issue, doer *use
|
|||
return comment, teamReviewRequestNotify(ctx, issue, doer, reviewer, isAdd, comment)
|
||||
}
|
||||
|
||||
func ReviewRequestNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewNotifers []*ReviewRequestNotifier) {
|
||||
for _, reviewNotifer := range reviewNotifers {
|
||||
if reviewNotifer.Reviwer != nil {
|
||||
notify_service.PullRequestReviewRequest(ctx, issue.Poster, issue, reviewNotifer.Reviwer, reviewNotifer.IsAdd, reviewNotifer.Comment)
|
||||
} else if reviewNotifer.ReviewTeam != nil {
|
||||
if err := teamReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifer.ReviewTeam, reviewNotifer.IsAdd, reviewNotifer.Comment); err != nil {
|
||||
func ReviewRequestNotify(ctx context.Context, issue *issues_model.Issue, doer *user_model.User, reviewNotifiers []*ReviewRequestNotifier) {
|
||||
for _, reviewNotifier := range reviewNotifiers {
|
||||
if reviewNotifier.Reviewer != nil {
|
||||
notify_service.PullRequestReviewRequest(ctx, issue.Poster, issue, reviewNotifier.Reviewer, reviewNotifier.IsAdd, reviewNotifier.Comment)
|
||||
} else if reviewNotifier.ReviewTeam != nil {
|
||||
if err := teamReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifier.ReviewTeam, reviewNotifier.IsAdd, reviewNotifier.Comment); err != nil {
|
||||
log.Error("teamReviewRequestNotify: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,17 +90,17 @@ func ChangeTitle(ctx context.Context, issue *issues_model.Issue, doer *user_mode
|
|||
return err
|
||||
}
|
||||
|
||||
var reviewNotifers []*ReviewRequestNotifier
|
||||
var reviewNotifiers []*ReviewRequestNotifier
|
||||
if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issues_model.HasWorkInProgressPrefix(title) {
|
||||
var err error
|
||||
reviewNotifers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest)
|
||||
reviewNotifiers, err = PullRequestCodeOwnersReview(ctx, issue, issue.PullRequest)
|
||||
if err != nil {
|
||||
log.Error("PullRequestCodeOwnersReview: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
notify_service.IssueChangeTitle(ctx, doer, issue, oldTitle)
|
||||
ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers)
|
||||
ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ func getMergeBase(repo *git.Repository, pr *issues_model.PullRequest, baseBranch
|
|||
type ReviewRequestNotifier struct {
|
||||
Comment *issues_model.Comment
|
||||
IsAdd bool
|
||||
Reviwer *user_model.User
|
||||
Reviewer *user_model.User
|
||||
ReviewTeam *org_model.Team
|
||||
}
|
||||
|
||||
|
@ -124,9 +124,9 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
|
|||
return nil, err
|
||||
}
|
||||
notifiers = append(notifiers, &ReviewRequestNotifier{
|
||||
Comment: comment,
|
||||
IsAdd: true,
|
||||
Reviwer: u,
|
||||
Comment: comment,
|
||||
IsAdd: true,
|
||||
Reviewer: u,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
|
|||
}
|
||||
log.Trace("Doing: Update")
|
||||
|
||||
handler := func(idx int, bean any) error {
|
||||
handler := func(bean any) error {
|
||||
var repo *repo_model.Repository
|
||||
var mirrorType SyncType
|
||||
var referenceID int64
|
||||
|
@ -91,7 +91,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
|
|||
pullMirrorsRequested := 0
|
||||
if pullLimit != 0 {
|
||||
if err := repo_model.MirrorsIterate(ctx, pullLimit, func(idx int, bean any) error {
|
||||
if err := handler(idx, bean); err != nil {
|
||||
if err := handler(bean); err != nil {
|
||||
return err
|
||||
}
|
||||
pullMirrorsRequested++
|
||||
|
@ -105,7 +105,7 @@ func Update(ctx context.Context, pullLimit, pushLimit int) error {
|
|||
pushMirrorsRequested := 0
|
||||
if pushLimit != 0 {
|
||||
if err := repo_model.PushMirrorsIterate(ctx, pushLimit, func(idx int, bean any) error {
|
||||
if err := handler(idx, bean); err != nil {
|
||||
if err := handler(bean); err != nil {
|
||||
return err
|
||||
}
|
||||
pushMirrorsRequested++
|
||||
|
|
|
@ -466,7 +466,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
|||
|
||||
log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results))
|
||||
if len(results) > 0 {
|
||||
if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok {
|
||||
if ok := checkAndUpdateEmptyRepository(ctx, m, results); !ok {
|
||||
log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err)
|
||||
return false
|
||||
}
|
||||
|
@ -564,7 +564,7 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, gitRepo *git.Repository, results []*mirrorSyncResult) bool {
|
||||
func checkAndUpdateEmptyRepository(ctx context.Context, m *repo_model.Mirror, results []*mirrorSyncResult) bool {
|
||||
if !m.Repo.IsEmpty {
|
||||
return true
|
||||
}
|
||||
|
|
|
@ -20,11 +20,11 @@ import (
|
|||
|
||||
// DeleteOrganization completely and permanently deletes everything of organization.
|
||||
func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge bool) error {
|
||||
ctx, commiter, err := db.TxContext(ctx)
|
||||
ctx, committer, err := db.TxContext(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer commiter.Close()
|
||||
defer committer.Close()
|
||||
|
||||
if purge {
|
||||
err := repo_service.DeleteOwnerRepositoriesDirectly(ctx, org.AsUser())
|
||||
|
@ -52,7 +52,7 @@ func DeleteOrganization(ctx context.Context, org *org_model.Organization, purge
|
|||
return fmt.Errorf("DeleteOrganization: %w", err)
|
||||
}
|
||||
|
||||
if err := commiter.Commit(); err != nil {
|
||||
if err := committer.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -66,7 +66,7 @@ const (
|
|||
MergeCheckTypeAuto // Auto Merge (Scheduled Merge) After Checks Succeed
|
||||
)
|
||||
|
||||
// CheckPullMergable check if the pull mergable based on all conditions (branch protection, merge options, ...)
|
||||
// CheckPullMergable check if the pull mergeable based on all conditions (branch protection, merge options, ...)
|
||||
func CheckPullMergable(stdCtx context.Context, doer *user_model.User, perm *access_model.Permission, pr *issues_model.PullRequest, mergeCheckType MergeCheckType, adminSkipProtectionCheck bool) error {
|
||||
return db.WithTx(stdCtx, func(ctx context.Context) error {
|
||||
if pr.HasMerged {
|
||||
|
|
|
@ -46,7 +46,7 @@ func getCommitIDsFromRepo(ctx context.Context, repo *repo_model.Repository, oldC
|
|||
return commitIDs, isForcePush, err
|
||||
}
|
||||
|
||||
// Find commits between new and old commit exclusing base branch commits
|
||||
// Find commits between new and old commit excluding base branch commits
|
||||
commits, err := gitRepo.CommitsBetweenNotBase(newCommit, oldCommit, baseBranch)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
|
|
|
@ -77,7 +77,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
|
|||
}
|
||||
defer baseGitRepo.Close()
|
||||
|
||||
var reviewNotifers []*issue_service.ReviewRequestNotifier
|
||||
var reviewNotifiers []*issue_service.ReviewRequestNotifier
|
||||
if err := db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if err := issues_model.NewPullRequest(ctx, repo, issue, labelIDs, uuids, pr); err != nil {
|
||||
return err
|
||||
|
@ -137,7 +137,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
|
|||
}
|
||||
|
||||
if !pr.IsWorkInProgress(ctx) {
|
||||
reviewNotifers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
|
||||
reviewNotifiers, err = issue_service.PullRequestCodeOwnersReview(ctx, issue, pr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -152,7 +152,7 @@ func NewPullRequest(ctx context.Context, repo *repo_model.Repository, issue *iss
|
|||
}
|
||||
baseGitRepo.Close() // close immediately to avoid notifications will open the repository again
|
||||
|
||||
issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifers)
|
||||
issue_service.ReviewRequestNotify(ctx, issue, issue.Poster, reviewNotifiers)
|
||||
|
||||
mentions, err := issues_model.FindAndUpdateIssueMentions(ctx, issue, issue.Poster, issue.Content)
|
||||
if err != nil {
|
||||
|
|
|
@ -6,6 +6,7 @@ package pull
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
|
@ -43,6 +44,9 @@ func (err ErrDismissRequestOnClosedPR) Unwrap() error {
|
|||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ErrSubmitReviewOnClosedPR represents an error when an user tries to submit an approve or reject review associated to a closed or merged PR.
|
||||
var ErrSubmitReviewOnClosedPR = errors.New("can't submit review for a closed or merged PR")
|
||||
|
||||
// checkInvalidation checks if the line of code comment got changed by another commit.
|
||||
// If the line got changed the comment is going to be invalidated.
|
||||
func checkInvalidation(ctx context.Context, c *issues_model.Comment, doer *user_model.User, repo *git.Repository, branch string) error {
|
||||
|
@ -293,6 +297,10 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
|
|||
if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject {
|
||||
stale = false
|
||||
} else {
|
||||
if issue.IsClosed {
|
||||
return nil, nil, ErrSubmitReviewOnClosedPR
|
||||
}
|
||||
|
||||
headCommitID, err := gitRepo.GetRefCommitID(pr.GetGitRefName())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
|
|
@ -191,8 +191,9 @@ export default {
|
|||
'no-invalid-double-slash-comments': true,
|
||||
'no-invalid-position-at-import-rule': [true, {ignoreAtRules: ['tailwind']}],
|
||||
'no-irregular-whitespace': true,
|
||||
'no-unknown-animations': null,
|
||||
'no-unknown-custom-properties': null,
|
||||
'no-unknown-animations': null, // disabled until stylelint supports multi-file linting
|
||||
'no-unknown-custom-media': null, // disabled until stylelint supports multi-file linting
|
||||
'no-unknown-custom-properties': null, // disabled until stylelint supports multi-file linting
|
||||
'number-max-precision': null,
|
||||
'plugin/declaration-block-no-ignored-properties': true,
|
||||
'property-allowed-list': null,
|
||||
|
|
|
@ -76,7 +76,8 @@
|
|||
{{ctx.Locale.Tr "admin.dashboard.system_status"}}
|
||||
</h4>
|
||||
{{/* TODO: make these stats work in multi-server deployments, likely needs per-server stats in DB */}}
|
||||
<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".divider" class="ui attached table segment">
|
||||
<div class="no-loading-indicator tw-hidden"></div>
|
||||
<div hx-get="{{$.Link}}/system_status" hx-swap="morph:innerHTML" hx-trigger="every 5s" hx-indicator=".no-loading-indicator" class="ui attached table segment">
|
||||
{{template "admin/system_status" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -235,7 +235,7 @@
|
|||
|
||||
{{if and (not $.Repository.IsArchived) (not .DiffNotAvailable)}}
|
||||
<template id="issue-comment-editor-template">
|
||||
<div class="ui comment form">
|
||||
<div class="ui form comment">
|
||||
{{template "shared/combomarkdowneditor" (dict
|
||||
"MarkdownPreviewUrl" (print $.Repository.Link "/markup")
|
||||
"MarkdownPreviewContext" $.RepoLink
|
||||
|
@ -249,7 +249,7 @@
|
|||
{{end}}
|
||||
<div class="text right edit buttons">
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary save button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -30,20 +30,24 @@
|
|||
{{end}}
|
||||
<div class="divider"></div>
|
||||
{{$showSelfTooltip := (and $.IsSigned ($.Issue.IsPoster $.SignedUser.ID))}}
|
||||
{{if $showSelfTooltip}}
|
||||
<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
|
||||
<button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
|
||||
</span>
|
||||
{{else}}
|
||||
<button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
|
||||
{{if not $.Issue.IsClosed}}
|
||||
{{if $showSelfTooltip}}
|
||||
<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_approve"}}">
|
||||
<button type="submit" name="type" value="approve" disabled class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
|
||||
</span>
|
||||
{{else}}
|
||||
<button type="submit" name="type" value="approve" class="ui submit primary tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.approve"}}</button>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<button type="submit" name="type" value="comment" class="ui submit tiny basic button btn-submit">{{ctx.Locale.Tr "repo.diff.review.comment"}}</button>
|
||||
{{if $showSelfTooltip}}
|
||||
<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
|
||||
<button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
|
||||
</span>
|
||||
{{else}}
|
||||
<button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
|
||||
{{if not $.Issue.IsClosed}}
|
||||
{{if $showSelfTooltip}}
|
||||
<span class="tw-inline-block" data-tooltip-content="{{ctx.Locale.Tr "repo.diff.review.self_reject"}}">
|
||||
<button type="submit" name="type" value="reject" disabled class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
|
||||
</span>
|
||||
{{else}}
|
||||
<button type="submit" name="type" value="reject" class="ui submit red tiny button btn-submit">{{ctx.Locale.Tr "repo.diff.review.reject"}}</button>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,16 @@
|
|||
{{with .Issue}}
|
||||
{{if eq $.Page.Project.CardType 1}}{{/* Images and Text*/}}
|
||||
{{$attachments := index $.Page.issuesAttachmentMap .ID}}
|
||||
{{if $attachments}}
|
||||
<div class="card-attachment-images">
|
||||
{{range (index $.Page.issuesAttachmentMap .ID)}}
|
||||
{{range $attachments}}
|
||||
<img src="{{.DownloadURL}}" alt="{{.Name}}" />
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<div class="content tw-p-0 tw-w-full">
|
||||
<div class="tw-flex tw-items-start">
|
||||
<div class="content tw-w-full">
|
||||
<div class="tw-flex tw-items-start tw-gap-[5px]">
|
||||
<div class="issue-card-icon">
|
||||
{{template "shared/issueicon" .}}
|
||||
</div>
|
||||
|
@ -18,7 +21,7 @@
|
|||
</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="meta tw-my-1">
|
||||
<div class="meta">
|
||||
<span class="text light grey muted-links">
|
||||
{{if not $.Page.Repository}}{{.Repo.FullName}}{{end}}#{{.Index}}
|
||||
{{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}}
|
||||
|
@ -59,13 +62,15 @@
|
|||
</div>
|
||||
|
||||
{{if or .Labels .Assignees}}
|
||||
<div class="extra content labels-list tw-p-0 tw-pt-1">
|
||||
{{range .Labels}}
|
||||
<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a>
|
||||
{{end}}
|
||||
<div class="right floated">
|
||||
<div class="tw-flex tw-justify-between">
|
||||
<div class="labels-list tw-flex-1">
|
||||
{{range .Labels}}
|
||||
<a target="_blank" href="{{$.Issue.Repo.Link}}/issues?labels={{.ID}}">{{RenderLabel ctx ctx.Locale .}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="tw-flex tw-flex-wrap tw-content-start tw-gap-1">
|
||||
{{range .Assignees}}
|
||||
<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28 "mini tw-mr-2"}}</a>
|
||||
<a target="_blank" href="{{.HomeLink}}" data-tooltip-content="{{ctx.Locale.Tr "repo.projects.column.assigned_to"}} {{.Name}}">{{ctx.AvatarUtils.Avatar . 28}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -146,7 +146,7 @@
|
|||
</div>
|
||||
|
||||
<template id="issue-comment-editor-template">
|
||||
<div class="ui comment form">
|
||||
<div class="ui form comment">
|
||||
<div class="field">
|
||||
{{template "shared/combomarkdowneditor" (dict
|
||||
"MarkdownPreviewUrl" (print .Repository.Link "/markup")
|
||||
|
@ -164,8 +164,8 @@
|
|||
|
||||
<div class="field">
|
||||
<div class="text right edit">
|
||||
<button class="ui basic cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary save button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
<button class="ui cancel button">{{ctx.Locale.Tr "repo.issues.cancel"}}</button>
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "repo.issues.save"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<span class="ui small label">{{if .NumFiles}}{{.NumFiles}}{{else}}-{{end}}</span>
|
||||
</a>
|
||||
{{if or .Diff.TotalAddition .Diff.TotalDeletion}}
|
||||
<span class="item tw-ml-auto tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
|
||||
<span class="tw-ml-auto tw-pl-3 tw-whitespace-nowrap tw-pr-0 tw-font-bold tw-flex tw-items-center tw-gap-2">
|
||||
<span><span class="text green">{{if .Diff.TotalAddition}}+{{.Diff.TotalAddition}}{{end}}</span> <span class="text red">{{if .Diff.TotalDeletion}}-{{.Diff.TotalDeletion}}{{end}}</span></span>
|
||||
<span class="diff-stats-bar">
|
||||
<div class="diff-stats-add-bar" style="width: {{Eval 100 "*" .Diff.TotalAddition "/" "(" .Diff.TotalAddition "+" .Diff.TotalDeletion "+" 0.0 ")"}}%"></div>
|
||||
|
|
|
@ -25282,7 +25282,7 @@
|
|||
}
|
||||
},
|
||||
"RegistrationToken": {
|
||||
"description": "RegistrationToken is response related to registeration token",
|
||||
"description": "RegistrationToken is response related to registration token",
|
||||
"headers": {
|
||||
"token": {
|
||||
"type": "string"
|
||||
|
|
|
@ -119,9 +119,9 @@ func TestAPIRepoIssueConfigPaths(t *testing.T) {
|
|||
".github/issue_template/config",
|
||||
}
|
||||
|
||||
for _, canidate := range templateConfigCandidates {
|
||||
for _, candidate := range templateConfigCandidates {
|
||||
for _, extension := range []string{".yaml", ".yml"} {
|
||||
fullPath := canidate + extension
|
||||
fullPath := candidate + extension
|
||||
t.Run(fullPath, func(t *testing.T) {
|
||||
configMap := make(map[string]any)
|
||||
configMap["blank_issues_enabled"] = false
|
||||
|
|
|
@ -67,7 +67,7 @@ func TestCompareBranches(t *testing.T) {
|
|||
|
||||
session := loginUser(t, "user2")
|
||||
|
||||
// Inderect compare remove-files-b (head) with add-csv (base) branch
|
||||
// Indirect compare remove-files-b (head) with add-csv (base) branch
|
||||
//
|
||||
// 'link_hi' and 'test.csv' are deleted, 'test.txt' is added
|
||||
req := NewRequest(t, "GET", "/user2/repo20/compare/add-csv...remove-files-b")
|
||||
|
@ -79,7 +79,7 @@ func TestCompareBranches(t *testing.T) {
|
|||
|
||||
inspectCompare(t, htmlDoc, diffCount, diffChanges)
|
||||
|
||||
// Inderect compare remove-files-b (head) with remove-files-a (base) branch
|
||||
// Indirect compare remove-files-b (head) with remove-files-a (base) branch
|
||||
//
|
||||
// 'link_hi' and 'test.csv' are deleted, 'test.txt' is added
|
||||
|
||||
|
@ -92,7 +92,7 @@ func TestCompareBranches(t *testing.T) {
|
|||
|
||||
inspectCompare(t, htmlDoc, diffCount, diffChanges)
|
||||
|
||||
// Inderect compare remove-files-a (head) with remove-files-b (base) branch
|
||||
// Indirect compare remove-files-a (head) with remove-files-b (base) branch
|
||||
//
|
||||
// 'link_hi' and 'test.csv' are deleted
|
||||
|
||||
|
|
|
@ -5,12 +5,15 @@ package integration
|
|||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
|
@ -176,3 +179,82 @@ func TestPullView_CodeOwner(t *testing.T) {
|
|||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestPullView_GivenApproveOrRejectReviewOnClosedPR(t *testing.T) {
|
||||
onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) {
|
||||
user1Session := loginUser(t, "user1")
|
||||
user2Session := loginUser(t, "user2")
|
||||
|
||||
// Have user1 create a fork of repo1.
|
||||
testRepoFork(t, user1Session, "user2", "repo1", "user1", "repo1")
|
||||
|
||||
t.Run("Submit approve/reject review on merged PR", func(t *testing.T) {
|
||||
// Create a merged PR (made by user1) in the upstream repo1.
|
||||
testEditFile(t, user1Session, "user1", "repo1", "master", "README.md", "Hello, World (Edited)\n")
|
||||
resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "master", "This is a pull title")
|
||||
elem := strings.Split(test.RedirectURL(resp), "/")
|
||||
assert.EqualValues(t, "pulls", elem[3])
|
||||
testPullMerge(t, user1Session, elem[1], elem[2], elem[4], repo_model.MergeStyleMerge, false)
|
||||
|
||||
// Grab the CSRF token.
|
||||
req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4]))
|
||||
resp = user2Session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
// Submit an approve review on the PR.
|
||||
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
|
||||
|
||||
// Submit a reject review on the PR.
|
||||
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
|
||||
})
|
||||
|
||||
t.Run("Submit approve/reject review on closed PR", func(t *testing.T) {
|
||||
// Created a closed PR (made by user1) in the upstream repo1.
|
||||
testEditFileToNewBranch(t, user1Session, "user1", "repo1", "master", "a-test-branch", "README.md", "Hello, World (Editied...again)\n")
|
||||
resp := testPullCreate(t, user1Session, "user1", "repo1", false, "master", "a-test-branch", "This is a pull title")
|
||||
elem := strings.Split(test.RedirectURL(resp), "/")
|
||||
assert.EqualValues(t, "pulls", elem[3])
|
||||
testIssueClose(t, user1Session, elem[1], elem[2], elem[4])
|
||||
|
||||
// Grab the CSRF token.
|
||||
req := NewRequest(t, "GET", path.Join(elem[1], elem[2], "pulls", elem[4]))
|
||||
resp = user2Session.MakeRequest(t, req, http.StatusOK)
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
|
||||
// Submit an approve review on the PR.
|
||||
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "approve", http.StatusUnprocessableEntity)
|
||||
|
||||
// Submit a reject review on the PR.
|
||||
testSubmitReview(t, user2Session, htmlDoc.GetCSRF(), "user2", "repo1", elem[4], "reject", http.StatusUnprocessableEntity)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func testSubmitReview(t *testing.T, session *TestSession, csrf, owner, repo, pullNumber, reviewType string, expectedSubmitStatus int) *httptest.ResponseRecorder {
|
||||
options := map[string]string{
|
||||
"_csrf": csrf,
|
||||
"commit_id": "",
|
||||
"content": "test",
|
||||
"type": reviewType,
|
||||
}
|
||||
|
||||
submitURL := path.Join(owner, repo, "pulls", pullNumber, "files", "reviews", "submit")
|
||||
req := NewRequestWithValues(t, "POST", submitURL, options)
|
||||
return session.MakeRequest(t, req, expectedSubmitStatus)
|
||||
}
|
||||
|
||||
func testIssueClose(t *testing.T, session *TestSession, owner, repo, issueNumber string) *httptest.ResponseRecorder {
|
||||
req := NewRequest(t, "GET", path.Join(owner, repo, "pulls", issueNumber))
|
||||
resp := session.MakeRequest(t, req, http.StatusOK)
|
||||
|
||||
htmlDoc := NewHTMLParser(t, resp.Body)
|
||||
closeURL := path.Join(owner, repo, "issues", issueNumber, "comments")
|
||||
|
||||
options := map[string]string{
|
||||
"_csrf": htmlDoc.GetCSRF(),
|
||||
"status": "close",
|
||||
}
|
||||
|
||||
req = NewRequestWithValues(t, "POST", closeURL, options)
|
||||
return session.MakeRequest(t, req, http.StatusOK)
|
||||
}
|
||||
|
|
|
@ -46,7 +46,6 @@ func InitTest(requireGitea bool) {
|
|||
// TODO: Speedup tests that rely on the event source ticker, confirm whether there is any bug or failure.
|
||||
// setting.UI.Notification.EventSourceUpdateTime = time.Second
|
||||
|
||||
setting.IsInTesting = true
|
||||
setting.AppWorkPath = giteaRoot
|
||||
setting.CustomPath = filepath.Join(setting.AppWorkPath, "custom")
|
||||
if requireGitea {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
acounts,accounts
|
||||
canidate,candidate
|
||||
comfirm,confirm
|
||||
converage,coverage
|
||||
currrently,currently
|
||||
delimeter,delimiter
|
||||
differrent,different
|
||||
exclusing,excluding
|
||||
finshed,finished
|
||||
formated,formatted
|
||||
inderect,indirect
|
||||
insuficient,insufficient
|
||||
likly,likely
|
||||
mergable,mergeable
|
||||
overrided,overridden
|
||||
priortized,prioritized
|
||||
registeration,registration
|
||||
reuqest,request
|
||||
reviwer,reviewer
|
||||
superceded,superseded
|
||||
underlaying,underlying
|
|
|
@ -680,10 +680,6 @@ input:-webkit-autofill:active,
|
|||
box-shadow: 0 6px 18px var(--color-shadow) !important;
|
||||
}
|
||||
|
||||
.ui.dimmer {
|
||||
background: var(--color-overlay-backdrop);
|
||||
}
|
||||
|
||||
.ui.dropdown .menu > .header {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
@import "./modules/table.css";
|
||||
@import "./modules/card.css";
|
||||
@import "./modules/checkbox.css";
|
||||
@import "./modules/dimmer.css";
|
||||
@import "./modules/modal.css";
|
||||
|
||||
@import "./modules/select.css";
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/* These are the remnants of the fomantic dimmer module */
|
||||
|
||||
.ui.dimmer {
|
||||
position: fixed;
|
||||
display: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--color-overlay-backdrop);
|
||||
opacity: 0;
|
||||
z-index: 1000;
|
||||
overflow-y: auto;
|
||||
justify-content: center;
|
||||
padding: 8px 0;
|
||||
animation-name: fadein;
|
||||
animation-duration: .2s;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ui.active.dimmer {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.ui.dimmer > * {
|
||||
position: static;
|
||||
margin-top: auto !important;
|
||||
margin-bottom: auto !important;
|
||||
}
|
|
@ -2520,7 +2520,7 @@ tbody.commit-list {
|
|||
display: inline-block;
|
||||
background-color: var(--color-red);
|
||||
height: 12px;
|
||||
width: 40px;
|
||||
width: 44px;
|
||||
}
|
||||
|
||||
.diff-stats-bar .diff-stats-add-bar {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
.issue-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
align-items: start;
|
||||
border-radius: var(--border-radius);
|
||||
padding: 8px 10px;
|
||||
|
@ -17,7 +18,6 @@
|
|||
.issue-card-title {
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.issue-card.sortable-chosen .issue-card-title {
|
||||
|
|
|
@ -8,363 +8,6 @@
|
|||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
/*!
|
||||
* # Fomantic-UI - Dimmer
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
*
|
||||
*
|
||||
* Released under the MIT license
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
|
||||
/*******************************
|
||||
Dimmer
|
||||
*******************************/
|
||||
|
||||
.dimmable:not(body) {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ui.dimmer {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0 !important;
|
||||
left: 0 !important;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
padding: 1em;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
opacity: 0;
|
||||
line-height: 1;
|
||||
animation-fill-mode: both;
|
||||
animation-duration: 0.5s;
|
||||
transition: background-color 0.5s linear;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
will-change: opacity;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Dimmer Content */
|
||||
|
||||
.ui.dimmer > .content {
|
||||
-webkit-user-select: text;
|
||||
-moz-user-select: text;
|
||||
user-select: text;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
|
||||
/* Loose Coupling */
|
||||
|
||||
.ui.segment > .ui.dimmer:not(.page) {
|
||||
border-radius: inherit;
|
||||
}
|
||||
|
||||
/* Scrollbars */
|
||||
|
||||
/*******************************
|
||||
States
|
||||
*******************************/
|
||||
|
||||
/* Animating */
|
||||
|
||||
.animating.dimmable:not(body),
|
||||
.dimmed.dimmable:not(body) {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Animating / Active / Visible */
|
||||
|
||||
.dimmed.dimmable > .ui.animating.dimmer,
|
||||
.dimmed.dimmable > .ui.visible.dimmer,
|
||||
.ui.active.dimmer {
|
||||
display: flex;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Disabled */
|
||||
|
||||
.ui.disabled.dimmer {
|
||||
width: 0 !important;
|
||||
height: 0 !important;
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Variations
|
||||
*******************************/
|
||||
|
||||
/*--------------
|
||||
Legacy
|
||||
---------------*/
|
||||
|
||||
/* Animating / Active / Visible */
|
||||
|
||||
.dimmed.dimmable > .ui.animating.legacy.dimmer,
|
||||
.dimmed.dimmable > .ui.visible.legacy.dimmer,
|
||||
.ui.active.legacy.dimmer {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Alignment
|
||||
---------------*/
|
||||
|
||||
.ui[class*="top aligned"].dimmer {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.ui[class*="bottom aligned"].dimmer {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Page
|
||||
---------------*/
|
||||
|
||||
.ui.page.dimmer {
|
||||
position: fixed;
|
||||
transform-style: '';
|
||||
perspective: 2000px;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.ui.page.dimmer.modals {
|
||||
-moz-perspective: none;
|
||||
}
|
||||
|
||||
body.animating.in.dimmable,
|
||||
body.dimmed.dimmable {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body.dimmable > .dimmer {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Blurring
|
||||
---------------*/
|
||||
|
||||
.blurring.dimmable > :not(.dimmer) {
|
||||
filter: initial;
|
||||
transition: 800ms filter ease;
|
||||
}
|
||||
|
||||
.blurring.dimmed.dimmable > :not(.dimmer):not(.popup) {
|
||||
filter: blur(5px) grayscale(0.7);
|
||||
}
|
||||
|
||||
/* Dimmer Color */
|
||||
|
||||
.blurring.dimmable > .dimmer {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
|
||||
.blurring.dimmable > .inverted.dimmer {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Aligned
|
||||
---------------*/
|
||||
|
||||
.ui.dimmer > .top.aligned.content > * {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.ui.dimmer > .bottom.aligned.content > * {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Shades
|
||||
---------------*/
|
||||
|
||||
.medium.medium.medium.medium.medium.dimmer {
|
||||
background: rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
|
||||
.light.light.light.light.light.dimmer {
|
||||
background: rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
|
||||
.very.light.light.light.light.dimmer {
|
||||
background: rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Simple
|
||||
---------------*/
|
||||
|
||||
/* Displays without javascript */
|
||||
|
||||
.ui.simple.dimmer {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: -100;
|
||||
background: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
.dimmed.dimmable > .ui.simple.dimmer {
|
||||
overflow: visible;
|
||||
opacity: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.ui.simple.inverted.dimmer {
|
||||
background: rgba(255, 255, 255, 0);
|
||||
}
|
||||
|
||||
.dimmed.dimmable > .ui.simple.inverted.dimmer {
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
}
|
||||
|
||||
/*--------------
|
||||
Partially
|
||||
----------------*/
|
||||
|
||||
.ui[class*="top dimmer"],
|
||||
.ui[class*="center dimmer"],
|
||||
.ui[class*="bottom dimmer"] {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.ui[class*="bottom dimmer"] {
|
||||
top: auto !important;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.ui[class*="center dimmer"] {
|
||||
top: 50% !important;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(calc(-50% - 0.5px));
|
||||
}
|
||||
|
||||
.ui.segment > .ui.ui[class*="top dimmer"] {
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.ui.segment > .ui.ui[class*="center dimmer"] {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.ui.segment > .ui.ui[class*="bottom dimmer"] {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.ui[class*="center dimmer"].transition[class*="fade up"].in {
|
||||
animation-name: fadeInUpCenter;
|
||||
}
|
||||
|
||||
.ui[class*="center dimmer"].transition[class*="fade down"].in {
|
||||
animation-name: fadeInDownCenter;
|
||||
}
|
||||
|
||||
.ui[class*="center dimmer"].transition[class*="fade up"].out {
|
||||
animation-name: fadeOutUpCenter;
|
||||
}
|
||||
|
||||
.ui[class*="center dimmer"].transition[class*="fade down"].out {
|
||||
animation-name: fadeOutDownCenter;
|
||||
}
|
||||
|
||||
.ui[class*="center dimmer"].bounce.transition {
|
||||
animation-name: bounceCenter;
|
||||
}
|
||||
|
||||
@keyframes fadeInUpCenter {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-40%);
|
||||
-webkit-transform: translateY(calc(-40% - 0.5px));
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(calc(-50% - 0.5px));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeInDownCenter {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: translateY(-60%);
|
||||
-webkit-transform: translateY(calc(-60% - 0.5px));
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(calc(-50% - 0.5px));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutUpCenter {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(calc(-50% - 0.5px));
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-45%);
|
||||
-webkit-transform: translateY(calc(-45% - 0.5px));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeOutDownCenter {
|
||||
0% {
|
||||
opacity: 1;
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(calc(-50% - 0.5px));
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 0;
|
||||
transform: translateY(-55%);
|
||||
-webkit-transform: translateY(calc(-55% - 0.5px));
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bounceCenter {
|
||||
0%, 20%, 50%, 80%, 100% {
|
||||
transform: translateY(-50%);
|
||||
-webkit-transform: translateY(calc(-50% - 0.5px));
|
||||
}
|
||||
|
||||
40% {
|
||||
transform: translateY(calc(-50% - 30px));
|
||||
}
|
||||
|
||||
60% {
|
||||
transform: translateY(calc(-50% - 15px));
|
||||
}
|
||||
}
|
||||
|
||||
/*******************************
|
||||
Theme Overrides
|
||||
*******************************/
|
||||
|
||||
/*******************************
|
||||
User Overrides
|
||||
*******************************/
|
||||
/*!
|
||||
* # Fomantic-UI - Dropdown
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
|
|
|
@ -1184,760 +1184,6 @@ $.api.settings = {
|
|||
|
||||
|
||||
|
||||
})( jQuery, window, document );
|
||||
|
||||
/*!
|
||||
* # Fomantic-UI - Dimmer
|
||||
* http://github.com/fomantic/Fomantic-UI/
|
||||
*
|
||||
*
|
||||
* Released under the MIT license
|
||||
* http://opensource.org/licenses/MIT
|
||||
*
|
||||
*/
|
||||
|
||||
;(function ($, window, document, undefined) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.isFunction = $.isFunction || function(obj) {
|
||||
return typeof obj === "function" && typeof obj.nodeType !== "number";
|
||||
};
|
||||
|
||||
window = (typeof window != 'undefined' && window.Math == Math)
|
||||
? window
|
||||
: (typeof self != 'undefined' && self.Math == Math)
|
||||
? self
|
||||
: Function('return this')()
|
||||
;
|
||||
|
||||
$.fn.dimmer = function(parameters) {
|
||||
var
|
||||
$allModules = $(this),
|
||||
|
||||
time = new Date().getTime(),
|
||||
performance = [],
|
||||
|
||||
query = arguments[0],
|
||||
methodInvoked = (typeof query == 'string'),
|
||||
queryArguments = [].slice.call(arguments, 1),
|
||||
|
||||
returnedValue
|
||||
;
|
||||
|
||||
$allModules
|
||||
.each(function() {
|
||||
var
|
||||
settings = ( $.isPlainObject(parameters) )
|
||||
? $.extend(true, {}, $.fn.dimmer.settings, parameters)
|
||||
: $.extend({}, $.fn.dimmer.settings),
|
||||
|
||||
selector = settings.selector,
|
||||
namespace = settings.namespace,
|
||||
className = settings.className,
|
||||
error = settings.error,
|
||||
|
||||
eventNamespace = '.' + namespace,
|
||||
moduleNamespace = 'module-' + namespace,
|
||||
moduleSelector = $allModules.selector || '',
|
||||
|
||||
clickEvent = "click", unstableClickEvent = ('ontouchstart' in document.documentElement)
|
||||
? 'touchstart'
|
||||
: 'click',
|
||||
|
||||
$module = $(this),
|
||||
$dimmer,
|
||||
$dimmable,
|
||||
|
||||
element = this,
|
||||
instance = $module.data(moduleNamespace),
|
||||
module
|
||||
;
|
||||
|
||||
module = {
|
||||
|
||||
preinitialize: function() {
|
||||
if( module.is.dimmer() ) {
|
||||
|
||||
$dimmable = $module.parent();
|
||||
$dimmer = $module;
|
||||
}
|
||||
else {
|
||||
$dimmable = $module;
|
||||
if( module.has.dimmer() ) {
|
||||
if(settings.dimmerName) {
|
||||
$dimmer = $dimmable.find(selector.dimmer).filter('.' + settings.dimmerName);
|
||||
}
|
||||
else {
|
||||
$dimmer = $dimmable.find(selector.dimmer);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$dimmer = module.create();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
initialize: function() {
|
||||
module.debug('Initializing dimmer', settings);
|
||||
|
||||
module.bind.events();
|
||||
module.set.dimmable();
|
||||
module.instantiate();
|
||||
},
|
||||
|
||||
instantiate: function() {
|
||||
module.verbose('Storing instance of module', module);
|
||||
instance = module;
|
||||
$module
|
||||
.data(moduleNamespace, instance)
|
||||
;
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
module.verbose('Destroying previous module', $dimmer);
|
||||
module.unbind.events();
|
||||
module.remove.variation();
|
||||
$dimmable
|
||||
.off(eventNamespace)
|
||||
;
|
||||
},
|
||||
|
||||
bind: {
|
||||
events: function() {
|
||||
if(settings.on == 'hover') {
|
||||
$dimmable
|
||||
.on('mouseenter' + eventNamespace, module.show)
|
||||
.on('mouseleave' + eventNamespace, module.hide)
|
||||
;
|
||||
}
|
||||
else if(settings.on == 'click') {
|
||||
$dimmable
|
||||
.on(clickEvent + eventNamespace, module.toggle)
|
||||
;
|
||||
}
|
||||
if( module.is.page() ) {
|
||||
module.debug('Setting as a page dimmer', $dimmable);
|
||||
module.set.pageDimmer();
|
||||
}
|
||||
|
||||
if( module.is.closable() ) {
|
||||
module.verbose('Adding dimmer close event', $dimmer);
|
||||
$dimmable
|
||||
.on(clickEvent + eventNamespace, selector.dimmer, module.event.click)
|
||||
;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
unbind: {
|
||||
events: function() {
|
||||
$module
|
||||
.removeData(moduleNamespace)
|
||||
;
|
||||
$dimmable
|
||||
.off(eventNamespace)
|
||||
;
|
||||
}
|
||||
},
|
||||
|
||||
event: {
|
||||
click: function(event) {
|
||||
module.verbose('Determining if event occured on dimmer', event);
|
||||
if( $dimmer.find(event.target).length === 0 || $(event.target).is(selector.content) ) {
|
||||
module.hide();
|
||||
event.stopImmediatePropagation();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
addContent: function(element) {
|
||||
var
|
||||
$content = $(element)
|
||||
;
|
||||
module.debug('Add content to dimmer', $content);
|
||||
if($content.parent()[0] !== $dimmer[0]) {
|
||||
$content.detach().appendTo($dimmer);
|
||||
}
|
||||
},
|
||||
|
||||
create: function() {
|
||||
var
|
||||
$element = $( settings.template.dimmer(settings) )
|
||||
;
|
||||
if(settings.dimmerName) {
|
||||
module.debug('Creating named dimmer', settings.dimmerName);
|
||||
$element.addClass(settings.dimmerName);
|
||||
}
|
||||
$element
|
||||
.appendTo($dimmable)
|
||||
;
|
||||
return $element;
|
||||
},
|
||||
|
||||
show: function(callback) {
|
||||
callback = $.isFunction(callback)
|
||||
? callback
|
||||
: function(){}
|
||||
;
|
||||
module.debug('Showing dimmer', $dimmer, settings);
|
||||
module.set.variation();
|
||||
if( (!module.is.dimmed() || module.is.animating()) && module.is.enabled() ) {
|
||||
module.animate.show(callback);
|
||||
settings.onShow.call(element);
|
||||
settings.onChange.call(element);
|
||||
}
|
||||
else {
|
||||
module.debug('Dimmer is already shown or disabled');
|
||||
}
|
||||
},
|
||||
|
||||
hide: function(callback) {
|
||||
callback = $.isFunction(callback)
|
||||
? callback
|
||||
: function(){}
|
||||
;
|
||||
if( module.is.dimmed() || module.is.animating() ) {
|
||||
module.debug('Hiding dimmer', $dimmer);
|
||||
module.animate.hide(callback);
|
||||
settings.onHide.call(element);
|
||||
settings.onChange.call(element);
|
||||
}
|
||||
else {
|
||||
module.debug('Dimmer is not visible');
|
||||
}
|
||||
},
|
||||
|
||||
toggle: function() {
|
||||
module.verbose('Toggling dimmer visibility', $dimmer);
|
||||
if( !module.is.dimmed() ) {
|
||||
module.show();
|
||||
}
|
||||
else {
|
||||
if ( module.is.closable() ) {
|
||||
module.hide();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
animate: {
|
||||
show: function(callback) {
|
||||
callback = $.isFunction(callback)
|
||||
? callback
|
||||
: function(){}
|
||||
;
|
||||
if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
|
||||
if(settings.useFlex) {
|
||||
module.debug('Using flex dimmer');
|
||||
module.remove.legacy();
|
||||
}
|
||||
else {
|
||||
module.debug('Using legacy non-flex dimmer');
|
||||
module.set.legacy();
|
||||
}
|
||||
if(settings.opacity !== 'auto') {
|
||||
module.set.opacity();
|
||||
}
|
||||
$dimmer
|
||||
.transition({
|
||||
displayType : settings.useFlex
|
||||
? 'flex'
|
||||
: 'block',
|
||||
animation : settings.transition + ' in',
|
||||
queue : false,
|
||||
duration : module.get.duration(),
|
||||
useFailSafe : true,
|
||||
onStart : function() {
|
||||
module.set.dimmed();
|
||||
},
|
||||
onComplete : function() {
|
||||
module.set.active();
|
||||
callback();
|
||||
}
|
||||
})
|
||||
;
|
||||
}
|
||||
else {
|
||||
module.verbose('Showing dimmer animation with javascript');
|
||||
module.set.dimmed();
|
||||
if(settings.opacity == 'auto') {
|
||||
settings.opacity = 0.8;
|
||||
}
|
||||
$dimmer
|
||||
.stop()
|
||||
.css({
|
||||
opacity : 0,
|
||||
width : '100%',
|
||||
height : '100%'
|
||||
})
|
||||
.fadeTo(module.get.duration(), settings.opacity, function() {
|
||||
$dimmer.removeAttr('style');
|
||||
module.set.active();
|
||||
callback();
|
||||
})
|
||||
;
|
||||
}
|
||||
},
|
||||
hide: function(callback) {
|
||||
callback = $.isFunction(callback)
|
||||
? callback
|
||||
: function(){}
|
||||
;
|
||||
if(settings.useCSS && $.fn.transition !== undefined && $dimmer.transition('is supported')) {
|
||||
module.verbose('Hiding dimmer with css');
|
||||
$dimmer
|
||||
.transition({
|
||||
displayType : settings.useFlex
|
||||
? 'flex'
|
||||
: 'block',
|
||||
animation : settings.transition + ' out',
|
||||
queue : false,
|
||||
duration : module.get.duration(),
|
||||
useFailSafe : true,
|
||||
onComplete : function() {
|
||||
module.remove.dimmed();
|
||||
module.remove.variation();
|
||||
module.remove.active();
|
||||
callback();
|
||||
}
|
||||
})
|
||||
;
|
||||
}
|
||||
else {
|
||||
module.verbose('Hiding dimmer with javascript');
|
||||
$dimmer
|
||||
.stop()
|
||||
.fadeOut(module.get.duration(), function() {
|
||||
module.remove.dimmed();
|
||||
module.remove.active();
|
||||
$dimmer.removeAttr('style');
|
||||
callback();
|
||||
})
|
||||
;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
get: {
|
||||
dimmer: function() {
|
||||
return $dimmer;
|
||||
},
|
||||
duration: function() {
|
||||
if(typeof settings.duration == 'object') {
|
||||
if( module.is.active() ) {
|
||||
return settings.duration.hide;
|
||||
}
|
||||
else {
|
||||
return settings.duration.show;
|
||||
}
|
||||
}
|
||||
return settings.duration;
|
||||
}
|
||||
},
|
||||
|
||||
has: {
|
||||
dimmer: function() {
|
||||
if(settings.dimmerName) {
|
||||
return ($module.find(selector.dimmer).filter('.' + settings.dimmerName).length > 0);
|
||||
}
|
||||
else {
|
||||
return ( $module.find(selector.dimmer).length > 0 );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
is: {
|
||||
active: function() {
|
||||
return $dimmer.hasClass(className.active);
|
||||
},
|
||||
animating: function() {
|
||||
return ( $dimmer.is(':animated') || $dimmer.hasClass(className.animating) );
|
||||
},
|
||||
closable: function() {
|
||||
if(settings.closable == 'auto') {
|
||||
if(settings.on == 'hover') {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return settings.closable;
|
||||
},
|
||||
dimmer: function() {
|
||||
return $module.hasClass(className.dimmer);
|
||||
},
|
||||
dimmable: function() {
|
||||
return $module.hasClass(className.dimmable);
|
||||
},
|
||||
dimmed: function() {
|
||||
return $dimmable.hasClass(className.dimmed);
|
||||
},
|
||||
disabled: function() {
|
||||
return $dimmable.hasClass(className.disabled);
|
||||
},
|
||||
enabled: function() {
|
||||
return !module.is.disabled();
|
||||
},
|
||||
page: function () {
|
||||
return $dimmable.is('body');
|
||||
},
|
||||
pageDimmer: function() {
|
||||
return $dimmer.hasClass(className.pageDimmer);
|
||||
}
|
||||
},
|
||||
|
||||
can: {
|
||||
show: function() {
|
||||
return !$dimmer.hasClass(className.disabled);
|
||||
}
|
||||
},
|
||||
|
||||
set: {
|
||||
opacity: function(opacity) {
|
||||
var
|
||||
color = $dimmer.css('background-color'),
|
||||
colorArray = color.split(','),
|
||||
isRGB = (colorArray && colorArray.length >= 3)
|
||||
;
|
||||
opacity = settings.opacity === 0 ? 0 : settings.opacity || opacity;
|
||||
if(isRGB) {
|
||||
colorArray[2] = colorArray[2].replace(')','');
|
||||
colorArray[3] = opacity + ')';
|
||||
color = colorArray.join(',');
|
||||
}
|
||||
else {
|
||||
color = 'rgba(0, 0, 0, ' + opacity + ')';
|
||||
}
|
||||
module.debug('Setting opacity to', opacity);
|
||||
$dimmer.css('background-color', color);
|
||||
},
|
||||
legacy: function() {
|
||||
$dimmer.addClass(className.legacy);
|
||||
},
|
||||
active: function() {
|
||||
$dimmer.addClass(className.active);
|
||||
},
|
||||
dimmable: function() {
|
||||
$dimmable.addClass(className.dimmable);
|
||||
},
|
||||
dimmed: function() {
|
||||
$dimmable.addClass(className.dimmed);
|
||||
},
|
||||
pageDimmer: function() {
|
||||
$dimmer.addClass(className.pageDimmer);
|
||||
},
|
||||
disabled: function() {
|
||||
$dimmer.addClass(className.disabled);
|
||||
},
|
||||
variation: function(variation) {
|
||||
variation = variation || settings.variation;
|
||||
if(variation) {
|
||||
$dimmer.addClass(variation);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
remove: {
|
||||
active: function() {
|
||||
$dimmer
|
||||
.removeClass(className.active)
|
||||
;
|
||||
},
|
||||
legacy: function() {
|
||||
$dimmer.removeClass(className.legacy);
|
||||
},
|
||||
dimmed: function() {
|
||||
$dimmable.removeClass(className.dimmed);
|
||||
},
|
||||
disabled: function() {
|
||||
$dimmer.removeClass(className.disabled);
|
||||
},
|
||||
variation: function(variation) {
|
||||
variation = variation || settings.variation;
|
||||
if(variation) {
|
||||
$dimmer.removeClass(variation);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setting: function(name, value) {
|
||||
module.debug('Changing setting', name, value);
|
||||
if( $.isPlainObject(name) ) {
|
||||
$.extend(true, settings, name);
|
||||
}
|
||||
else if(value !== undefined) {
|
||||
if($.isPlainObject(settings[name])) {
|
||||
$.extend(true, settings[name], value);
|
||||
}
|
||||
else {
|
||||
settings[name] = value;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return settings[name];
|
||||
}
|
||||
},
|
||||
internal: function(name, value) {
|
||||
if( $.isPlainObject(name) ) {
|
||||
$.extend(true, module, name);
|
||||
}
|
||||
else if(value !== undefined) {
|
||||
module[name] = value;
|
||||
}
|
||||
else {
|
||||
return module[name];
|
||||
}
|
||||
},
|
||||
debug: function() {
|
||||
if(!settings.silent && settings.debug) {
|
||||
if(settings.performance) {
|
||||
module.performance.log(arguments);
|
||||
}
|
||||
else {
|
||||
module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
||||
module.debug.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
verbose: function() {
|
||||
if(!settings.silent && settings.verbose && settings.debug) {
|
||||
if(settings.performance) {
|
||||
module.performance.log(arguments);
|
||||
}
|
||||
else {
|
||||
module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
|
||||
module.verbose.apply(console, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
if(!settings.silent) {
|
||||
module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
|
||||
module.error.apply(console, arguments);
|
||||
}
|
||||
},
|
||||
performance: {
|
||||
log: function(message) {
|
||||
var
|
||||
currentTime,
|
||||
executionTime,
|
||||
previousTime
|
||||
;
|
||||
if(settings.performance) {
|
||||
currentTime = new Date().getTime();
|
||||
previousTime = time || currentTime;
|
||||
executionTime = currentTime - previousTime;
|
||||
time = currentTime;
|
||||
performance.push({
|
||||
'Name' : message[0],
|
||||
'Arguments' : [].slice.call(message, 1) || '',
|
||||
'Element' : element,
|
||||
'Execution Time' : executionTime
|
||||
});
|
||||
}
|
||||
clearTimeout(module.performance.timer);
|
||||
module.performance.timer = setTimeout(module.performance.display, 500);
|
||||
},
|
||||
display: function() {
|
||||
var
|
||||
title = settings.name + ':',
|
||||
totalTime = 0
|
||||
;
|
||||
time = false;
|
||||
clearTimeout(module.performance.timer);
|
||||
$.each(performance, function(index, data) {
|
||||
totalTime += data['Execution Time'];
|
||||
});
|
||||
title += ' ' + totalTime + 'ms';
|
||||
if(moduleSelector) {
|
||||
title += ' \'' + moduleSelector + '\'';
|
||||
}
|
||||
if($allModules.length > 1) {
|
||||
title += ' ' + '(' + $allModules.length + ')';
|
||||
}
|
||||
if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
|
||||
console.groupCollapsed(title);
|
||||
if(console.table) {
|
||||
console.table(performance);
|
||||
}
|
||||
else {
|
||||
$.each(performance, function(index, data) {
|
||||
console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
|
||||
});
|
||||
}
|
||||
console.groupEnd();
|
||||
}
|
||||
performance = [];
|
||||
}
|
||||
},
|
||||
invoke: function(query, passedArguments, context) {
|
||||
var
|
||||
object = instance,
|
||||
maxDepth,
|
||||
found,
|
||||
response
|
||||
;
|
||||
passedArguments = passedArguments || queryArguments;
|
||||
context = element || context;
|
||||
if(typeof query == 'string' && object !== undefined) {
|
||||
query = query.split(/[\. ]/);
|
||||
maxDepth = query.length - 1;
|
||||
$.each(query, function(depth, value) {
|
||||
var camelCaseValue = (depth != maxDepth)
|
||||
? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
|
||||
: query
|
||||
;
|
||||
if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
|
||||
object = object[camelCaseValue];
|
||||
}
|
||||
else if( object[camelCaseValue] !== undefined ) {
|
||||
found = object[camelCaseValue];
|
||||
return false;
|
||||
}
|
||||
else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
|
||||
object = object[value];
|
||||
}
|
||||
else if( object[value] !== undefined ) {
|
||||
found = object[value];
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
module.error(error.method, query);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
if ( $.isFunction( found ) ) {
|
||||
response = found.apply(context, passedArguments);
|
||||
}
|
||||
else if(found !== undefined) {
|
||||
response = found;
|
||||
}
|
||||
if(Array.isArray(returnedValue)) {
|
||||
returnedValue.push(response);
|
||||
}
|
||||
else if(returnedValue !== undefined) {
|
||||
returnedValue = [returnedValue, response];
|
||||
}
|
||||
else if(response !== undefined) {
|
||||
returnedValue = response;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
};
|
||||
|
||||
module.preinitialize();
|
||||
|
||||
if(methodInvoked) {
|
||||
if(instance === undefined) {
|
||||
module.initialize();
|
||||
}
|
||||
module.invoke(query);
|
||||
}
|
||||
else {
|
||||
if(instance !== undefined) {
|
||||
instance.invoke('destroy');
|
||||
}
|
||||
module.initialize();
|
||||
}
|
||||
})
|
||||
;
|
||||
|
||||
return (returnedValue !== undefined)
|
||||
? returnedValue
|
||||
: this
|
||||
;
|
||||
};
|
||||
|
||||
$.fn.dimmer.settings = {
|
||||
|
||||
name : 'Dimmer',
|
||||
namespace : 'dimmer',
|
||||
|
||||
silent : false,
|
||||
debug : false,
|
||||
verbose : false,
|
||||
performance : true,
|
||||
|
||||
// whether should use flex layout
|
||||
useFlex : true,
|
||||
|
||||
// name to distinguish between multiple dimmers in context
|
||||
dimmerName : false,
|
||||
|
||||
// whether to add a variation type
|
||||
variation : false,
|
||||
|
||||
// whether to bind close events
|
||||
closable : 'auto',
|
||||
|
||||
// whether to use css animations
|
||||
useCSS : true,
|
||||
|
||||
// css animation to use
|
||||
transition : 'fade',
|
||||
|
||||
// event to bind to
|
||||
on : false,
|
||||
|
||||
// overriding opacity value
|
||||
opacity : 'auto',
|
||||
|
||||
// transition durations
|
||||
duration : {
|
||||
show : 500,
|
||||
hide : 500
|
||||
},
|
||||
// whether the dynamically created dimmer should have a loader
|
||||
displayLoader: false,
|
||||
loaderText : false,
|
||||
loaderVariation : '',
|
||||
|
||||
onChange : function(){},
|
||||
onShow : function(){},
|
||||
onHide : function(){},
|
||||
|
||||
error : {
|
||||
method : 'The method you called is not defined.'
|
||||
},
|
||||
|
||||
className : {
|
||||
active : 'active',
|
||||
animating : 'animating',
|
||||
dimmable : 'dimmable',
|
||||
dimmed : 'dimmed',
|
||||
dimmer : 'dimmer',
|
||||
disabled : 'disabled',
|
||||
hide : 'hide',
|
||||
legacy : 'legacy',
|
||||
pageDimmer : 'page',
|
||||
show : 'show',
|
||||
loader : 'ui loader'
|
||||
},
|
||||
|
||||
selector: {
|
||||
dimmer : '> .ui.dimmer',
|
||||
content : '.ui.dimmer > .content, .ui.dimmer > .content > .center'
|
||||
},
|
||||
|
||||
template: {
|
||||
dimmer: function(settings) {
|
||||
var d = $('<div/>').addClass('ui dimmer'),l;
|
||||
if(settings.displayLoader) {
|
||||
l = $('<div/>')
|
||||
.addClass(settings.className.loader)
|
||||
.addClass(settings.loaderVariation);
|
||||
if(!!settings.loaderText){
|
||||
l.text(settings.loaderText);
|
||||
l.addClass('text');
|
||||
}
|
||||
d.append(l);
|
||||
}
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
})( jQuery, window, document );
|
||||
|
||||
/*!
|
||||
|
|
|
@ -22,7 +22,6 @@
|
|||
"admin": false,
|
||||
"components": [
|
||||
"api",
|
||||
"dimmer",
|
||||
"dropdown",
|
||||
"form",
|
||||
"modal",
|
||||
|
|
|
@ -6,18 +6,10 @@
|
|||
// This file must be imported before any lazy-loading is being attempted.
|
||||
__webpack_public_path__ = `${window.config?.assetUrlPrefix ?? '/assets'}/`;
|
||||
|
||||
const filteredErrors = new Set([
|
||||
'getModifierState is not a function', // https://github.com/microsoft/monaco-editor/issues/4325
|
||||
]);
|
||||
|
||||
export function showGlobalErrorMessage(msg) {
|
||||
const pageContent = document.querySelector('.page-content');
|
||||
if (!pageContent) return;
|
||||
|
||||
for (const filteredError of filteredErrors) {
|
||||
if (msg.includes(filteredError)) return;
|
||||
}
|
||||
|
||||
// compact the message to a data attribute to avoid too many duplicated messages
|
||||
const msgCompact = msg.replace(/\W/g, '').trim();
|
||||
let msgDiv = pageContent.querySelector(`.js-global-error[data-global-error-msg-compact="${msgCompact}"]`);
|
||||
|
@ -50,7 +42,7 @@ function processWindowErrorEvent({error, reason, message, type, filename, lineno
|
|||
const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin));
|
||||
const {runModeIsProd} = window.config ?? {};
|
||||
|
||||
// `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likly a
|
||||
// `error` and `reason` are not guaranteed to be errors. If the value is falsy, it is likely a
|
||||
// non-critical event from the browser. We log them but don't show them to users. Examples:
|
||||
// - https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver#observation_errors
|
||||
// - https://github.com/mozilla-mobile/firefox-ios/issues/10817
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
export function handleGlobalEnterQuickSubmit(target) {
|
||||
const form = target.closest('form');
|
||||
let form = target.closest('form');
|
||||
if (form) {
|
||||
if (!form.checkValidity()) {
|
||||
form.reportValidity();
|
||||
|
@ -9,5 +9,10 @@ export function handleGlobalEnterQuickSubmit(target) {
|
|||
// here use the event to trigger the submit event (instead of calling `submit()` method directly)
|
||||
// otherwise the `areYouSure` handler won't be executed, then there will be an annoying "confirm to leave" dialog
|
||||
form.dispatchEvent(new SubmitEvent('submit', {bubbles: true, cancelable: true}));
|
||||
return;
|
||||
}
|
||||
form = target.closest('.ui.form');
|
||||
if (form) {
|
||||
form.querySelector('.ui.primary.button')?.click();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,8 +162,8 @@ async function onEditContent(event) {
|
|||
editContentZone.innerHTML = document.getElementById('issue-comment-editor-template').innerHTML;
|
||||
comboMarkdownEditor = await initComboMarkdownEditor(editContentZone.querySelector('.combo-markdown-editor'));
|
||||
comboMarkdownEditor.attachedDropzoneInst = await setupDropzone(editContentZone.querySelector('.dropzone'));
|
||||
editContentZone.querySelector('.cancel.button').addEventListener('click', cancelAndReset);
|
||||
editContentZone.querySelector('.save.button').addEventListener('click', saveAndRefresh);
|
||||
editContentZone.querySelector('.ui.cancel.button').addEventListener('click', cancelAndReset);
|
||||
editContentZone.querySelector('.ui.primary.button').addEventListener('click', saveAndRefresh);
|
||||
}
|
||||
|
||||
// Show write/preview tab and copy raw content as needed
|
||||
|
|
|
@ -5,6 +5,7 @@ import {initAriaFormFieldPatch} from './fomantic/form.js';
|
|||
import {initAriaDropdownPatch} from './fomantic/dropdown.js';
|
||||
import {initAriaModalPatch} from './fomantic/modal.js';
|
||||
import {initFomanticTransition} from './fomantic/transition.js';
|
||||
import {initFomanticDimmer} from './fomantic/dimmer.js';
|
||||
import {svg} from '../svg.js';
|
||||
|
||||
export const fomanticMobileScreen = window.matchMedia('only screen and (max-width: 767.98px)');
|
||||
|
@ -24,6 +25,7 @@ export function initGiteaFomantic() {
|
|||
};
|
||||
|
||||
initFomanticTransition();
|
||||
initFomanticDimmer();
|
||||
initFomanticApiPatch();
|
||||
|
||||
// Use the patches to improve accessibility, these patches are designed to be as independent as possible, make it easy to modify or remove in the future.
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import $ from 'jquery';
|
||||
import {queryElemChildren} from '../../utils/dom.js';
|
||||
|
||||
export function initFomanticDimmer() {
|
||||
// stand-in for removed dimmer module
|
||||
$.fn.dimmer = function (arg0, $el) {
|
||||
if (arg0 === 'add content') {
|
||||
const existingDimmer = document.querySelector('body > .ui.dimmer');
|
||||
if (existingDimmer) {
|
||||
queryElemChildren(existingDimmer, '*', (el) => el.remove());
|
||||
this._dimmer = existingDimmer;
|
||||
} else {
|
||||
this._dimmer = document.createElement('div');
|
||||
this._dimmer.classList.add('ui', 'dimmer');
|
||||
document.body.append(this._dimmer);
|
||||
}
|
||||
this._dimmer.append($el[0]);
|
||||
} else if (arg0 === 'get dimmer') {
|
||||
return $(this._dimmer);
|
||||
} else if (arg0 === 'show') {
|
||||
this._dimmer.classList.add('active');
|
||||
document.body.classList.add('tw-overflow-hidden');
|
||||
} else if (arg0 === 'hide') {
|
||||
this._dimmer.classList.remove('active');
|
||||
document.body.classList.remove('tw-overflow-hidden');
|
||||
}
|
||||
return this;
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue