Merge pull request '[gitea] week 14 cherry-pick' (#2872) from earl-warren/forgejo:wip-gitea-cherry-pick into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2872 Reviewed-by: Gusted <gusted@noreply.codeberg.org>
This commit is contained in:
commit
91b745a5b6
324 changed files with 3486 additions and 5440 deletions
|
@ -14,7 +14,7 @@ _test
|
|||
|
||||
# MS VSCode
|
||||
.vscode
|
||||
__debug_bin
|
||||
__debug_bin*
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
|
|
|
@ -167,7 +167,7 @@ rules:
|
|||
"@stylistic/js/semi-spacing": [2, {before: false, after: true}]
|
||||
"@stylistic/js/semi-style": [2, last]
|
||||
"@stylistic/js/space-before-blocks": [2, always]
|
||||
"@stylistic/js/space-before-function-paren": [0]
|
||||
"@stylistic/js/space-before-function-paren": [2, {anonymous: ignore, named: never, asyncArrow: always}]
|
||||
"@stylistic/js/space-in-parens": [2, never]
|
||||
"@stylistic/js/space-infix-ops": [2]
|
||||
"@stylistic/js/space-unary-ops": [2]
|
||||
|
@ -281,7 +281,7 @@ rules:
|
|||
jquery/no-ajax-events: [2]
|
||||
jquery/no-ajax: [2]
|
||||
jquery/no-animate: [2]
|
||||
jquery/no-attr: [0]
|
||||
jquery/no-attr: [2]
|
||||
jquery/no-bind: [2]
|
||||
jquery/no-class: [0]
|
||||
jquery/no-clone: [2]
|
||||
|
@ -303,7 +303,7 @@ rules:
|
|||
jquery/no-in-array: [2]
|
||||
jquery/no-is-array: [2]
|
||||
jquery/no-is-function: [2]
|
||||
jquery/no-is: [0]
|
||||
jquery/no-is: [2]
|
||||
jquery/no-load: [2]
|
||||
jquery/no-map: [2]
|
||||
jquery/no-merge: [2]
|
||||
|
@ -397,7 +397,7 @@ rules:
|
|||
no-jquery/no-animate-toggle: [2]
|
||||
no-jquery/no-animate: [2]
|
||||
no-jquery/no-append-html: [2]
|
||||
no-jquery/no-attr: [0]
|
||||
no-jquery/no-attr: [2]
|
||||
no-jquery/no-bind: [2]
|
||||
no-jquery/no-box-model: [2]
|
||||
no-jquery/no-browser: [2]
|
||||
|
@ -440,7 +440,7 @@ rules:
|
|||
no-jquery/no-is-numeric: [2]
|
||||
no-jquery/no-is-plain-object: [2]
|
||||
no-jquery/no-is-window: [2]
|
||||
no-jquery/no-is: [0]
|
||||
no-jquery/no-is: [2]
|
||||
no-jquery/no-jquery-constructor: [0]
|
||||
no-jquery/no-live: [2]
|
||||
no-jquery/no-load-shorthand: [2]
|
||||
|
|
5
Makefile
5
Makefile
|
@ -458,7 +458,8 @@ lint-actions:
|
|||
$(GO) run $(ACTIONLINT_PACKAGE)
|
||||
|
||||
.PHONY: lint-templates
|
||||
lint-templates: .venv
|
||||
lint-templates: .venv node_modules
|
||||
@node tools/lint-templates-svg.js
|
||||
@poetry run djlint $(shell find templates -type f -iname '*.tmpl')
|
||||
|
||||
.PHONY: lint-yaml
|
||||
|
@ -999,7 +1000,7 @@ generate-gitignore:
|
|||
|
||||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
npm install --no-save fabric@6.0.0-beta19 imagemin-zopfli@7
|
||||
npm install --no-save fabric@6.0.0-beta20 imagemin-zopfli@7
|
||||
node tools/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
|
|
|
@ -441,7 +441,7 @@ INTERNAL_TOKEN =
|
|||
;INTERNAL_TOKEN_URI = file:/etc/gitea/internal_token
|
||||
;;
|
||||
;; How long to remember that a user is logged in before requiring relogin (in days)
|
||||
;LOGIN_REMEMBER_DAYS = 7
|
||||
;LOGIN_REMEMBER_DAYS = 31
|
||||
;;
|
||||
;; Name of the cookie used to store the current username.
|
||||
;COOKIE_USERNAME = gitea_awesome
|
||||
|
|
|
@ -528,7 +528,7 @@ And the following unique queues:
|
|||
- `INSTALL_LOCK`: **false**: Controls access to the installation page. When set to "true", the installation page is not accessible.
|
||||
- `SECRET_KEY`: **\<random at every install\>**: Global secret key. This key is VERY IMPORTANT, if you lost it, the data encrypted by it (like 2FA secret) can't be decrypted anymore.
|
||||
- `SECRET_KEY_URI`: **_empty_**: Instead of defining SECRET_KEY, this option can be used to use the key stored in a file (example value: `file:/etc/gitea/secret_key`). It shouldn't be lost like SECRET_KEY.
|
||||
- `LOGIN_REMEMBER_DAYS`: **7**: Cookie lifetime, in days.
|
||||
- `LOGIN_REMEMBER_DAYS`: **31**: How long to remember that a user is logged in before requiring relogin (in days).
|
||||
- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**: Name of cookie used to store authentication
|
||||
information.
|
||||
- `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**: Header name for reverse proxy
|
||||
|
|
|
@ -507,7 +507,7 @@ Gitea 创建以下非唯一队列:
|
|||
- `INSTALL_LOCK`: **false**:控制是否能够访问安装向导页面,设置为 `true` 则禁止访问安装向导页面。
|
||||
- `SECRET_KEY`: **\<每次安装时随机生成\>**:全局服务器安全密钥。这个密钥非常重要,如果丢失将无法解密加密的数据(例如 2FA)。
|
||||
- `SECRET_KEY_URI`: **_empty_**:与定义 `SECRET_KEY` 不同,此选项可用于使用存储在文件中的密钥(示例值:`file:/etc/gitea/secret_key`)。它不应该像 `SECRET_KEY` 一样容易丢失。
|
||||
- `LOGIN_REMEMBER_DAYS`: **7**:Cookie 保存时间,单位为天。
|
||||
- `LOGIN_REMEMBER_DAYS`: **31**:在要求重新登录之前,记住用户的登录状态多长时间(以天为单位)。
|
||||
- `COOKIE_REMEMBER_NAME`: **gitea\_incredible**:保存自动登录信息的 Cookie 名称。
|
||||
- `REVERSE_PROXY_AUTHENTICATION_USER`: **X-WEBAUTH-USER**:反向代理认证的 HTTP 头部名称,用于提供用户信息。
|
||||
- `REVERSE_PROXY_AUTHENTICATION_EMAIL`: **X-WEBAUTH-EMAIL**:反向代理认证的 HTTP 头部名称,用于提供邮箱信息。
|
||||
|
|
|
@ -47,7 +47,7 @@ We recommend [Google HTML/CSS Style Guide](https://google.github.io/styleguide/h
|
|||
9. Avoid unnecessary `!important` in CSS, add comments to explain why it's necessary if it can't be avoided.
|
||||
10. Avoid mixing different events in one event listener, prefer to use individual event listeners for every event.
|
||||
11. Custom event names are recommended to use `ce-` prefix.
|
||||
12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-mono`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
|
||||
12. Prefer using Tailwind CSS which is available via `tw-` prefix, e.g. `tw-relative`. Gitea's helper CSS classes use `gt-` prefix (`gt-word-break`), while Gitea's own private framework-level CSS classes use `g-` prefix (`g-modal-confirm`).
|
||||
13. Avoid inline scripts & styles as much as possible, it's recommended to put JS code into JS files and use CSS classes. If inline scripts & styles are unavoidable, explain the reason why it can't be avoided.
|
||||
|
||||
### Accessibility / ARIA
|
||||
|
@ -118,7 +118,7 @@ However, there are still some special cases, so the current guideline is:
|
|||
### Show/Hide Elements
|
||||
|
||||
* Vue components are recommended to use `v-if` and `v-show` to show/hide elements.
|
||||
* Go template code should use Gitea's `.gt-hidden` and `showElem()/hideElem()/toggleElem()`, see more details in `.gt-hidden`'s comment.
|
||||
* Go template code should use `.tw-hidden` and `showElem()/hideElem()/toggleElem()`, see more details in `.tw-hidden`'s comment.
|
||||
|
||||
### Styles and Attributes in Go HTML Template
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ HTML 页面由[Go HTML Template](https://pkg.go.dev/html/template)渲染。
|
|||
9. 避免在 CSS 中使用不必要的`!important`,如果无法避免,添加注释解释为什么需要它。
|
||||
10. 避免在一个事件监听器中混合不同的事件,优先为每个事件使用独立的事件监听器。
|
||||
11. 推荐使用自定义事件名称前缀`ce-`。
|
||||
12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-mono`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
|
||||
12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-word-break`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
|
||||
13. 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。
|
||||
|
||||
### 可访问性 / ARIA
|
||||
|
@ -117,7 +117,7 @@ Gitea使用一些补丁使Fomantic UI更具可访问性(参见`aria.js`和`ari
|
|||
### 显示/隐藏元素
|
||||
|
||||
* 推荐在Vue组件中使用`v-if`和`v-show`来显示/隐藏元素。
|
||||
* Go 模板代码应使用 Gitea 的 `.gt-hidden` 和 `showElem()/hideElem()/toggleElem()` 来显示/隐藏元素,请参阅`.gt-hidden`的注释以获取更多详细信息。
|
||||
* Go 模板代码应使用 `.tw-hidden` 和 `showElem()/hideElem()/toggleElem()` 来显示/隐藏元素,请参阅`.tw-hidden`的注释以获取更多详细信息。
|
||||
|
||||
### Go HTML 模板中的样式和属性
|
||||
|
||||
|
|
|
@ -120,6 +120,16 @@ func (c *halfCommitter) Close() error {
|
|||
|
||||
// TxContext represents a transaction Context,
|
||||
// it will reuse the existing transaction in the parent context or create a new one.
|
||||
// Some tips to use:
|
||||
//
|
||||
// 1 It's always recommended to use `WithTx` in new code instead of `TxContext`, since `WithTx` will handle the transaction automatically.
|
||||
// 2. To maintain the old code which uses `TxContext`:
|
||||
// a. Always call `Close()` before returning regardless of whether `Commit()` has been called.
|
||||
// b. Always call `Commit()` before returning if there are no errors, even if the code did not change any data.
|
||||
// c. Remember the `Committer` will be a halfCommitter when a transaction is being reused.
|
||||
// So calling `Commit()` will do nothing, but calling `Close()` without calling `Commit()` will rollback the transaction.
|
||||
// And all operations submitted by the caller stack will be rollbacked as well, not only the operations in the current function.
|
||||
// d. It doesn't mean rollback is forbidden, but always do it only when there is an error, and you do want to rollback.
|
||||
func TxContext(parentCtx context.Context) (*Context, Committer, error) {
|
||||
if sess, ok := inTransaction(parentCtx); ok {
|
||||
return newContext(parentCtx, sess, true), &halfCommitter{committer: sess}, nil
|
||||
|
|
|
@ -45,3 +45,27 @@
|
|||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
|
||||
-
|
||||
id: 5
|
||||
title: project without default column
|
||||
owner_id: 2
|
||||
repo_id: 0
|
||||
is_closed: false
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
|
||||
-
|
||||
id: 6
|
||||
title: project with multiple default columns
|
||||
owner_id: 2
|
||||
repo_id: 0
|
||||
is_closed: false
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
project_id: 1
|
||||
title: To Do
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
|
@ -29,3 +30,48 @@
|
|||
creator_id: 2
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 5
|
||||
project_id: 2
|
||||
title: Backlog
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 6
|
||||
project_id: 4
|
||||
title: Backlog
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 7
|
||||
project_id: 5
|
||||
title: Done
|
||||
creator_id: 2
|
||||
default: false
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 8
|
||||
project_id: 6
|
||||
title: Backlog
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 9
|
||||
project_id: 6
|
||||
title: Uncategorized
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/translation"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CommitStatus holds a single Status of a single Commit
|
||||
|
@ -264,44 +265,48 @@ type CommitStatusIndex struct {
|
|||
|
||||
// GetLatestCommitStatus returns all statuses with a unique context for a given commit.
|
||||
func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOptions db.ListOptions) ([]*CommitStatus, int64, error) {
|
||||
ids := make([]int64, 0, 10)
|
||||
sess := db.GetEngine(ctx).Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repoID).And("sha = ?", sha).
|
||||
Select("max( id ) as id").
|
||||
GroupBy("context_hash").OrderBy("max( id ) desc")
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).
|
||||
Where("repo_id = ?", repoID).And("sha = ?", sha)
|
||||
}
|
||||
indices := make([]int64, 0, 10)
|
||||
sess := getBase().Select("max( `index` ) as `index`").
|
||||
GroupBy("context_hash").OrderBy("max( `index` ) desc")
|
||||
if !listOptions.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
}
|
||||
count, err := sess.FindAndCount(&ids)
|
||||
count, err := sess.FindAndCount(&indices)
|
||||
if err != nil {
|
||||
return nil, count, err
|
||||
}
|
||||
statuses := make([]*CommitStatus, 0, len(ids))
|
||||
if len(ids) == 0 {
|
||||
statuses := make([]*CommitStatus, 0, len(indices))
|
||||
if len(indices) == 0 {
|
||||
return statuses, count, nil
|
||||
}
|
||||
return statuses, count, db.GetEngine(ctx).In("id", ids).Find(&statuses)
|
||||
return statuses, count, getBase().And(builder.In("`index`", indices)).Find(&statuses)
|
||||
}
|
||||
|
||||
// GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs
|
||||
func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) {
|
||||
type result struct {
|
||||
ID int64
|
||||
Index int64
|
||||
RepoID int64
|
||||
}
|
||||
|
||||
results := make([]result, 0, len(repoIDsToLatestCommitSHAs))
|
||||
|
||||
sess := db.GetEngine(ctx).Table(&CommitStatus{})
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{})
|
||||
}
|
||||
|
||||
// Create a disjunction of conditions for each repoID and SHA pair
|
||||
conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs))
|
||||
for repoID, sha := range repoIDsToLatestCommitSHAs {
|
||||
conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha})
|
||||
}
|
||||
sess = sess.Where(builder.Or(conds...)).
|
||||
Select("max( id ) as id, repo_id").
|
||||
GroupBy("context_hash, repo_id").OrderBy("max( id ) desc")
|
||||
sess := getBase().Where(builder.Or(conds...)).
|
||||
Select("max( `index` ) as `index`, repo_id").
|
||||
GroupBy("context_hash, repo_id").OrderBy("max( `index` ) desc")
|
||||
|
||||
if !listOptions.IsListAll() {
|
||||
sess = db.SetSessionPagination(sess, &listOptions)
|
||||
|
@ -312,15 +317,21 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA
|
|||
return nil, err
|
||||
}
|
||||
|
||||
ids := make([]int64, 0, len(results))
|
||||
repoStatuses := make(map[int64][]*CommitStatus)
|
||||
for _, result := range results {
|
||||
ids = append(ids, result.ID)
|
||||
}
|
||||
|
||||
statuses := make([]*CommitStatus, 0, len(ids))
|
||||
if len(ids) > 0 {
|
||||
err = db.GetEngine(ctx).In("id", ids).Find(&statuses)
|
||||
if len(results) > 0 {
|
||||
statuses := make([]*CommitStatus, 0, len(results))
|
||||
|
||||
conds = make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
cond := builder.Eq{
|
||||
"`index`": result.Index,
|
||||
"repo_id": result.RepoID,
|
||||
"sha": repoIDsToLatestCommitSHAs[result.RepoID],
|
||||
}
|
||||
conds = append(conds, cond)
|
||||
}
|
||||
err = getBase().Where(builder.Or(conds...)).Find(&statuses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -337,42 +348,43 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA
|
|||
// GetLatestCommitStatusForRepoCommitIDs returns all statuses with a unique context for a given list of repo-sha pairs
|
||||
func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, commitIDs []string) (map[string][]*CommitStatus, error) {
|
||||
type result struct {
|
||||
ID int64
|
||||
Sha string
|
||||
Index int64
|
||||
SHA string
|
||||
}
|
||||
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID)
|
||||
}
|
||||
results := make([]result, 0, len(commitIDs))
|
||||
|
||||
sess := db.GetEngine(ctx).Table(&CommitStatus{})
|
||||
|
||||
// Create a disjunction of conditions for each repoID and SHA pair
|
||||
conds := make([]builder.Cond, 0, len(commitIDs))
|
||||
for _, sha := range commitIDs {
|
||||
conds = append(conds, builder.Eq{"sha": sha})
|
||||
}
|
||||
sess = sess.Where(builder.Eq{"repo_id": repoID}.And(builder.Or(conds...))).
|
||||
Select("max( id ) as id, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( id ) desc")
|
||||
sess := getBase().And(builder.Or(conds...)).
|
||||
Select("max( `index` ) as `index`, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( `index` ) desc")
|
||||
|
||||
err := sess.Find(&results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ids := make([]int64, 0, len(results))
|
||||
repoStatuses := make(map[string][]*CommitStatus)
|
||||
for _, result := range results {
|
||||
ids = append(ids, result.ID)
|
||||
}
|
||||
|
||||
statuses := make([]*CommitStatus, 0, len(ids))
|
||||
if len(ids) > 0 {
|
||||
err = db.GetEngine(ctx).In("id", ids).Find(&statuses)
|
||||
if len(results) > 0 {
|
||||
statuses := make([]*CommitStatus, 0, len(results))
|
||||
|
||||
conds = make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA})
|
||||
}
|
||||
err = getBase().And(builder.Or(conds...)).Find(&statuses)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Group the statuses by repo ID
|
||||
// Group the statuses by commit
|
||||
for _, status := range statuses {
|
||||
repoStatuses[status.SHA] = append(repoStatuses[status.SHA], status)
|
||||
}
|
||||
|
@ -383,22 +395,36 @@ func GetLatestCommitStatusForRepoCommitIDs(ctx context.Context, repoID int64, co
|
|||
|
||||
// FindRepoRecentCommitStatusContexts returns repository's recent commit status contexts
|
||||
func FindRepoRecentCommitStatusContexts(ctx context.Context, repoID int64, before time.Duration) ([]string, error) {
|
||||
type result struct {
|
||||
Index int64
|
||||
SHA string
|
||||
}
|
||||
getBase := func() *xorm.Session {
|
||||
return db.GetEngine(ctx).Table(&CommitStatus{}).Where("repo_id = ?", repoID)
|
||||
}
|
||||
|
||||
start := timeutil.TimeStampNow().AddDuration(-before)
|
||||
ids := make([]int64, 0, 10)
|
||||
if err := db.GetEngine(ctx).Table("commit_status").
|
||||
Where("repo_id = ?", repoID).
|
||||
And("updated_unix >= ?", start).
|
||||
Select("max( id ) as id").
|
||||
GroupBy("context_hash").OrderBy("max( id ) desc").
|
||||
Find(&ids); err != nil {
|
||||
results := make([]result, 0, 10)
|
||||
|
||||
sess := getBase().And("updated_unix >= ?", start).
|
||||
Select("max( `index` ) as `index`, sha").
|
||||
GroupBy("context_hash, sha").OrderBy("max( `index` ) desc")
|
||||
|
||||
err := sess.Find(&results)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contexts := make([]string, 0, len(ids))
|
||||
if len(ids) == 0 {
|
||||
contexts := make([]string, 0, len(results))
|
||||
if len(results) == 0 {
|
||||
return contexts, nil
|
||||
}
|
||||
return contexts, db.GetEngine(ctx).Select("context").Table("commit_status").In("id", ids).Find(&contexts)
|
||||
|
||||
conds := make([]builder.Cond, 0, len(results))
|
||||
for _, result := range results {
|
||||
conds = append(conds, builder.Eq{"`index`": result.Index, "sha": result.SHA})
|
||||
}
|
||||
return contexts, getBase().And(builder.Or(conds...)).Select("context").Find(&contexts)
|
||||
}
|
||||
|
||||
// NewCommitStatusOptions holds options for creating a CommitStatus
|
||||
|
|
|
@ -117,6 +117,10 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if err := comments.LoadAttachments(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Find all reviews by ReviewID
|
||||
reviews := make(map[int64]*Review)
|
||||
ids := make([]int64, 0, len(comments))
|
||||
|
|
|
@ -49,18 +49,13 @@ func (issue *Issue) ProjectBoardID(ctx context.Context) int64 {
|
|||
|
||||
// LoadIssuesFromBoard load issues assigned to this board
|
||||
func LoadIssuesFromBoard(ctx context.Context, b *project_model.Board) (IssueList, error) {
|
||||
issueList := make(IssueList, 0, 10)
|
||||
|
||||
if b.ID > 0 {
|
||||
issues, err := Issues(ctx, &IssuesOptions{
|
||||
ProjectBoardID: b.ID,
|
||||
ProjectID: b.ProjectID,
|
||||
SortType: "project-column-sorting",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
issueList = issues
|
||||
issueList, err := Issues(ctx, &IssuesOptions{
|
||||
ProjectBoardID: b.ID,
|
||||
ProjectID: b.ProjectID,
|
||||
SortType: "project-column-sorting",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if b.Default {
|
||||
|
|
|
@ -21,7 +21,7 @@ import (
|
|||
|
||||
// IssuesOptions represents options of an issue.
|
||||
type IssuesOptions struct { //nolint
|
||||
db.Paginator
|
||||
Paginator *db.ListOptions
|
||||
RepoIDs []int64 // overwrites RepoCond if the length is not 0
|
||||
AllPublic bool // include also all public repositories
|
||||
RepoCond builder.Cond
|
||||
|
@ -104,23 +104,11 @@ func applyLimit(sess *xorm.Session, opts *IssuesOptions) *xorm.Session {
|
|||
return sess
|
||||
}
|
||||
|
||||
// Warning: Do not use GetSkipTake() for *db.ListOptions
|
||||
// Its implementation could reset the page size with setting.API.MaxResponseItems
|
||||
if listOptions, ok := opts.Paginator.(*db.ListOptions); ok {
|
||||
if listOptions.Page >= 0 && listOptions.PageSize > 0 {
|
||||
var start int
|
||||
if listOptions.Page == 0 {
|
||||
start = 0
|
||||
} else {
|
||||
start = (listOptions.Page - 1) * listOptions.PageSize
|
||||
}
|
||||
sess.Limit(listOptions.PageSize, start)
|
||||
}
|
||||
return sess
|
||||
start := 0
|
||||
if opts.Paginator.Page > 1 {
|
||||
start = (opts.Paginator.Page - 1) * opts.Paginator.PageSize
|
||||
}
|
||||
|
||||
start, limit := opts.Paginator.GetSkipTake()
|
||||
sess.Limit(limit, start)
|
||||
sess.Limit(opts.Paginator.PageSize, start)
|
||||
|
||||
return sess
|
||||
}
|
||||
|
|
|
@ -68,13 +68,17 @@ func CountIssuesByRepo(ctx context.Context, opts *IssuesOptions) (map[int64]int6
|
|||
}
|
||||
|
||||
// CountIssues number return of issues by given conditions.
|
||||
func CountIssues(ctx context.Context, opts *IssuesOptions) (int64, error) {
|
||||
func CountIssues(ctx context.Context, opts *IssuesOptions, otherConds ...builder.Cond) (int64, error) {
|
||||
sess := db.GetEngine(ctx).
|
||||
Select("COUNT(issue.id) AS count").
|
||||
Table("issue").
|
||||
Join("INNER", "repository", "`issue`.repo_id = `repository`.id")
|
||||
applyConditions(sess, opts)
|
||||
|
||||
for _, cond := range otherConds {
|
||||
sess.And(cond)
|
||||
}
|
||||
|
||||
return sess.Count()
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,23 @@ func (err ErrNotValidReviewRequest) Unwrap() error {
|
|||
return util.ErrInvalidArgument
|
||||
}
|
||||
|
||||
// ErrReviewRequestOnClosedPR represents an error when an user tries to request a re-review on a closed or merged PR.
|
||||
type ErrReviewRequestOnClosedPR struct{}
|
||||
|
||||
// IsErrReviewRequestOnClosedPR checks if an error is an ErrReviewRequestOnClosedPR.
|
||||
func IsErrReviewRequestOnClosedPR(err error) bool {
|
||||
_, ok := err.(ErrReviewRequestOnClosedPR)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrReviewRequestOnClosedPR) Error() string {
|
||||
return "cannot request a re-review on a closed or merged PR"
|
||||
}
|
||||
|
||||
func (err ErrReviewRequestOnClosedPR) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// ReviewType defines the sort of feedback a review gives
|
||||
type ReviewType int
|
||||
|
||||
|
@ -618,9 +635,24 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// skip it when reviewer hase been request to review
|
||||
if review != nil && review.Type == ReviewTypeRequest {
|
||||
return nil, nil
|
||||
if review != nil {
|
||||
// skip it when reviewer hase been request to review
|
||||
if review.Type == ReviewTypeRequest {
|
||||
return nil, committer.Commit() // still commit the transaction, or committer.Close() will rollback it, even if it's a reused transaction.
|
||||
}
|
||||
|
||||
if issue.IsClosed {
|
||||
return nil, ErrReviewRequestOnClosedPR{}
|
||||
}
|
||||
|
||||
if issue.IsPull {
|
||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if issue.PullRequest.HasMerged {
|
||||
return nil, ErrReviewRequestOnClosedPR{}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if the reviewer is an official reviewer,
|
||||
|
|
|
@ -288,3 +288,33 @@ func TestDeleteDismissedReview(t *testing.T) {
|
|||
assert.NoError(t, issues_model.DeleteReview(db.DefaultContext, review))
|
||||
unittest.AssertNotExistsBean(t, &issues_model.Comment{ID: comment.ID})
|
||||
}
|
||||
|
||||
func TestAddReviewRequest(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 1})
|
||||
assert.NoError(t, pull.LoadIssue(db.DefaultContext))
|
||||
issue := pull.Issue
|
||||
assert.NoError(t, issue.LoadRepo(db.DefaultContext))
|
||||
reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
_, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{
|
||||
Issue: issue,
|
||||
Reviewer: reviewer,
|
||||
Type: issues_model.ReviewTypeReject,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
pull.HasMerged = false
|
||||
assert.NoError(t, pull.UpdateCols(db.DefaultContext, "has_merged"))
|
||||
issue.IsClosed = true
|
||||
_, err = issues_model.AddReviewRequest(db.DefaultContext, issue, reviewer, &user_model.User{})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err))
|
||||
|
||||
pull.HasMerged = true
|
||||
assert.NoError(t, pull.UpdateCols(db.DefaultContext, "has_merged"))
|
||||
issue.IsClosed = false
|
||||
_, err = issues_model.AddReviewRequest(db.DefaultContext, issue, reviewer, &user_model.User{})
|
||||
assert.Error(t, err)
|
||||
assert.True(t, issues_model.IsErrReviewRequestOnClosedPR(err))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
-
|
||||
id: 1
|
||||
title: project without default column
|
||||
owner_id: 2
|
||||
repo_id: 0
|
||||
is_closed: false
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
||||
|
||||
-
|
||||
id: 2
|
||||
title: project with multiple default columns
|
||||
owner_id: 2
|
||||
repo_id: 0
|
||||
is_closed: false
|
||||
creator_id: 2
|
||||
board_type: 1
|
||||
type: 2
|
||||
created_unix: 1688973000
|
||||
updated_unix: 1688973000
|
|
@ -0,0 +1,26 @@
|
|||
-
|
||||
id: 1
|
||||
project_id: 1
|
||||
title: Done
|
||||
creator_id: 2
|
||||
default: false
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 2
|
||||
project_id: 2
|
||||
title: Backlog
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
||||
|
||||
-
|
||||
id: 3
|
||||
project_id: 2
|
||||
title: Uncategorized
|
||||
creator_id: 2
|
||||
default: true
|
||||
created_unix: 1588117528
|
||||
updated_unix: 1588117528
|
|
@ -568,6 +568,12 @@ var migrations = []Migration{
|
|||
NewMigration("Add default_wiki_branch to repository table", v1_22.AddDefaultWikiBranch),
|
||||
// v290 -> v291
|
||||
NewMigration("Add PayloadVersion to HookTask", v1_22.AddPayloadVersionToHookTaskTable),
|
||||
// v291 -> v292
|
||||
NewMigration("Add Index to attachment.comment_id", v1_22.AddCommentIDIndexofAttachment),
|
||||
// v292 -> v293
|
||||
NewMigration("Ensure every project has exactly one default column - No Op", noopMigration),
|
||||
// v293 -> v294
|
||||
NewMigration("Ensure every project has exactly one default column", v1_22.CheckProjectColumnsConsistency),
|
||||
}
|
||||
|
||||
// GetCurrentDBVersion returns the current db version
|
||||
|
|
14
models/migrations/v1_22/v291.go
Normal file
14
models/migrations/v1_22/v291.go
Normal file
|
@ -0,0 +1,14 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import "xorm.io/xorm"
|
||||
|
||||
func AddCommentIDIndexofAttachment(x *xorm.Engine) error {
|
||||
type Attachment struct {
|
||||
CommentID int64 `xorm:"INDEX"`
|
||||
}
|
||||
|
||||
return x.Sync(&Attachment{})
|
||||
}
|
9
models/migrations/v1_22/v292.go
Normal file
9
models/migrations/v1_22/v292.go
Normal file
|
@ -0,0 +1,9 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
// NOTE: noop the original migration has bug which some projects will be skip, so
|
||||
// these projects will have no default board.
|
||||
// So that this migration will be skipped and go to v293.go
|
||||
// This file is a placeholder so that readers can know what happened
|
108
models/migrations/v1_22/v293.go
Normal file
108
models/migrations/v1_22/v293.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
|
||||
"xorm.io/xorm"
|
||||
)
|
||||
|
||||
// CheckProjectColumnsConsistency ensures there is exactly one default board per project present
|
||||
func CheckProjectColumnsConsistency(x *xorm.Engine) error {
|
||||
sess := x.NewSession()
|
||||
defer sess.Close()
|
||||
|
||||
limit := setting.Database.IterateBufferSize
|
||||
if limit <= 0 {
|
||||
limit = 50
|
||||
}
|
||||
|
||||
type Project struct {
|
||||
ID int64
|
||||
CreatorID int64
|
||||
BoardID int64
|
||||
}
|
||||
|
||||
type ProjectBoard struct {
|
||||
ID int64 `xorm:"pk autoincr"`
|
||||
Title string
|
||||
Default bool `xorm:"NOT NULL DEFAULT false"` // issues not assigned to a specific board will be assigned to this board
|
||||
Sorting int8 `xorm:"NOT NULL DEFAULT 0"`
|
||||
Color string `xorm:"VARCHAR(7)"`
|
||||
|
||||
ProjectID int64 `xorm:"INDEX NOT NULL"`
|
||||
CreatorID int64 `xorm:"NOT NULL"`
|
||||
|
||||
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
|
||||
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
|
||||
}
|
||||
|
||||
for {
|
||||
if err := sess.Begin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// all these projects without defaults will be fixed in the same loop, so
|
||||
// we just need to always get projects without defaults until no such project
|
||||
var projects []*Project
|
||||
if err := sess.Select("project.id as id, project.creator_id, project_board.id as board_id").
|
||||
Join("LEFT", "project_board", "project_board.project_id = project.id AND project_board.`default`=?", true).
|
||||
Where("project_board.id is NULL OR project_board.id = 0").
|
||||
Limit(limit).
|
||||
Find(&projects); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range projects {
|
||||
if _, err := sess.Insert(ProjectBoard{
|
||||
ProjectID: p.ID,
|
||||
Default: true,
|
||||
Title: "Uncategorized",
|
||||
CreatorID: p.CreatorID,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := sess.Commit(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(projects) == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
sess.Close()
|
||||
|
||||
return removeDuplicatedBoardDefault(x)
|
||||
}
|
||||
|
||||
func removeDuplicatedBoardDefault(x *xorm.Engine) error {
|
||||
type ProjectInfo struct {
|
||||
ProjectID int64
|
||||
DefaultNum int
|
||||
}
|
||||
var projects []ProjectInfo
|
||||
if err := x.Select("project_id, count(*) AS default_num").
|
||||
Table("project_board").
|
||||
Where("`default` = ?", true).
|
||||
GroupBy("project_id").
|
||||
Having("count(*) > 1").
|
||||
Find(&projects); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, project := range projects {
|
||||
if _, err := x.Where("project_id=?", project.ProjectID).
|
||||
Table("project_board").
|
||||
Limit(project.DefaultNum - 1).
|
||||
Update(map[string]bool{
|
||||
"`default`": false,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
44
models/migrations/v1_22/v293_test.go
Normal file
44
models/migrations/v1_22/v293_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package v1_22 //nolint
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/migrations/base"
|
||||
"code.gitea.io/gitea/models/project"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_CheckProjectColumnsConsistency(t *testing.T) {
|
||||
// Prepare and load the testing database
|
||||
x, deferable := base.PrepareTestEnv(t, 0, new(project.Project), new(project.Board))
|
||||
defer deferable()
|
||||
if x == nil || t.Failed() {
|
||||
return
|
||||
}
|
||||
|
||||
assert.NoError(t, CheckProjectColumnsConsistency(x))
|
||||
|
||||
// check if default board was added
|
||||
var defaultBoard project.Board
|
||||
has, err := x.Where("project_id=? AND `default` = ?", 1, true).Get(&defaultBoard)
|
||||
assert.NoError(t, err)
|
||||
assert.True(t, has)
|
||||
assert.Equal(t, int64(1), defaultBoard.ProjectID)
|
||||
assert.True(t, defaultBoard.Default)
|
||||
|
||||
// check if multiple defaults, previous were removed and last will be kept
|
||||
expectDefaultBoard, err := project.GetBoard(db.DefaultContext, 2)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), expectDefaultBoard.ProjectID)
|
||||
assert.False(t, expectDefaultBoard.Default)
|
||||
|
||||
expectNonDefaultBoard, err := project.GetBoard(db.DefaultContext, 3)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(2), expectNonDefaultBoard.ProjectID)
|
||||
assert.True(t, expectNonDefaultBoard.Default)
|
||||
}
|
|
@ -123,6 +123,17 @@ func createBoardsForProjectsType(ctx context.Context, project *Project) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
board := Board{
|
||||
CreatedUnix: timeutil.TimeStampNow(),
|
||||
CreatorID: project.CreatorID,
|
||||
Title: "Backlog",
|
||||
ProjectID: project.ID,
|
||||
Default: true,
|
||||
}
|
||||
if err := db.Insert(ctx, board); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
@ -176,6 +187,10 @@ func deleteBoardByID(ctx context.Context, boardID int64) error {
|
|||
return err
|
||||
}
|
||||
|
||||
if board.Default {
|
||||
return fmt.Errorf("deleteBoardByID: cannot delete default board")
|
||||
}
|
||||
|
||||
if err = board.removeIssues(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -194,7 +209,6 @@ func deleteBoardByProjectID(ctx context.Context, projectID int64) error {
|
|||
// GetBoard fetches the current board of a project
|
||||
func GetBoard(ctx context.Context, boardID int64) (*Board, error) {
|
||||
board := new(Board)
|
||||
|
||||
has, err := db.GetEngine(ctx).ID(boardID).Get(board)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -228,7 +242,6 @@ func UpdateBoard(ctx context.Context, board *Board) error {
|
|||
}
|
||||
|
||||
// GetBoards fetches all boards related to a project
|
||||
// if no default board set, first board is a temporary "Uncategorized" board
|
||||
func (p *Project) GetBoards(ctx context.Context) (BoardList, error) {
|
||||
boards := make([]*Board, 0, 5)
|
||||
|
||||
|
@ -244,53 +257,64 @@ func (p *Project) GetBoards(ctx context.Context) (BoardList, error) {
|
|||
return append([]*Board{defaultB}, boards...), nil
|
||||
}
|
||||
|
||||
// getDefaultBoard return default board and create a dummy if none exist
|
||||
// getDefaultBoard return default board and ensure only one exists
|
||||
func (p *Project) getDefaultBoard(ctx context.Context) (*Board, error) {
|
||||
var board Board
|
||||
exist, err := db.GetEngine(ctx).Where("project_id=? AND `default`=?", p.ID, true).Get(&board)
|
||||
has, err := db.GetEngine(ctx).
|
||||
Where("project_id=? AND `default` = ?", p.ID, true).
|
||||
Desc("id").Get(&board)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exist {
|
||||
|
||||
if has {
|
||||
return &board, nil
|
||||
}
|
||||
|
||||
// represents a board for issues not assigned to one
|
||||
return &Board{
|
||||
// create a default board if none is found
|
||||
board = Board{
|
||||
ProjectID: p.ID,
|
||||
Title: "Uncategorized",
|
||||
Default: true,
|
||||
}, nil
|
||||
Title: "Uncategorized",
|
||||
CreatorID: p.CreatorID,
|
||||
}
|
||||
if _, err := db.GetEngine(ctx).Insert(&board); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &board, nil
|
||||
}
|
||||
|
||||
// SetDefaultBoard represents a board for issues not assigned to one
|
||||
// if boardID is 0 unset default
|
||||
func SetDefaultBoard(ctx context.Context, projectID, boardID int64) error {
|
||||
_, err := db.GetEngine(ctx).Where(builder.Eq{
|
||||
"project_id": projectID,
|
||||
"`default`": true,
|
||||
}).Cols("`default`").Update(&Board{Default: false})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
if _, err := GetBoard(ctx, boardID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if boardID > 0 {
|
||||
_, err = db.GetEngine(ctx).ID(boardID).Where(builder.Eq{"project_id": projectID}).
|
||||
if _, err := db.GetEngine(ctx).Where(builder.Eq{
|
||||
"project_id": projectID,
|
||||
"`default`": true,
|
||||
}).Cols("`default`").Update(&Board{Default: false}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err := db.GetEngine(ctx).ID(boardID).
|
||||
Where(builder.Eq{"project_id": projectID}).
|
||||
Cols("`default`").Update(&Board{Default: true})
|
||||
}
|
||||
|
||||
return err
|
||||
return err
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateBoardSorting update project board sorting
|
||||
func UpdateBoardSorting(ctx context.Context, bs BoardList) error {
|
||||
for i := range bs {
|
||||
_, err := db.GetEngine(ctx).ID(bs[i].ID).Cols(
|
||||
"sorting",
|
||||
).Update(bs[i])
|
||||
if err != nil {
|
||||
return err
|
||||
return db.WithTx(ctx, func(ctx context.Context) error {
|
||||
for i := range bs {
|
||||
if _, err := db.GetEngine(ctx).ID(bs[i].ID).Cols(
|
||||
"sorting",
|
||||
).Update(bs[i]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
|
44
models/project/board_test.go
Normal file
44
models/project/board_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package project
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetDefaultBoard(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
projectWithoutDefault, err := GetProjectByID(db.DefaultContext, 5)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// check if default board was added
|
||||
board, err := projectWithoutDefault.getDefaultBoard(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(5), board.ProjectID)
|
||||
assert.Equal(t, "Uncategorized", board.Title)
|
||||
|
||||
projectWithMultipleDefaults, err := GetProjectByID(db.DefaultContext, 6)
|
||||
assert.NoError(t, err)
|
||||
|
||||
// check if multiple defaults were removed
|
||||
board, err = projectWithMultipleDefaults.getDefaultBoard(db.DefaultContext)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(6), board.ProjectID)
|
||||
assert.Equal(t, int64(9), board.ID)
|
||||
|
||||
// set 8 as default board
|
||||
assert.NoError(t, SetDefaultBoard(db.DefaultContext, board.ProjectID, 8))
|
||||
|
||||
// then 9 will become a non-default board
|
||||
board, err = GetBoard(db.DefaultContext, 9)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, int64(6), board.ProjectID)
|
||||
assert.False(t, board.Default)
|
||||
}
|
|
@ -92,19 +92,19 @@ func TestProjectsSort(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
sortType: "default",
|
||||
wants: []int64{1, 3, 2, 4},
|
||||
wants: []int64{1, 3, 2, 6, 5, 4},
|
||||
},
|
||||
{
|
||||
sortType: "oldest",
|
||||
wants: []int64{4, 2, 3, 1},
|
||||
wants: []int64{4, 5, 6, 2, 3, 1},
|
||||
},
|
||||
{
|
||||
sortType: "recentupdate",
|
||||
wants: []int64{1, 3, 2, 4},
|
||||
wants: []int64{1, 3, 2, 6, 5, 4},
|
||||
},
|
||||
{
|
||||
sortType: "leastupdate",
|
||||
wants: []int64{4, 2, 3, 1},
|
||||
wants: []int64{4, 5, 6, 2, 3, 1},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -113,8 +113,8 @@ func TestProjectsSort(t *testing.T) {
|
|||
OrderBy: GetSearchOrderByBySortType(tt.sortType),
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, int64(4), count)
|
||||
if assert.Len(t, projects, 4) {
|
||||
assert.EqualValues(t, int64(6), count)
|
||||
if assert.Len(t, projects, 6) {
|
||||
for i := range projects {
|
||||
assert.EqualValues(t, tt.wants[i], projects[i].ID)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
)
|
||||
|
||||
// ParsePaginator parses a db.Paginator into a skip and limit
|
||||
func ParsePaginator(paginator db.Paginator, max ...int) (int, int) {
|
||||
func ParsePaginator(paginator *db.ListOptions, max ...int) (int, int) {
|
||||
// Use a very large number to indicate no limit
|
||||
unlimited := math.MaxInt32
|
||||
if len(max) > 0 {
|
||||
|
@ -19,22 +19,15 @@ func ParsePaginator(paginator db.Paginator, max ...int) (int, int) {
|
|||
}
|
||||
|
||||
if paginator == nil || paginator.IsListAll() {
|
||||
// It shouldn't happen. In actual usage scenarios, there should not be requests to search all.
|
||||
// But if it does happen, respect it and return "unlimited".
|
||||
// And it's also useful for testing.
|
||||
return 0, unlimited
|
||||
}
|
||||
|
||||
// Warning: Do not use GetSkipTake() for *db.ListOptions
|
||||
// Its implementation could reset the page size with setting.API.MaxResponseItems
|
||||
if listOptions, ok := paginator.(*db.ListOptions); ok {
|
||||
if listOptions.Page >= 0 && listOptions.PageSize > 0 {
|
||||
var start int
|
||||
if listOptions.Page == 0 {
|
||||
start = 0
|
||||
} else {
|
||||
start = (listOptions.Page - 1) * listOptions.PageSize
|
||||
}
|
||||
return start, listOptions.PageSize
|
||||
}
|
||||
return 0, unlimited
|
||||
if paginator.PageSize == 0 {
|
||||
// Do not return any results when searching, it's used to get the total count only.
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
return paginator.GetSkipTake()
|
||||
|
|
|
@ -78,6 +78,17 @@ func (i *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
return nil, err
|
||||
}
|
||||
|
||||
// If pagesize == 0, return total count only. It's a special case for search count.
|
||||
if options.Paginator != nil && options.Paginator.PageSize == 0 {
|
||||
total, err := issue_model.CountIssues(ctx, opt, cond)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &internal.SearchResult{
|
||||
Total: total,
|
||||
}, nil
|
||||
}
|
||||
|
||||
ids, total, err := issue_model.IssueIDs(ctx, opt, cond)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -308,7 +308,7 @@ func SearchIssues(ctx context.Context, opts *SearchOptions) ([]int64, int64, err
|
|||
|
||||
// CountIssues counts issues by options. It is a shortcut of SearchIssues(ctx, opts) but only returns the total count.
|
||||
func CountIssues(ctx context.Context, opts *SearchOptions) (int64, error) {
|
||||
opts = opts.Copy(func(options *SearchOptions) { opts.Paginator = &db_model.ListOptions{PageSize: 0} })
|
||||
opts = opts.Copy(func(options *SearchOptions) { options.Paginator = &db_model.ListOptions{PageSize: 0} })
|
||||
|
||||
_, total, err := SearchIssues(ctx, opts)
|
||||
return total, err
|
||||
|
|
|
@ -106,7 +106,7 @@ type SearchOptions struct {
|
|||
UpdatedAfterUnix optional.Option[int64]
|
||||
UpdatedBeforeUnix optional.Option[int64]
|
||||
|
||||
db.Paginator
|
||||
Paginator *db.ListOptions
|
||||
|
||||
SortBy SortBy // sort by field
|
||||
}
|
||||
|
|
|
@ -77,6 +77,13 @@ func TestIndexer(t *testing.T, indexer internal.Indexer) {
|
|||
assert.Equal(t, c.ExpectedIDs, ids)
|
||||
assert.Equal(t, c.ExpectedTotal, result.Total)
|
||||
}
|
||||
|
||||
// test counting
|
||||
c.SearchOptions.Paginator = &db.ListOptions{PageSize: 0}
|
||||
countResult, err := indexer.Search(context.Background(), c.SearchOptions)
|
||||
require.NoError(t, err)
|
||||
assert.Empty(t, countResult.Hits)
|
||||
assert.Equal(t, result.Total, countResult.Total)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -218,6 +218,14 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
|
||||
skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits)
|
||||
|
||||
counting := limit == 0
|
||||
if counting {
|
||||
// If set limit to 0, it will be 20 by default, and -1 is not allowed.
|
||||
// See https://www.meilisearch.com/docs/reference/api/search#limit
|
||||
// So set limit to 1 to make the cost as low as possible, then clear the result before returning.
|
||||
limit = 1
|
||||
}
|
||||
|
||||
keyword := options.Keyword
|
||||
if !options.IsFuzzyKeyword {
|
||||
// to make it non fuzzy ("typo tolerance" in meilisearch terms), we have to quote the keyword(s)
|
||||
|
@ -236,6 +244,10 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
return nil, err
|
||||
}
|
||||
|
||||
if counting {
|
||||
searchRes.Hits = nil
|
||||
}
|
||||
|
||||
hits, err := convertHits(searchRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
@ -103,7 +103,7 @@ func generateSaveInternalToken(rootCfg ConfigProvider) {
|
|||
func loadSecurityFrom(rootCfg ConfigProvider) {
|
||||
sec := rootCfg.Section("security")
|
||||
InstallLock = HasInstallLock(rootCfg)
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(7)
|
||||
LogInRememberDays = sec.Key("LOGIN_REMEMBER_DAYS").MustInt(31)
|
||||
SecretKey = loadSecret(sec, "SECRET_KEY_URI", "SECRET_KEY")
|
||||
if SecretKey == "" {
|
||||
// FIXME: https://github.com/go-gitea/gitea/issues/16832
|
||||
|
|
|
@ -40,7 +40,7 @@ func RenderCommitMessage(ctx context.Context, msg string, metas map[string]strin
|
|||
if len(msgLines) == 0 {
|
||||
return template.HTML("")
|
||||
}
|
||||
return template.HTML(msgLines[0])
|
||||
return RenderCodeBlock(template.HTML(msgLines[0]))
|
||||
}
|
||||
|
||||
// RenderCommitMessageLinkSubject renders commit message as a XSS-safe link to
|
||||
|
@ -67,7 +67,7 @@ func RenderCommitMessageLinkSubject(ctx context.Context, msg, urlDefault string,
|
|||
log.Error("RenderCommitMessageSubject: %v", err)
|
||||
return template.HTML("")
|
||||
}
|
||||
return template.HTML(renderedMessage)
|
||||
return RenderCodeBlock(template.HTML(renderedMessage))
|
||||
}
|
||||
|
||||
// RenderCommitBody extracts the body of a commit message without its title.
|
||||
|
|
|
@ -1423,7 +1423,6 @@ projects.type.basic_kanban = Basic Kanban
|
|||
projects.type.bug_triage = Bug Triage
|
||||
projects.template.desc = Template
|
||||
projects.template.desc_helper = Select a project template to get started
|
||||
projects.type.uncategorized = Uncategorized
|
||||
projects.column.edit = Edit Column
|
||||
projects.column.edit_title = Name
|
||||
projects.column.new_title = Name
|
||||
|
@ -1431,10 +1430,8 @@ projects.column.new_submit = Create Column
|
|||
projects.column.new = New Column
|
||||
projects.column.set_default = Set Default
|
||||
projects.column.set_default_desc = Set this column as default for uncategorized issues and pulls
|
||||
projects.column.unset_default = Unset Default
|
||||
projects.column.unset_default_desc = Unset this column as default
|
||||
projects.column.delete = Delete Column
|
||||
projects.column.deletion_desc = Deleting a project column moves all related issues to "Uncategorized". Continue?
|
||||
projects.column.deletion_desc = Deleting a project column moves all related issues to the default column. Continue?
|
||||
projects.column.color = Color
|
||||
projects.open = Open
|
||||
projects.close = Close
|
||||
|
|
409
package-lock.json
generated
409
package-lock.json
generated
|
@ -11,10 +11,10 @@
|
|||
"@citation-js/plugin-software-formats": "0.6.1",
|
||||
"@claviska/jquery-minicolors": "2.3.6",
|
||||
"@github/markdown-toolbar-element": "2.2.3",
|
||||
"@github/relative-time-element": "4.3.1",
|
||||
"@github/relative-time-element": "4.4.0",
|
||||
"@github/text-expander-element": "2.6.1",
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||
"@primer/octicons": "19.8.0",
|
||||
"@primer/octicons": "19.9.0",
|
||||
"add-asset-webpack-plugin": "2.0.1",
|
||||
"ansi_up": "6.0.2",
|
||||
"asciinema-player": "3.7.1",
|
||||
|
@ -40,7 +40,7 @@
|
|||
"monaco-editor": "0.47.0",
|
||||
"monaco-editor-webpack-plugin": "7.1.0",
|
||||
"pdfobject": "2.3.0",
|
||||
"postcss": "8.4.35",
|
||||
"postcss": "8.4.38",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-nesting": "12.1.0",
|
||||
"pretty-ms": "9.0.0",
|
||||
|
@ -59,7 +59,7 @@
|
|||
"vue-chartjs": "5.3.0",
|
||||
"vue-loader": "17.4.2",
|
||||
"vue3-calendar-heatmap": "2.0.5",
|
||||
"webpack": "5.90.3",
|
||||
"webpack": "5.91.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"wrap-ansi": "9.0.0"
|
||||
},
|
||||
|
@ -77,24 +77,24 @@
|
|||
"eslint-plugin-jquery": "1.5.1",
|
||||
"eslint-plugin-no-jquery": "2.7.0",
|
||||
"eslint-plugin-no-use-extend-native": "0.5.0",
|
||||
"eslint-plugin-regexp": "2.3.0",
|
||||
"eslint-plugin-regexp": "2.4.0",
|
||||
"eslint-plugin-sonarjs": "0.24.0",
|
||||
"eslint-plugin-unicorn": "51.0.1",
|
||||
"eslint-plugin-vitest": "0.3.26",
|
||||
"eslint-plugin-vitest-globals": "1.4.0",
|
||||
"eslint-plugin-vue": "9.23.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.7.2",
|
||||
"eslint-plugin-vitest": "0.4.0",
|
||||
"eslint-plugin-vitest-globals": "1.5.0",
|
||||
"eslint-plugin-vue": "9.24.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.8.0",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"happy-dom": "14.2.0",
|
||||
"happy-dom": "14.3.7",
|
||||
"markdownlint-cli": "0.39.0",
|
||||
"postcss-html": "1.6.0",
|
||||
"stylelint": "16.2.1",
|
||||
"stylelint": "16.3.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||
"stylelint-declaration-strict-value": "1.10.4",
|
||||
"svgo": "3.2.0",
|
||||
"updates": "15.3.1",
|
||||
"updates": "16.0.0",
|
||||
"vite-string-plugin": "1.1.5",
|
||||
"vitest": "1.3.1"
|
||||
"vitest": "1.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18.0.0"
|
||||
|
@ -516,6 +516,16 @@
|
|||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@dual-bundle/import-meta-resolve": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@dual-bundle/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
|
||||
"integrity": "sha512-ZKXyJeFAzcpKM2kk8ipoGIPUqx9BX52omTGnfwjJvxOCaZTM2wtDK7zN0aIgPRbT9XYAlha0HtmZ+XKteuh0Gw==",
|
||||
"dev": true,
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
|
||||
|
@ -994,9 +1004,9 @@
|
|||
"integrity": "sha512-AlquKGee+IWiAMYVB0xyHFZRMnu4n3X4HTvJHu79GiVJ1ojTukCWyxMlF5NMsecoLcBKsuBhx3QPv2vkE/zQ0A=="
|
||||
},
|
||||
"node_modules/@github/relative-time-element": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.3.1.tgz",
|
||||
"integrity": "sha512-zL79nlhZVCg7x2Pf/HT5MB0mowmErE71VXpF10/3Wy8dQwkninNO1M9aOizh2wKC5LkSpDXqNYjDZwbH0/bcSg=="
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@github/relative-time-element/-/relative-time-element-4.4.0.tgz",
|
||||
"integrity": "sha512-CrI6oAecoahG7PF5dsgjdvlF5kCtusVMjg810EULD81TvnDsP+k/FRi/ClFubWLgBo4EGpr2EfvmumtqQFo7ow=="
|
||||
},
|
||||
"node_modules/@github/text-expander-element": {
|
||||
"version": "2.6.1",
|
||||
|
@ -1365,9 +1375,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@primer/octicons": {
|
||||
"version": "19.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.8.0.tgz",
|
||||
"integrity": "sha512-Imze/fyW41Io5fN+27T5EAeXJrgBjMbz6nzU+wYbRylXvIAjLPUvaJPVoStiFlgSU+TjTUJqg5A9rgMDzTyMCg==",
|
||||
"version": "19.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@primer/octicons/-/octicons-19.9.0.tgz",
|
||||
"integrity": "sha512-uAZa9cMgWkzbEsZnYWB7tg0vt7QprubD7ljtprz2fBJ8CjyqoxFRRsFvH4UiJdjK/3o87ODgDkhiflyJXDh+Lg==",
|
||||
"dependencies": {
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
|
@ -2238,16 +2248,16 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/@typescript-eslint/eslint-plugin": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.3.1.tgz",
|
||||
"integrity": "sha512-STEDMVQGww5lhCuNXVSQfbfuNII5E08QWkvAw5Qwf+bj2WT+JkG1uc+5/vXA3AOYMDHVOSpL+9rcbEUiHIm2dw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz",
|
||||
"integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/regexpp": "^4.5.1",
|
||||
"@typescript-eslint/scope-manager": "7.3.1",
|
||||
"@typescript-eslint/type-utils": "7.3.1",
|
||||
"@typescript-eslint/utils": "7.3.1",
|
||||
"@typescript-eslint/visitor-keys": "7.3.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/type-utils": "7.4.0",
|
||||
"@typescript-eslint/utils": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"graphemer": "^1.4.0",
|
||||
"ignore": "^5.2.4",
|
||||
|
@ -2273,15 +2283,15 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/parser": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.3.1.tgz",
|
||||
"integrity": "sha512-Rq49+pq7viTRCH48XAbTA+wdLRrB/3sRq4Lpk0oGDm0VmnjBrAOVXH/Laalmwsv2VpekiEfVFwJYVk6/e8uvQw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz",
|
||||
"integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "7.3.1",
|
||||
"@typescript-eslint/types": "7.3.1",
|
||||
"@typescript-eslint/typescript-estree": "7.3.1",
|
||||
"@typescript-eslint/visitor-keys": "7.3.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2301,13 +2311,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/scope-manager": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.3.1.tgz",
|
||||
"integrity": "sha512-fVS6fPxldsKY2nFvyT7IP78UO1/I2huG+AYu5AMjCT9wtl6JFiDnsv4uad4jQ0GTFzcUV5HShVeN96/17bTBag==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz",
|
||||
"integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.3.1",
|
||||
"@typescript-eslint/visitor-keys": "7.3.1"
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
|
@ -2318,13 +2328,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/type-utils": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.3.1.tgz",
|
||||
"integrity": "sha512-iFhaysxFsMDQlzJn+vr3OrxN8NmdQkHks4WaqD4QBnt5hsq234wcYdyQ9uquzJJIDAj5W4wQne3yEsYA6OmXGw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz",
|
||||
"integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/typescript-estree": "7.3.1",
|
||||
"@typescript-eslint/utils": "7.3.1",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"@typescript-eslint/utils": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"ts-api-utils": "^1.0.1"
|
||||
},
|
||||
|
@ -2345,9 +2355,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/types": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.3.1.tgz",
|
||||
"integrity": "sha512-2tUf3uWggBDl4S4183nivWQ2HqceOZh1U4hhu4p1tPiIJoRRXrab7Y+Y0p+dozYwZVvLPRI6r5wKe9kToF9FIw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz",
|
||||
"integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": "^18.18.0 || >=20.0.0"
|
||||
|
@ -2358,13 +2368,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/typescript-estree": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.3.1.tgz",
|
||||
"integrity": "sha512-tLpuqM46LVkduWP7JO7yVoWshpJuJzxDOPYIVWUUZbW+4dBpgGeUdl/fQkhuV0A8eGnphYw3pp8d2EnvPOfxmQ==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz",
|
||||
"integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.3.1",
|
||||
"@typescript-eslint/visitor-keys": "7.3.1",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/visitor-keys": "7.4.0",
|
||||
"debug": "^4.3.4",
|
||||
"globby": "^11.1.0",
|
||||
"is-glob": "^4.0.3",
|
||||
|
@ -2386,17 +2396,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/utils": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.3.1.tgz",
|
||||
"integrity": "sha512-jIERm/6bYQ9HkynYlNZvXpzmXWZGhMbrOvq3jJzOSOlKXsVjrrolzWBjDW6/TvT5Q3WqaN4EkmcfdQwi9tDjBQ==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz",
|
||||
"integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"@types/json-schema": "^7.0.12",
|
||||
"@types/semver": "^7.5.0",
|
||||
"@typescript-eslint/scope-manager": "7.3.1",
|
||||
"@typescript-eslint/types": "7.3.1",
|
||||
"@typescript-eslint/typescript-estree": "7.3.1",
|
||||
"@typescript-eslint/scope-manager": "7.4.0",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"@typescript-eslint/typescript-estree": "7.4.0",
|
||||
"semver": "^7.5.4"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2411,12 +2421,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@typescript-eslint/visitor-keys": {
|
||||
"version": "7.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.3.1.tgz",
|
||||
"integrity": "sha512-9RMXwQF8knsZvfv9tdi+4D/j7dMG28X/wMJ8Jj6eOHyHWwDW4ngQJcqEczSsqIKKjFiLFr40Mnr7a5ulDD3vmw==",
|
||||
"version": "7.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz",
|
||||
"integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/types": "7.3.1",
|
||||
"@typescript-eslint/types": "7.4.0",
|
||||
"eslint-visitor-keys": "^3.4.1"
|
||||
},
|
||||
"engines": {
|
||||
|
@ -2447,13 +2457,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/expect": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.3.1.tgz",
|
||||
"integrity": "sha512-xofQFwIzfdmLLlHa6ag0dPV8YsnKOCP1KdAeVVh34vSjN2dcUiXYCD9htu/9eM7t8Xln4v03U9HLxLpPlsXdZw==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz",
|
||||
"integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/spy": "1.3.1",
|
||||
"@vitest/utils": "1.3.1",
|
||||
"@vitest/spy": "1.4.0",
|
||||
"@vitest/utils": "1.4.0",
|
||||
"chai": "^4.3.10"
|
||||
},
|
||||
"funding": {
|
||||
|
@ -2461,12 +2471,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/runner": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.3.1.tgz",
|
||||
"integrity": "sha512-5FzF9c3jG/z5bgCnjr8j9LNq/9OxV2uEBAITOXfoe3rdZJTdO7jzThth7FXv/6b+kdY65tpRQB7WaKhNZwX+Kg==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz",
|
||||
"integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/utils": "1.3.1",
|
||||
"@vitest/utils": "1.4.0",
|
||||
"p-limit": "^5.0.0",
|
||||
"pathe": "^1.1.1"
|
||||
},
|
||||
|
@ -2502,9 +2512,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/snapshot": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.3.1.tgz",
|
||||
"integrity": "sha512-EF++BZbt6RZmOlE3SuTPu/NfwBF6q4ABS37HHXzs2LUVPBLx2QoY/K0fKpRChSo8eLiuxcbCVfqKgx/dplCDuQ==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz",
|
||||
"integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"magic-string": "^0.30.5",
|
||||
|
@ -2528,9 +2538,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/spy": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.3.1.tgz",
|
||||
"integrity": "sha512-xAcW+S099ylC9VLU7eZfdT9myV67Nor9w9zhf0mGCYJSO+zM2839tOeROTdikOi/8Qeusffvxb/MyBSOja1Uig==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz",
|
||||
"integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"tinyspy": "^2.2.0"
|
||||
|
@ -2540,9 +2550,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/@vitest/utils": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.3.1.tgz",
|
||||
"integrity": "sha512-d3Waie/299qqRyHTm2DjADeTaNdNSVsnwHPWrs20JMpjh6eiVq7ggggweO8rc4arhf6rRkWuHKwvxGvejUXZZQ==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz",
|
||||
"integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"diff-sequences": "^29.6.3",
|
||||
|
@ -3475,9 +3485,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001599",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001599.tgz",
|
||||
"integrity": "sha512-LRAQHZ4yT1+f9LemSMeqdMpMxZcc4RMWdj4tiFe3G8tNkWK+E58g+/tzotb5cU6TbcVJLr4fySiAW7XmxQvZQA==",
|
||||
"version": "1.0.30001600",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001600.tgz",
|
||||
"integrity": "sha512-+2S9/2JFhYmYaDpZvo0lKkfvuKIglrx68MwOBqMGHhQsNkLjB5xtc/TGoEPs+MxjSyN/72qer2g97nzR641mOQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -4721,9 +4731,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/dompurify": {
|
||||
"version": "3.0.10",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.10.tgz",
|
||||
"integrity": "sha512-WZDL8ZHTliEVP3Lk4phtvjg8SNQ3YMc5WVstxE8cszKZrFjzI4PF4ZTIk9VGAc9vZADO7uGO2V/ZiStcRSAT4Q=="
|
||||
"version": "3.0.11",
|
||||
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.0.11.tgz",
|
||||
"integrity": "sha512-Fan4uMuyB26gFV3ovPoEoQbxRRPfTu3CvImyZnhGq5fsIEO+gEFLp45ISFt+kQBWsK5ulDdT0oV28jS1UrwQLg=="
|
||||
},
|
||||
"node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
|
@ -4766,9 +4776,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.713",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.713.tgz",
|
||||
"integrity": "sha512-vDarADhwntXiULEdmWd77g2dV6FrNGa8ecAC29MZ4TwPut2fvosD0/5sJd1qWNNe8HcJFAC+F5Lf9jW1NPtWmw=="
|
||||
"version": "1.4.716",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.716.tgz",
|
||||
"integrity": "sha512-t/MXMzFKQC3UfMDpw7V5wdB/UAB8dWx4hEsy+fpPYJWW3gqh3u5T1uXp6vR+H6dGCPBxkRo+YBcapBLvbGQHRw=="
|
||||
},
|
||||
"node_modules/elkjs": {
|
||||
"version": "0.9.2",
|
||||
|
@ -4899,19 +4909,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/es-aggregate-error": {
|
||||
"version": "1.0.12",
|
||||
"resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.12.tgz",
|
||||
"integrity": "sha512-j0PupcmELoVbYS2NNrsn5zcLLEsryQwP02x8fRawh7c2eEaPHwJFAxltZsqV7HJjsF57+SMpYyVRWgbVLfOagg==",
|
||||
"version": "1.0.13",
|
||||
"resolved": "https://registry.npmjs.org/es-aggregate-error/-/es-aggregate-error-1.0.13.tgz",
|
||||
"integrity": "sha512-KkzhUUuD2CUMqEc8JEqsXEMDHzDPE8RCjZeUBitsnB1eNcAJWQPiciKsMXe3Yytj4Flw1XLl46Qcf9OxvZha7A==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"define-data-property": "^1.1.1",
|
||||
"define-data-property": "^1.1.4",
|
||||
"define-properties": "^1.2.1",
|
||||
"es-abstract": "^1.22.3",
|
||||
"es-errors": "^1.1.0",
|
||||
"es-abstract": "^1.23.2",
|
||||
"es-errors": "^1.3.0",
|
||||
"function-bind": "^1.1.2",
|
||||
"globalthis": "^1.0.3",
|
||||
"has-property-descriptors": "^1.0.1",
|
||||
"set-function-name": "^2.0.1"
|
||||
"has-property-descriptors": "^1.0.2",
|
||||
"set-function-name": "^2.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
|
@ -4967,9 +4977,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/es-module-lexer": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.4.2.tgz",
|
||||
"integrity": "sha512-7nOqkomXZEaxUDJw21XZNtRk739QvrPSoZoRtbsEfcii00vdzZUh6zh1CQwHhrib8MdEtJfv5rJiGeb4KuV/vw=="
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.0.tgz",
|
||||
"integrity": "sha512-pqrTKmwEIgafsYZAGw9kszYzmagcE/n4dbgwGWLEXg7J4QFJVQRBld8j3Q3GNez79jzxZshq0bcT962QHOghjw=="
|
||||
},
|
||||
"node_modules/es-object-atoms": {
|
||||
"version": "1.0.0",
|
||||
|
@ -5164,9 +5174,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-compat-utils": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.4.1.tgz",
|
||||
"integrity": "sha512-5N7ZaJG5pZxUeNNJfUchurLVrunD1xJvyg5kYOIVF8kg1f3ajTikmAu/5fZ9w100omNPOoMjngRszh/Q/uFGMg==",
|
||||
"version": "0.5.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-compat-utils/-/eslint-compat-utils-0.5.0.tgz",
|
||||
"integrity": "sha512-dc6Y8tzEcSYZMHa+CMPLi/hyo1FzNeonbhJL7Ol0ccuKQkwopJcJBA9YL/xmMTLU1eKigXo9vj9nALElWYSowg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"semver": "^7.5.4"
|
||||
|
@ -5598,9 +5608,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-regexp": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.3.0.tgz",
|
||||
"integrity": "sha512-T8JUs7ssRGbuXb+CGfdUJbcxTBMCNOpNqNBLuC8JUKAEIez72J37RaOi5/4dAUsGz92GbWVtqTLPSJZGyP/sQA==",
|
||||
"version": "2.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.4.0.tgz",
|
||||
"integrity": "sha512-OL2S6VPjQhs9s/NclQ0qattVq1J0GU8ox70/HIVy5Dxw+qbbdd7KQkyucsez2clEQjvdtDe12DTnPphFFUyXFg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.2.0",
|
||||
|
@ -5664,12 +5674,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vitest": {
|
||||
"version": "0.3.26",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.26.tgz",
|
||||
"integrity": "sha512-oxe5JSPgRjco8caVLTh7Ti8PxpwJdhSV0hTQAmkFcNcmy/9DnqLB/oNVRA11RmVRP//2+jIIT6JuBEcpW3obYg==",
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.0.tgz",
|
||||
"integrity": "sha512-3oWgZIwdWVBQ5plvkmOBjreIGLQRdYb7x54OP8uIRHeZyRVJIdOn9o/qWVb9292fDMC8jn7H7d9TSFBZqhrykQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/utils": "^7.1.1"
|
||||
"@typescript-eslint/utils": "^7.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18.0.0 || >= 20.0.0"
|
||||
|
@ -5688,18 +5698,19 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vitest-globals": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vitest-globals/-/eslint-plugin-vitest-globals-1.4.0.tgz",
|
||||
"integrity": "sha512-WE+YlK9X9s4vf5EaYRU0Scw7WItDZStm+PapFSYlg2ABNtaQ4zIG7wEqpoUB3SlfM+SgkhgmzR0TeJOO5k3/Nw==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vitest-globals/-/eslint-plugin-vitest-globals-1.5.0.tgz",
|
||||
"integrity": "sha512-ZSsVOaOIig0oVLzRTyk8lUfBfqzWxr/J3/NFMfGGRIkGQPejJYmDH3gXmSJxAojts77uzAGB/UmVrwi2DC4LYA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eslint-plugin-vue": {
|
||||
"version": "9.23.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.23.0.tgz",
|
||||
"integrity": "sha512-Bqd/b7hGYGrlV+wP/g77tjyFmp81lh5TMw0be9093X02SyelxRRfCI6/IsGq/J7Um0YwB9s0Ry0wlFyjPdmtUw==",
|
||||
"version": "9.24.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz",
|
||||
"integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"globals": "^13.24.0",
|
||||
"natural-compare": "^1.4.0",
|
||||
"nth-check": "^2.1.1",
|
||||
"postcss-selector-parser": "^6.0.15",
|
||||
|
@ -5715,13 +5726,13 @@
|
|||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-vue-scoped-css": {
|
||||
"version": "2.7.2",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.7.2.tgz",
|
||||
"integrity": "sha512-myJ99CJuwmAx5kq1WjgIeaUkxeU6PIEUh7age79Alm30bhN4fVTapOQLSMlvVTgxr36Y3igsZ3BCJM32LbHHig==",
|
||||
"version": "2.8.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-vue-scoped-css/-/eslint-plugin-vue-scoped-css-2.8.0.tgz",
|
||||
"integrity": "sha512-JXb3Um4+AhuDGxSX6FAGCI0p811xF7W8L7yxC8wmAEZEI/teTjlpC09noqQZHXn53RZ/TGQJ8Onaq4teYLxBbg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.4.0",
|
||||
"eslint-compat-utils": "^0.4.0",
|
||||
"eslint-compat-utils": "^0.5.0",
|
||||
"lodash": "^4.17.21",
|
||||
"postcss": "^8.4.31",
|
||||
"postcss-safe-parser": "^6.0.0",
|
||||
|
@ -6502,9 +6513,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/happy-dom": {
|
||||
"version": "14.2.0",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.2.0.tgz",
|
||||
"integrity": "sha512-vTqF/9MEkRKgYy5eKq9W0uiNmkgnVAmJhRwn8x8fQBR7lc4C84859jLhgZ1lR4Gi/t70oSdgvtLpxlHjgdJrAw==",
|
||||
"version": "14.3.7",
|
||||
"resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.3.7.tgz",
|
||||
"integrity": "sha512-lUfDRGzjrVJF2pnvh13OL+qEJ9eDpcedVLm77a3aMg8gPGKXfG+xFMNk3cOWetjucU8FveJ4qcSC/EX55nJ4fQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"entities": "^4.5.0",
|
||||
|
@ -7584,9 +7595,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/known-css-properties": {
|
||||
"version": "0.29.0",
|
||||
"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.29.0.tgz",
|
||||
"integrity": "sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==",
|
||||
"version": "0.30.0",
|
||||
"resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.30.0.tgz",
|
||||
"integrity": "sha512-VSWXYUnsPu9+WYKkfmJyLKtIvaRJi1kXUqVmBACORXZQxT5oZDsoZ2vQP+bQFDnWtpI/4eq3MLoRMjI2fnLzTQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/language-subtag-registry": {
|
||||
|
@ -9346,9 +9357,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
@ -9366,7 +9377,7 @@
|
|||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
"source-map-js": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
|
@ -10676,14 +10687,17 @@
|
|||
}
|
||||
},
|
||||
"node_modules/string.prototype.trimstart": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz",
|
||||
"integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==",
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
|
||||
"integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"define-properties": "^1.2.0",
|
||||
"es-abstract": "^1.22.1"
|
||||
"call-bind": "^1.0.7",
|
||||
"define-properties": "^1.2.1",
|
||||
"es-object-atoms": "^1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
|
@ -10776,15 +10790,16 @@
|
|||
"dev": true
|
||||
},
|
||||
"node_modules/stylelint": {
|
||||
"version": "16.2.1",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.2.1.tgz",
|
||||
"integrity": "sha512-SfIMGFK+4n7XVAyv50CpVfcGYWG4v41y6xG7PqOgQSY8M/PgdK0SQbjWFblxjJZlN9jNq879mB4BCZHJRIJ1hA==",
|
||||
"version": "16.3.0",
|
||||
"resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.3.0.tgz",
|
||||
"integrity": "sha512-hqC6xNTbQ5HRGQXfIW4HwXcx09raIFz4W4XFbraeqWqYRVVY/ibYvI0dsu0ORMQY8re2bpDdCAeIa2cm+QJ4Sw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@csstools/css-parser-algorithms": "^2.5.0",
|
||||
"@csstools/css-tokenizer": "^2.2.3",
|
||||
"@csstools/media-query-list-parser": "^2.1.7",
|
||||
"@csstools/selector-specificity": "^3.0.1",
|
||||
"@csstools/css-parser-algorithms": "^2.6.1",
|
||||
"@csstools/css-tokenizer": "^2.2.4",
|
||||
"@csstools/media-query-list-parser": "^2.1.9",
|
||||
"@csstools/selector-specificity": "^3.0.2",
|
||||
"@dual-bundle/import-meta-resolve": "^4.0.0",
|
||||
"balanced-match": "^2.0.0",
|
||||
"colord": "^2.9.3",
|
||||
"cosmiconfig": "^9.0.0",
|
||||
|
@ -10798,19 +10813,19 @@
|
|||
"globby": "^11.1.0",
|
||||
"globjoin": "^0.1.4",
|
||||
"html-tags": "^3.3.1",
|
||||
"ignore": "^5.3.0",
|
||||
"ignore": "^5.3.1",
|
||||
"imurmurhash": "^0.1.4",
|
||||
"is-plain-object": "^5.0.0",
|
||||
"known-css-properties": "^0.29.0",
|
||||
"known-css-properties": "^0.30.0",
|
||||
"mathml-tag-names": "^2.1.3",
|
||||
"meow": "^13.1.0",
|
||||
"meow": "^13.2.0",
|
||||
"micromatch": "^4.0.5",
|
||||
"normalize-path": "^3.0.0",
|
||||
"picocolors": "^1.0.0",
|
||||
"postcss": "^8.4.33",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-resolve-nested-selector": "^0.1.1",
|
||||
"postcss-safe-parser": "^7.0.0",
|
||||
"postcss-selector-parser": "^6.0.15",
|
||||
"postcss-selector-parser": "^6.0.16",
|
||||
"postcss-value-parser": "^4.2.0",
|
||||
"resolve-from": "^5.0.0",
|
||||
"string-width": "^4.2.3",
|
||||
|
@ -11418,9 +11433,9 @@
|
|||
"integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="
|
||||
},
|
||||
"node_modules/tinypool": {
|
||||
"version": "0.8.2",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.2.tgz",
|
||||
"integrity": "sha512-SUszKYe5wgsxnNOVlBYO6IC+8VGWdVGZWAqUxp3UErNBtptZvWbwyUOyzNL59zigz2rCA92QiL3wvG+JDSdJdQ==",
|
||||
"version": "0.8.3",
|
||||
"resolved": "https://registry.npmjs.org/tinypool/-/tinypool-0.8.3.tgz",
|
||||
"integrity": "sha512-Ud7uepAklqRH1bvwy22ynrliC7Dljz7Tm8M/0RBUW+YRa4YHhZ6e4PpgE+fu1zr/WqB1kbeuVrdfeuyIBpy4tw==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
|
@ -11611,9 +11626,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/typed-array-length": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.5.tgz",
|
||||
"integrity": "sha512-yMi0PlwuznKHxKmcpoOdeLwxBoVPkqZxd7q2FgMkmD3bNwvF5VW0+UlUQ1k1vmktTu4Yu13Q0RIxEP8+B+wloA==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.6.tgz",
|
||||
"integrity": "sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.7",
|
||||
|
@ -11737,12 +11752,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/updates": {
|
||||
"version": "15.3.1",
|
||||
"resolved": "https://registry.npmjs.org/updates/-/updates-15.3.1.tgz",
|
||||
"integrity": "sha512-DqHT1aJ6p6jVLWRiAeuVx/TQotvEwUjgrY1Mlc0a2qYk+eKEQVXugQ4M+6QoVMA3X1NFAVsb02d93pmWam4bBA==",
|
||||
"version": "16.0.0",
|
||||
"resolved": "https://registry.npmjs.org/updates/-/updates-16.0.0.tgz",
|
||||
"integrity": "sha512-Ra3QUu/rfbSCsG83zNNvoRQt0FVT3qULBSALYTlwTDX6oyz7R5GQAYwqJoIG/RDjYAXpwr3usrInoyHHTP6cag==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"updates": "bin/updates.js"
|
||||
"updates": "dist/updates.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
|
@ -11825,9 +11840,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.2.tgz",
|
||||
"integrity": "sha512-FWZbz0oSdLq5snUI0b6sULbz58iXFXdvkZfZWR/F0ZJuKTSPO7v72QPXt6KqYeMFb0yytNp6kZosxJ96Nr/wDQ==",
|
||||
"version": "5.2.6",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.6.tgz",
|
||||
"integrity": "sha512-FPtnxFlSIKYjZ2eosBQamz4CbyrTizbZ3hnGJlh/wMtCrlp1Hah6AzBLjGI5I2urTfNnpovpHdrL6YRuBOPnCA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.20.1",
|
||||
|
@ -11880,9 +11895,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vite-node": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.3.1.tgz",
|
||||
"integrity": "sha512-azbRrqRxlWTJEVbzInZCTchx0X69M/XPTCz4H+TLvlTcR/xH/3hkRqhOakT41fMJCMzXTu4UvegkZiEoJAWvng==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz",
|
||||
"integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cac": "^6.7.14",
|
||||
|
@ -11927,34 +11942,6 @@
|
|||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/postcss": {
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/rollup": {
|
||||
"version": "4.13.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.13.0.tgz",
|
||||
|
@ -11988,16 +11975,16 @@
|
|||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.3.1.tgz",
|
||||
"integrity": "sha512-/1QJqXs8YbCrfv/GPQ05wAZf2eakUPLPa18vkJAKE7RXOKfVHqMZZ1WlTjiwl6Gcn65M5vpNUB6EFLnEdRdEXQ==",
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz",
|
||||
"integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@vitest/expect": "1.3.1",
|
||||
"@vitest/runner": "1.3.1",
|
||||
"@vitest/snapshot": "1.3.1",
|
||||
"@vitest/spy": "1.3.1",
|
||||
"@vitest/utils": "1.3.1",
|
||||
"@vitest/expect": "1.4.0",
|
||||
"@vitest/runner": "1.4.0",
|
||||
"@vitest/snapshot": "1.4.0",
|
||||
"@vitest/spy": "1.4.0",
|
||||
"@vitest/utils": "1.4.0",
|
||||
"acorn-walk": "^8.3.2",
|
||||
"chai": "^4.3.10",
|
||||
"debug": "^4.3.4",
|
||||
|
@ -12011,7 +11998,7 @@
|
|||
"tinybench": "^2.5.1",
|
||||
"tinypool": "^0.8.2",
|
||||
"vite": "^5.0.0",
|
||||
"vite-node": "1.3.1",
|
||||
"vite-node": "1.4.0",
|
||||
"why-is-node-running": "^2.2.2"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -12026,8 +12013,8 @@
|
|||
"peerDependencies": {
|
||||
"@edge-runtime/vm": "*",
|
||||
"@types/node": "^18.0.0 || >=20.0.0",
|
||||
"@vitest/browser": "1.3.1",
|
||||
"@vitest/ui": "1.3.1",
|
||||
"@vitest/browser": "1.4.0",
|
||||
"@vitest/ui": "1.4.0",
|
||||
"happy-dom": "*",
|
||||
"jsdom": "*"
|
||||
},
|
||||
|
@ -12186,25 +12173,25 @@
|
|||
}
|
||||
},
|
||||
"node_modules/webpack": {
|
||||
"version": "5.90.3",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.90.3.tgz",
|
||||
"integrity": "sha512-h6uDYlWCctQRuXBs1oYpVe6sFcWedl0dpcVaTf/YF67J9bKvwJajFulMVSYKHrksMB3I/pIagRzDxwxkebuzKA==",
|
||||
"version": "5.91.0",
|
||||
"resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz",
|
||||
"integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==",
|
||||
"dependencies": {
|
||||
"@types/eslint-scope": "^3.7.3",
|
||||
"@types/estree": "^1.0.5",
|
||||
"@webassemblyjs/ast": "^1.11.5",
|
||||
"@webassemblyjs/wasm-edit": "^1.11.5",
|
||||
"@webassemblyjs/wasm-parser": "^1.11.5",
|
||||
"@webassemblyjs/ast": "^1.12.1",
|
||||
"@webassemblyjs/wasm-edit": "^1.12.1",
|
||||
"@webassemblyjs/wasm-parser": "^1.12.1",
|
||||
"acorn": "^8.7.1",
|
||||
"acorn-import-assertions": "^1.9.0",
|
||||
"browserslist": "^4.21.10",
|
||||
"chrome-trace-event": "^1.0.2",
|
||||
"enhanced-resolve": "^5.15.0",
|
||||
"enhanced-resolve": "^5.16.0",
|
||||
"es-module-lexer": "^1.2.1",
|
||||
"eslint-scope": "5.1.1",
|
||||
"events": "^3.2.0",
|
||||
"glob-to-regexp": "^0.4.1",
|
||||
"graceful-fs": "^4.2.9",
|
||||
"graceful-fs": "^4.2.11",
|
||||
"json-parse-even-better-errors": "^2.3.1",
|
||||
"loader-runner": "^4.2.0",
|
||||
"mime-types": "^2.1.27",
|
||||
|
@ -12212,7 +12199,7 @@
|
|||
"schema-utils": "^3.2.0",
|
||||
"tapable": "^2.1.1",
|
||||
"terser-webpack-plugin": "^5.3.10",
|
||||
"watchpack": "^2.4.0",
|
||||
"watchpack": "^2.4.1",
|
||||
"webpack-sources": "^3.2.3"
|
||||
},
|
||||
"bin": {
|
||||
|
|
26
package.json
26
package.json
|
@ -10,10 +10,10 @@
|
|||
"@citation-js/plugin-software-formats": "0.6.1",
|
||||
"@claviska/jquery-minicolors": "2.3.6",
|
||||
"@github/markdown-toolbar-element": "2.2.3",
|
||||
"@github/relative-time-element": "4.3.1",
|
||||
"@github/relative-time-element": "4.4.0",
|
||||
"@github/text-expander-element": "2.6.1",
|
||||
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
|
||||
"@primer/octicons": "19.8.0",
|
||||
"@primer/octicons": "19.9.0",
|
||||
"add-asset-webpack-plugin": "2.0.1",
|
||||
"ansi_up": "6.0.2",
|
||||
"asciinema-player": "3.7.1",
|
||||
|
@ -39,7 +39,7 @@
|
|||
"monaco-editor": "0.47.0",
|
||||
"monaco-editor-webpack-plugin": "7.1.0",
|
||||
"pdfobject": "2.3.0",
|
||||
"postcss": "8.4.35",
|
||||
"postcss": "8.4.38",
|
||||
"postcss-loader": "8.1.1",
|
||||
"postcss-nesting": "12.1.0",
|
||||
"pretty-ms": "9.0.0",
|
||||
|
@ -58,7 +58,7 @@
|
|||
"vue-chartjs": "5.3.0",
|
||||
"vue-loader": "17.4.2",
|
||||
"vue3-calendar-heatmap": "2.0.5",
|
||||
"webpack": "5.90.3",
|
||||
"webpack": "5.91.0",
|
||||
"webpack-cli": "5.1.4",
|
||||
"wrap-ansi": "9.0.0"
|
||||
},
|
||||
|
@ -76,24 +76,24 @@
|
|||
"eslint-plugin-jquery": "1.5.1",
|
||||
"eslint-plugin-no-jquery": "2.7.0",
|
||||
"eslint-plugin-no-use-extend-native": "0.5.0",
|
||||
"eslint-plugin-regexp": "2.3.0",
|
||||
"eslint-plugin-regexp": "2.4.0",
|
||||
"eslint-plugin-sonarjs": "0.24.0",
|
||||
"eslint-plugin-unicorn": "51.0.1",
|
||||
"eslint-plugin-vitest": "0.3.26",
|
||||
"eslint-plugin-vitest-globals": "1.4.0",
|
||||
"eslint-plugin-vue": "9.23.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.7.2",
|
||||
"eslint-plugin-vitest": "0.4.0",
|
||||
"eslint-plugin-vitest-globals": "1.5.0",
|
||||
"eslint-plugin-vue": "9.24.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.8.0",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"happy-dom": "14.2.0",
|
||||
"happy-dom": "14.3.7",
|
||||
"markdownlint-cli": "0.39.0",
|
||||
"postcss-html": "1.6.0",
|
||||
"stylelint": "16.2.1",
|
||||
"stylelint": "16.3.0",
|
||||
"stylelint-declaration-block-no-ignored-properties": "2.8.0",
|
||||
"stylelint-declaration-strict-value": "1.10.4",
|
||||
"svgo": "3.2.0",
|
||||
"updates": "15.3.1",
|
||||
"updates": "16.0.0",
|
||||
"vite-string-plugin": "1.1.5",
|
||||
"vitest": "1.3.1"
|
||||
"vitest": "1.4.0"
|
||||
},
|
||||
"browserslist": [
|
||||
"defaults"
|
||||
|
|
9
poetry.lock
generated
9
poetry.lock
generated
|
@ -113,18 +113,15 @@ six = ">=1.13.0"
|
|||
|
||||
[[package]]
|
||||
name = "json5"
|
||||
version = "0.9.18"
|
||||
version = "0.9.24"
|
||||
description = "A Python implementation of the JSON5 data format."
|
||||
optional = false
|
||||
python-versions = ">=3.8"
|
||||
files = [
|
||||
{file = "json5-0.9.18-py2.py3-none-any.whl", hash = "sha256:3f20193ff8dfdec6ab114b344e7ac5d76fac453c8bab9bdfe1460d1d528ec393"},
|
||||
{file = "json5-0.9.18.tar.gz", hash = "sha256:ecb8ac357004e3522fb989da1bf08b146011edbd14fdffae6caad3bd68493467"},
|
||||
{file = "json5-0.9.24-py3-none-any.whl", hash = "sha256:4ca101fd5c7cb47960c055ef8f4d0e31e15a7c6c48c3b6f1473fc83b6c462a13"},
|
||||
{file = "json5-0.9.24.tar.gz", hash = "sha256:0c638399421da959a20952782800e5c1a78c14e08e1dc9738fa10d8ec14d58c8"},
|
||||
]
|
||||
|
||||
[package.extras]
|
||||
dev = ["hypothesis"]
|
||||
|
||||
[[package]]
|
||||
name = "pathspec"
|
||||
version = "0.12.1"
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -18,8 +19,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
packageNameRegex = regexp.MustCompile(`\A[A-Za-z0-9\.\_\-\+]+\z`)
|
||||
filenameRegex = packageNameRegex
|
||||
packageNameRegex = regexp.MustCompile(`\A[-_+.\w]+\z`)
|
||||
filenameRegex = regexp.MustCompile(`\A[-_+=:;.()\[\]{}~!@#$%^& \w]+\z`)
|
||||
)
|
||||
|
||||
func apiError(ctx *context.Context, status int, obj any) {
|
||||
|
@ -54,20 +55,38 @@ func DownloadPackageFile(ctx *context.Context) {
|
|||
helper.ServePackageFile(ctx, s, u, pf)
|
||||
}
|
||||
|
||||
func isValidPackageName(packageName string) bool {
|
||||
if len(packageName) == 1 && !unicode.IsLetter(rune(packageName[0])) && !unicode.IsNumber(rune(packageName[0])) {
|
||||
return false
|
||||
}
|
||||
return packageNameRegex.MatchString(packageName) && packageName != ".."
|
||||
}
|
||||
|
||||
func isValidFileName(filename string) bool {
|
||||
return filenameRegex.MatchString(filename) &&
|
||||
strings.TrimSpace(filename) == filename &&
|
||||
filename != "." && filename != ".."
|
||||
}
|
||||
|
||||
// UploadPackage uploads the specific generic package.
|
||||
// Duplicated packages get rejected.
|
||||
func UploadPackage(ctx *context.Context) {
|
||||
packageName := ctx.Params("packagename")
|
||||
filename := ctx.Params("filename")
|
||||
|
||||
if !packageNameRegex.MatchString(packageName) || !filenameRegex.MatchString(filename) {
|
||||
apiError(ctx, http.StatusBadRequest, errors.New("Invalid package name or filename"))
|
||||
if !isValidPackageName(packageName) {
|
||||
apiError(ctx, http.StatusBadRequest, errors.New("invalid package name"))
|
||||
return
|
||||
}
|
||||
|
||||
if !isValidFileName(filename) {
|
||||
apiError(ctx, http.StatusBadRequest, errors.New("invalid filename"))
|
||||
return
|
||||
}
|
||||
|
||||
packageVersion := ctx.Params("packageversion")
|
||||
if packageVersion != strings.TrimSpace(packageVersion) {
|
||||
apiError(ctx, http.StatusBadRequest, errors.New("Invalid package version"))
|
||||
apiError(ctx, http.StatusBadRequest, errors.New("invalid package version"))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
65
routers/api/packages/generic/generic_test.go
Normal file
65
routers/api/packages/generic/generic_test.go
Normal file
|
@ -0,0 +1,65 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package generic
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestValidatePackageName(t *testing.T) {
|
||||
bad := []string{
|
||||
"",
|
||||
".",
|
||||
"..",
|
||||
"-",
|
||||
"a?b",
|
||||
"a b",
|
||||
"a/b",
|
||||
}
|
||||
for _, name := range bad {
|
||||
assert.False(t, isValidPackageName(name), "bad=%q", name)
|
||||
}
|
||||
|
||||
good := []string{
|
||||
"a",
|
||||
"1",
|
||||
"a-",
|
||||
"a_b",
|
||||
"c.d+",
|
||||
}
|
||||
for _, name := range good {
|
||||
assert.True(t, isValidPackageName(name), "good=%q", name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateFileName(t *testing.T) {
|
||||
bad := []string{
|
||||
"",
|
||||
".",
|
||||
"..",
|
||||
"a?b",
|
||||
"a/b",
|
||||
" a",
|
||||
"a ",
|
||||
}
|
||||
for _, name := range bad {
|
||||
assert.False(t, isValidFileName(name), "bad=%q", name)
|
||||
}
|
||||
|
||||
good := []string{
|
||||
"-",
|
||||
"a",
|
||||
"1",
|
||||
"a-",
|
||||
"a_b",
|
||||
"a b",
|
||||
"c.d+",
|
||||
`-_+=:;.()[]{}~!@#$%^& aA1`,
|
||||
}
|
||||
for _, name := range good {
|
||||
assert.True(t, isValidFileName(name), "good=%q", name)
|
||||
}
|
||||
}
|
|
@ -150,7 +150,7 @@ func GetRawFileOrLFS(ctx *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
// OK, now the blob is known to have at most 1024 bytes we can simply read this in in one go (This saves reading it twice)
|
||||
// OK, now the blob is known to have at most 1024 bytes we can simply read this in one go (This saves reading it twice)
|
||||
dataRc, err := blob.DataAsync()
|
||||
if err != nil {
|
||||
ctx.ServerError("DataAsync", err)
|
||||
|
|
|
@ -787,6 +787,8 @@ func DeleteReviewRequests(ctx *context.APIContext) {
|
|||
// "$ref": "#/responses/empty"
|
||||
// "422":
|
||||
// "$ref": "#/responses/validationError"
|
||||
// "403":
|
||||
// "$ref": "#/responses/forbidden"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
opts := web.GetForm(ctx).(*api.PullReviewRequestOptions)
|
||||
|
@ -855,6 +857,10 @@ func apiReviewRequest(ctx *context.APIContext, opts api.PullReviewRequestOptions
|
|||
for _, reviewer := range reviewers {
|
||||
comment, err := issue_service.ReviewRequest(ctx, pr.Issue, ctx.Doer, reviewer, isAdd)
|
||||
if err != nil {
|
||||
if issues_model.IsErrReviewRequestOnClosedPR(err) {
|
||||
ctx.Error(http.StatusForbidden, "", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "ReviewRequest", err)
|
||||
return
|
||||
}
|
||||
|
@ -1068,7 +1074,7 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors
|
|||
ctx.Error(http.StatusForbidden, "", "Must be repo admin")
|
||||
return
|
||||
}
|
||||
review, pr, isWrong := prepareSingleReview(ctx)
|
||||
review, _, isWrong := prepareSingleReview(ctx)
|
||||
if isWrong {
|
||||
return
|
||||
}
|
||||
|
@ -1078,13 +1084,12 @@ func dismissReview(ctx *context.APIContext, msg string, isDismiss, dismissPriors
|
|||
return
|
||||
}
|
||||
|
||||
if pr.Issue.IsClosed {
|
||||
ctx.Error(http.StatusForbidden, "", "not need to dismiss this review because this pr is closed")
|
||||
return
|
||||
}
|
||||
|
||||
_, err := pull_service.DismissReview(ctx, review.ID, ctx.Repo.Repository.ID, msg, ctx.Doer, isDismiss, dismissPriors)
|
||||
if err != nil {
|
||||
if pull_service.IsErrDismissRequestOnClosedPR(err) {
|
||||
ctx.Error(http.StatusForbidden, "", err)
|
||||
return
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "pull_service.DismissReview", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -207,11 +207,7 @@ func ChangeProjectStatus(ctx *context.Context) {
|
|||
id := ctx.ParamsInt64(":id")
|
||||
|
||||
if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, 0, id, toClose); err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", err)
|
||||
} else {
|
||||
ctx.ServerError("ChangeProjectStatusByRepoIDAndID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action")))
|
||||
|
@ -221,11 +217,7 @@ func ChangeProjectStatus(ctx *context.Context) {
|
|||
func DeleteProject(ctx *context.Context) {
|
||||
p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
if p.OwnerID != ctx.ContextUser.ID {
|
||||
|
@ -254,11 +246,7 @@ func RenderEditProject(ctx *context.Context) {
|
|||
|
||||
p, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
if p.OwnerID != ctx.ContextUser.ID {
|
||||
|
@ -303,11 +291,7 @@ func EditProjectPost(ctx *context.Context) {
|
|||
|
||||
p, err := project_model.GetProjectByID(ctx, projectID)
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
if p.OwnerID != ctx.ContextUser.ID {
|
||||
|
@ -335,11 +319,7 @@ func EditProjectPost(ctx *context.Context) {
|
|||
func ViewProject(ctx *context.Context) {
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
if project.OwnerID != ctx.ContextUser.ID {
|
||||
|
@ -353,10 +333,6 @@ func ViewProject(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if boards[0].ID == 0 {
|
||||
boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized")
|
||||
}
|
||||
|
||||
issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadIssuesOfBoards", err)
|
||||
|
@ -493,11 +469,7 @@ func DeleteProjectBoard(ctx *context.Context) {
|
|||
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -534,11 +506,7 @@ func AddBoardToProjectPost(ctx *context.Context) {
|
|||
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -566,11 +534,7 @@ func CheckProjectBoardChangePermissions(ctx *context.Context) (*project_model.Pr
|
|||
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -636,21 +600,6 @@ func SetDefaultProjectBoard(ctx *context.Context) {
|
|||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
// UnsetDefaultProjectBoard unset default board for uncategorized issues/pulls
|
||||
func UnsetDefaultProjectBoard(ctx *context.Context) {
|
||||
project, _ := CheckProjectBoardChangePermissions(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.SetDefaultBoard(ctx, project.ID, 0); err != nil {
|
||||
ctx.ServerError("SetDefaultBoard", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
// MoveIssues moves or keeps issues in a column and sorts them inside that column
|
||||
func MoveIssues(ctx *context.Context) {
|
||||
if ctx.Doer == nil {
|
||||
|
@ -662,11 +611,7 @@ func MoveIssues(ctx *context.Context) {
|
|||
|
||||
project, err := project_model.GetProjectByID(ctx, ctx.ParamsInt64(":id"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectNotExist(err) {
|
||||
ctx.NotFound("ProjectNotExist", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetProjectByID", project_model.IsErrProjectNotExist, err)
|
||||
return
|
||||
}
|
||||
if project.OwnerID != ctx.ContextUser.ID {
|
||||
|
@ -674,28 +619,15 @@ func MoveIssues(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var board *project_model.Board
|
||||
board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
|
||||
if err != nil {
|
||||
ctx.NotFoundOrServerError("GetProjectBoard", project_model.IsErrProjectBoardNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.ParamsInt64(":boardID") == 0 {
|
||||
board = &project_model.Board{
|
||||
ID: 0,
|
||||
ProjectID: project.ID,
|
||||
Title: ctx.Locale.TrString("repo.projects.type.uncategorized"),
|
||||
}
|
||||
} else {
|
||||
board, err = project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectBoardNotExist(err) {
|
||||
ctx.NotFound("ProjectBoardNotExist", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectBoard", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if board.ProjectID != project.ID {
|
||||
ctx.NotFound("BoardNotInProject", nil)
|
||||
return
|
||||
}
|
||||
if board.ProjectID != project.ID {
|
||||
ctx.NotFound("BoardNotInProject", nil)
|
||||
return
|
||||
}
|
||||
|
||||
type movedIssuesForm struct {
|
||||
|
@ -718,11 +650,7 @@ func MoveIssues(ctx *context.Context) {
|
|||
}
|
||||
movedIssues, err := issues_model.GetIssuesByIDs(ctx, issueIDs)
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound("IssueNotExisting", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetIssueByID", err)
|
||||
}
|
||||
ctx.NotFoundOrServerError("GetIssueByID", issues_model.IsErrIssueNotExist, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -260,7 +260,7 @@ func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames
|
|||
if commit.User != nil {
|
||||
avatar = string(avatarUtils.Avatar(commit.User, 18))
|
||||
} else {
|
||||
avatar = string(avatarUtils.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "gt-mr-3"))
|
||||
avatar = string(avatarUtils.AvatarByEmail(commit.Author.Email, commit.Author.Name, 18, "tw-mr-2"))
|
||||
}
|
||||
|
||||
br.Avatar = gotemplate.HTML(avatar)
|
||||
|
|
|
@ -1600,7 +1600,7 @@ func ViewIssue(ctx *context.Context) {
|
|||
}
|
||||
marked[issue.PosterID] = issue.ShowRole
|
||||
|
||||
// Render comments and and fetch participants.
|
||||
// Render comments and fetch participants.
|
||||
participants[0] = issue.Poster
|
||||
for _, comment = range issue.Comments {
|
||||
comment.Issue = issue
|
||||
|
@ -2494,6 +2494,10 @@ func UpdatePullReviewRequest(ctx *context.Context) {
|
|||
|
||||
_, err = issue_service.ReviewRequest(ctx, issue, ctx.Doer, reviewer, action == "attach")
|
||||
if err != nil {
|
||||
if issues_model.IsErrReviewRequestOnClosedPR(err) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
ctx.ServerError("ReviewRequest", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -70,7 +70,7 @@ func GetContentHistoryList(ctx *context.Context) {
|
|||
}
|
||||
|
||||
src := html.EscapeString(item.UserAvatarLink)
|
||||
class := avatars.DefaultAvatarClass + " gt-mr-3"
|
||||
class := avatars.DefaultAvatarClass + " tw-mr-2"
|
||||
name := html.EscapeString(username)
|
||||
avatarHTML := string(templates.AvatarHTML(src, 28, class, username))
|
||||
timeSinceText := string(timeutil.TimeSinceUnix(item.EditedUnix, ctx.Locale))
|
||||
|
|
|
@ -314,10 +314,6 @@ func ViewProject(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if boards[0].ID == 0 {
|
||||
boards[0].Title = ctx.Locale.TrString("repo.projects.type.uncategorized")
|
||||
}
|
||||
|
||||
issuesMap, err := issues_model.LoadIssuesFromBoardList(ctx, boards)
|
||||
if err != nil {
|
||||
ctx.ServerError("LoadIssuesOfBoards", err)
|
||||
|
@ -582,21 +578,6 @@ func SetDefaultProjectBoard(ctx *context.Context) {
|
|||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
// UnSetDefaultProjectBoard unset default board for uncategorized issues/pulls
|
||||
func UnSetDefaultProjectBoard(ctx *context.Context) {
|
||||
project, _ := checkProjectBoardChangePermissions(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if err := project_model.SetDefaultBoard(ctx, project.ID, 0); err != nil {
|
||||
ctx.ServerError("SetDefaultBoard", err)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSONOK()
|
||||
}
|
||||
|
||||
// MoveIssues moves or keeps issues in a column and sorts them inside that column
|
||||
func MoveIssues(ctx *context.Context) {
|
||||
if ctx.Doer == nil {
|
||||
|
@ -627,28 +608,19 @@ func MoveIssues(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
var board *project_model.Board
|
||||
board, err := project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectBoardNotExist(err) {
|
||||
ctx.NotFound("ProjectBoardNotExist", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectBoard", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if ctx.ParamsInt64(":boardID") == 0 {
|
||||
board = &project_model.Board{
|
||||
ID: 0,
|
||||
ProjectID: project.ID,
|
||||
Title: ctx.Locale.TrString("repo.projects.type.uncategorized"),
|
||||
}
|
||||
} else {
|
||||
board, err = project_model.GetBoard(ctx, ctx.ParamsInt64(":boardID"))
|
||||
if err != nil {
|
||||
if project_model.IsErrProjectBoardNotExist(err) {
|
||||
ctx.NotFound("ProjectBoardNotExist", nil)
|
||||
} else {
|
||||
ctx.ServerError("GetProjectBoard", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if board.ProjectID != project.ID {
|
||||
ctx.NotFound("BoardNotInProject", nil)
|
||||
return
|
||||
}
|
||||
if board.ProjectID != project.ID {
|
||||
ctx.NotFound("BoardNotInProject", nil)
|
||||
return
|
||||
}
|
||||
|
||||
type movedIssuesForm struct {
|
||||
|
|
|
@ -263,6 +263,10 @@ func DismissReview(ctx *context.Context) {
|
|||
form := web.GetForm(ctx).(*forms.DismissReviewForm)
|
||||
comm, err := pull_service.DismissReview(ctx, form.ReviewID, ctx.Repo.Repository.ID, form.Message, ctx.Doer, true, true)
|
||||
if err != nil {
|
||||
if pull_service.IsErrDismissRequestOnClosedPR(err) {
|
||||
ctx.Status(http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
ctx.ServerError("pull_service.DismissReview", err)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ import (
|
|||
|
||||
"code.gitea.io/gitea/modules/charset"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/typesniffer"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
@ -44,20 +45,17 @@ func RenderFile(ctx *context.Context) {
|
|||
isTextFile := st.IsText()
|
||||
|
||||
rd := charset.ToUTF8WithFallbackReader(io.MultiReader(bytes.NewReader(buf), dataRc), charset.ConvertOpts{})
|
||||
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
||||
|
||||
if markupType := markup.Type(blob.Name()); markupType == "" {
|
||||
if isTextFile {
|
||||
_, err = io.Copy(ctx.Resp, rd)
|
||||
if err != nil {
|
||||
ctx.ServerError("Copy", err)
|
||||
}
|
||||
return
|
||||
_, _ = io.Copy(ctx.Resp, rd)
|
||||
} else {
|
||||
http.Error(ctx.Resp, "Unsupported file type render", http.StatusInternalServerError)
|
||||
}
|
||||
ctx.Error(http.StatusInternalServerError, "Unsupported file type render")
|
||||
return
|
||||
}
|
||||
|
||||
ctx.Resp.Header().Add("Content-Security-Policy", "frame-src 'self'; sandbox allow-scripts")
|
||||
err = markup.Render(&markup.RenderContext{
|
||||
Ctx: ctx,
|
||||
RelativePath: ctx.Repo.TreePath,
|
||||
|
@ -71,7 +69,8 @@ func RenderFile(ctx *context.Context) {
|
|||
InStandalonePage: true,
|
||||
}, rd, ctx.Resp)
|
||||
if err != nil {
|
||||
ctx.ServerError("Render", err)
|
||||
log.Error("Failed to render file %q: %v", ctx.Repo.TreePath, err)
|
||||
http.Error(ctx.Resp, "Failed to render file", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
|
@ -938,9 +938,9 @@ func prepareOpenWithEditorApps(ctx *context.Context) {
|
|||
schema, _, _ := strings.Cut(app.OpenURL, ":")
|
||||
var iconHTML template.HTML
|
||||
if schema == "vscode" || schema == "vscodium" || schema == "jetbrains" {
|
||||
iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-open-with-%s", schema), 16, "gt-mr-3")
|
||||
iconHTML = svg.RenderHTML(fmt.Sprintf("gitea-open-with-%s", schema), 16, "tw-mr-2")
|
||||
} else {
|
||||
iconHTML = svg.RenderHTML("gitea-git", 16, "gt-mr-3") // TODO: it could support user's customized icon in the future
|
||||
iconHTML = svg.RenderHTML("gitea-git", 16, "tw-mr-2") // TODO: it could support user's customized icon in the future
|
||||
}
|
||||
tmplApps = append(tmplApps, map[string]any{
|
||||
"DisplayName": app.DisplayName,
|
||||
|
|
|
@ -986,7 +986,6 @@ func registerRoutes(m *web.Route) {
|
|||
m.Put("", web.Bind(forms.EditProjectBoardForm{}), org.EditProjectBoard)
|
||||
m.Delete("", org.DeleteProjectBoard)
|
||||
m.Post("/default", org.SetDefaultProjectBoard)
|
||||
m.Post("/unsetdefault", org.UnsetDefaultProjectBoard)
|
||||
|
||||
m.Post("/move", org.MoveIssues)
|
||||
})
|
||||
|
@ -1360,7 +1359,6 @@ func registerRoutes(m *web.Route) {
|
|||
m.Put("", web.Bind(forms.EditProjectBoardForm{}), repo.EditProjectBoard)
|
||||
m.Delete("", repo.DeleteProjectBoard)
|
||||
m.Post("/default", repo.SetDefaultProjectBoard)
|
||||
m.Post("/unsetdefault", repo.UnSetDefaultProjectBoard)
|
||||
|
||||
m.Post("/move", repo.MoveIssues)
|
||||
})
|
||||
|
|
|
@ -59,7 +59,7 @@ func (p *AuthSourceProvider) DisplayName() string {
|
|||
|
||||
func (p *AuthSourceProvider) IconHTML(size int) template.HTML {
|
||||
if p.iconURL != "" {
|
||||
img := fmt.Sprintf(`<img class="tw-object-contain gt-mr-3" width="%d" height="%d" src="%s" alt="%s">`,
|
||||
img := fmt.Sprintf(`<img class="tw-object-contain tw-mr-2" width="%d" height="%d" src="%s" alt="%s">`,
|
||||
size,
|
||||
size,
|
||||
html.EscapeString(p.iconURL), html.EscapeString(p.DisplayName()),
|
||||
|
|
|
@ -35,10 +35,10 @@ func (b *BaseProvider) IconHTML(size int) template.HTML {
|
|||
case "github":
|
||||
svgName = "octicon-mark-github"
|
||||
}
|
||||
svgHTML := svg.RenderHTML(svgName, size, "gt-mr-3")
|
||||
svgHTML := svg.RenderHTML(svgName, size, "tw-mr-2")
|
||||
if svgHTML == "" {
|
||||
log.Error("No SVG icon for oauth2 provider %q", b.name)
|
||||
svgHTML = svg.RenderHTML("gitea-openid", size, "gt-mr-3")
|
||||
svgHTML = svg.RenderHTML("gitea-openid", size, "tw-mr-2")
|
||||
}
|
||||
return svgHTML
|
||||
}
|
||||
|
|
|
@ -29,7 +29,7 @@ func (o *OpenIDProvider) DisplayName() string {
|
|||
|
||||
// IconHTML returns icon HTML for this provider
|
||||
func (o *OpenIDProvider) IconHTML(size int) template.HTML {
|
||||
return svg.RenderHTML("gitea-openid", size, "gt-mr-3")
|
||||
return svg.RenderHTML("gitea-openid", size, "tw-mr-2")
|
||||
}
|
||||
|
||||
// CreateGothProvider creates a GothProvider from this Provider
|
||||
|
|
|
@ -216,6 +216,12 @@ func fixBrokenRepoUnit16961(repoUnit *repo_model.RepoUnit, bs []byte) (fixed boo
|
|||
return false, nil
|
||||
}
|
||||
|
||||
var cfg any
|
||||
err = json.UnmarshalHandleDoubleEncode(bs, &cfg)
|
||||
if err == nil {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
switch repoUnit.Type {
|
||||
case unit.TypeCode, unit.TypeReleases, unit.TypeWiki, unit.TypeProjects:
|
||||
cfg := &repo_model.UnitConfig{}
|
||||
|
|
|
@ -162,7 +162,7 @@ func checkStorage(opts *checkStorageOptions) func(ctx context.Context, logger lo
|
|||
if opts.RepoArchives || opts.All {
|
||||
if err := commonCheckStorage(ctx, logger, autofix,
|
||||
&commonStorageCheckOptions{
|
||||
storer: storage.RepoAvatars,
|
||||
storer: storage.RepoArchives,
|
||||
isOrphaned: func(path string, obj storage.Object, stat fs.FileInfo) (bool, error) {
|
||||
exists, err := repo.ExistsRepoArchiverWithStoragePath(ctx, path)
|
||||
if err == nil || errors.Is(err, util.ErrInvalidArgument) {
|
||||
|
|
|
@ -257,14 +257,13 @@ func migrateRepository(ctx context.Context, doer *user_model.User, downloader ba
|
|||
}
|
||||
log.Warn("migrating milestones is not supported, ignored")
|
||||
}
|
||||
|
||||
msBatchSize := uploader.MaxBatchInsertSize("milestone")
|
||||
for len(milestones) > 0 {
|
||||
if len(milestones) < msBatchSize {
|
||||
msBatchSize = len(milestones)
|
||||
}
|
||||
|
||||
if err := uploader.CreateMilestones(milestones...); err != nil {
|
||||
if err := uploader.CreateMilestones(milestones[:msBatchSize]...); err != nil {
|
||||
return err
|
||||
}
|
||||
milestones = milestones[msBatchSize:]
|
||||
|
|
|
@ -20,11 +20,29 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
notify_service "code.gitea.io/gitea/services/notify"
|
||||
)
|
||||
|
||||
var notEnoughLines = regexp.MustCompile(`fatal: file .* has only \d+ lines?`)
|
||||
|
||||
// ErrDismissRequestOnClosedPR represents an error when an user tries to dismiss a review associated to a closed or merged PR.
|
||||
type ErrDismissRequestOnClosedPR struct{}
|
||||
|
||||
// IsErrDismissRequestOnClosedPR checks if an error is an ErrDismissRequestOnClosedPR.
|
||||
func IsErrDismissRequestOnClosedPR(err error) bool {
|
||||
_, ok := err.(ErrDismissRequestOnClosedPR)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (err ErrDismissRequestOnClosedPR) Error() string {
|
||||
return "can't dismiss a review associated to a closed or merged PR"
|
||||
}
|
||||
|
||||
func (err ErrDismissRequestOnClosedPR) Unwrap() error {
|
||||
return util.ErrPermissionDenied
|
||||
}
|
||||
|
||||
// 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 {
|
||||
|
@ -382,6 +400,21 @@ func DismissReview(ctx context.Context, reviewID, repoID int64, message string,
|
|||
return nil, fmt.Errorf("reviews's repository is not the same as the one we expect")
|
||||
}
|
||||
|
||||
issue := review.Issue
|
||||
|
||||
if issue.IsClosed {
|
||||
return nil, ErrDismissRequestOnClosedPR{}
|
||||
}
|
||||
|
||||
if issue.IsPull {
|
||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if issue.PullRequest.HasMerged {
|
||||
return nil, ErrDismissRequestOnClosedPR{}
|
||||
}
|
||||
}
|
||||
|
||||
if err := issues_model.DismissReview(ctx, review, isDismiss); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
48
services/pull/review_test.go
Normal file
48
services/pull/review_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package pull_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
"code.gitea.io/gitea/models/unittest"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDismissReview(t *testing.T) {
|
||||
assert.NoError(t, unittest.PrepareTestDatabase())
|
||||
|
||||
pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{})
|
||||
assert.NoError(t, pull.LoadIssue(db.DefaultContext))
|
||||
issue := pull.Issue
|
||||
assert.NoError(t, issue.LoadRepo(db.DefaultContext))
|
||||
reviewer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1})
|
||||
review, err := issues_model.CreateReview(db.DefaultContext, issues_model.CreateReviewOptions{
|
||||
Issue: issue,
|
||||
Reviewer: reviewer,
|
||||
Type: issues_model.ReviewTypeReject,
|
||||
})
|
||||
|
||||
assert.NoError(t, err)
|
||||
issue.IsClosed = true
|
||||
pull.HasMerged = false
|
||||
assert.NoError(t, issues_model.UpdateIssueCols(db.DefaultContext, issue, "is_closed"))
|
||||
assert.NoError(t, pull.UpdateCols(db.DefaultContext, "has_merged"))
|
||||
_, err = pull_service.DismissReview(db.DefaultContext, review.ID, issue.RepoID, "", &user_model.User{}, false, false)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, pull_service.IsErrDismissRequestOnClosedPR(err))
|
||||
|
||||
pull.HasMerged = true
|
||||
pull.Issue.IsClosed = false
|
||||
assert.NoError(t, issues_model.UpdateIssueCols(db.DefaultContext, issue, "is_closed"))
|
||||
assert.NoError(t, pull.UpdateCols(db.DefaultContext, "has_merged"))
|
||||
_, err = pull_service.DismissReview(db.DefaultContext, review.ID, issue.RepoID, "", &user_model.User{}, false, false)
|
||||
assert.Error(t, err)
|
||||
assert.True(t, pull_service.IsErrDismissRequestOnClosedPR(err))
|
||||
}
|
|
@ -113,7 +113,6 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) {
|
|||
err := hook.SetHeaderAuthorization("Bearer s3cr3t-t0ken")
|
||||
assert.NoError(t, err)
|
||||
assert.NoError(t, webhook_model.CreateWebhook(db.DefaultContext, hook))
|
||||
db.GetEngine(db.DefaultContext).NoAutoTime().DB().Logger.ShowSQL(true)
|
||||
|
||||
hookTask := &webhook_model.HookTask{
|
||||
HookID: hook.ID,
|
||||
|
|
|
@ -41,6 +41,8 @@ export default {
|
|||
// classes that don't work without CSS variables from "@tailwind base" which we don't use
|
||||
'transform', 'shadow', 'ring', 'blur', 'grayscale', 'invert', '!invert', 'filter', '!filter',
|
||||
'backdrop-filter',
|
||||
// we use double-class tw-hidden defined in web_src/css/helpers.css for increased specificity
|
||||
'hidden',
|
||||
// unneeded classes
|
||||
'[-a-zA-Z:0-9_.]',
|
||||
],
|
||||
|
@ -66,6 +68,10 @@ export default {
|
|||
'3xl': '24px',
|
||||
'full': 'var(--border-radius-circle)', // 50%
|
||||
},
|
||||
fontFamily: {
|
||||
sans: 'var(--fonts-regular)',
|
||||
mono: 'var(--fonts-monospace)',
|
||||
},
|
||||
fontWeight: {
|
||||
light: 'var(--font-weight-light)',
|
||||
normal: 'var(--font-weight-normal)',
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<label for="port">{{ctx.Locale.Tr "admin.auths.port"}}</label>
|
||||
<input id="port" name="port" value="{{$cfg.Port}}" placeholder="636" required>
|
||||
</div>
|
||||
<div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}">
|
||||
<div class="has-tls inline field {{if not .HasTLS}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{ctx.Locale.Tr "admin.auths.skip_tls_verify"}}</strong></label>
|
||||
<input name="skip_verify" type="checkbox" {{if .Source.SkipVerify}}checked{{end}}>
|
||||
|
@ -113,7 +113,7 @@
|
|||
<input type="checkbox" name="groups_enabled" class="js-ldap-group-toggle" {{if $cfg.GroupsEnabled}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ldap-group-options" class="ui segment secondary {{if not $cfg.GroupsEnabled}}gt-hidden{{end}}">
|
||||
<div id="ldap-group-options" class="ui segment secondary {{if not $cfg.GroupsEnabled}}tw-hidden{{end}}">
|
||||
<div class="field">
|
||||
<label>{{ctx.Locale.Tr "admin.auths.group_search_base"}}</label>
|
||||
<input name="group_dn" value="{{$cfg.GroupDN}}" placeholder="ou=group,dc=mydomain,dc=com">
|
||||
|
@ -148,7 +148,7 @@
|
|||
<input id="use_paged_search" name="use_paged_search" type="checkbox" {{if $cfg.UsePagedSearch}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field required search-page-size{{if not $cfg.UsePagedSearch}} gt-hidden{{end}}">
|
||||
<div class="field required search-page-size{{if not $cfg.UsePagedSearch}} tw-hidden{{end}}">
|
||||
<label for="search_page_size">{{ctx.Locale.Tr "admin.auths.search_page_size"}}</label>
|
||||
<input id="search_page_size" name="search_page_size" value="{{if $cfg.UsePagedSearch}}{{$cfg.SearchPageSize}}{{end}}">
|
||||
</div>
|
||||
|
@ -205,7 +205,7 @@
|
|||
</div>
|
||||
<p class="help">{{ctx.Locale.Tr "admin.auths.force_smtps_helper"}}</p>
|
||||
</div>
|
||||
<div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}">
|
||||
<div class="has-tls inline field {{if not .HasTLS}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{ctx.Locale.Tr "admin.auths.skip_tls_verify"}}</strong></label>
|
||||
<input name="skip_verify" type="checkbox" {{if $cfg.SkipVerify}}checked{{end}}>
|
||||
|
|
|
@ -33,13 +33,13 @@
|
|||
{{template "admin/auth/source/smtp" .}}
|
||||
|
||||
<!-- PAM -->
|
||||
<div class="pam required field {{if not (eq .type 4)}}gt-hidden{{end}}">
|
||||
<div class="pam required field {{if not (eq .type 4)}}tw-hidden{{end}}">
|
||||
<label for="pam_service_name">{{ctx.Locale.Tr "admin.auths.pam_service_name"}}</label>
|
||||
<input id="pam_service_name" name="pam_service_name" value="{{.pam_service_name}}">
|
||||
<label for="pam_email_domain">{{ctx.Locale.Tr "admin.auths.pam_email_domain"}}</label>
|
||||
<input id="pam_email_domain" name="pam_email_domain" value="{{.pam_email_domain}}">
|
||||
</div>
|
||||
<div class="pam optional field {{if not (eq .type 4)}}gt-hidden{{end}}">
|
||||
<div class="pam optional field {{if not (eq .type 4)}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label for="skip_local_two_fa"><strong>{{ctx.Locale.Tr "admin.auths.skip_local_two_fa"}}</strong></label>
|
||||
<input id="skip_local_two_fa" name="skip_local_two_fa" type="checkbox" {{if .skip_local_two_fa}}checked{{end}}>
|
||||
|
@ -59,7 +59,7 @@
|
|||
<input name="attributes_in_bind" type="checkbox" {{if .attributes_in_bind}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{ctx.Locale.Tr "admin.auths.syncenabled"}}</strong></label>
|
||||
<input name="is_sync_enabled" type="checkbox" {{if .is_sync_enabled}}checked{{end}}>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="ldap dldap field {{if not (or (eq .type 2) (eq .type 5))}}gt-hidden{{end}}">
|
||||
<div class="ldap dldap field {{if not (or (eq .type 2) (eq .type 5))}}tw-hidden{{end}}">
|
||||
<div class="inline required field {{if .Err_SecurityProtocol}}error{{end}}">
|
||||
<label>{{ctx.Locale.Tr "admin.auths.security_protocol"}}</label>
|
||||
<div class="ui selection security-protocol dropdown">
|
||||
|
@ -20,17 +20,17 @@
|
|||
<label for="port">{{ctx.Locale.Tr "admin.auths.port"}}</label>
|
||||
<input id="port" name="port" value="{{.port}}" placeholder="636">
|
||||
</div>
|
||||
<div class="has-tls inline field {{if not .HasTLS}}gt-hidden{{end}}">
|
||||
<div class="has-tls inline field {{if not .HasTLS}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{ctx.Locale.Tr "admin.auths.skip_tls_verify"}}</strong></label>
|
||||
<input name="skip_verify" type="checkbox" {{if .skip_verify}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ldap field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<div class="ldap field {{if not (eq .type 2)}}tw-hidden{{end}}">
|
||||
<label for="bind_dn">{{ctx.Locale.Tr "admin.auths.bind_dn"}}</label>
|
||||
<input id="bind_dn" name="bind_dn" value="{{.bind_dn}}" placeholder="cn=Search,dc=mydomain,dc=com">
|
||||
</div>
|
||||
<div class="ldap field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<div class="ldap field {{if not (eq .type 2)}}tw-hidden{{end}}">
|
||||
<label for="bind_password">{{ctx.Locale.Tr "admin.auths.bind_password"}}</label>
|
||||
<input id="bind_password" name="bind_password" type="password" autocomplete="off" value="{{.bind_password}}">
|
||||
</div>
|
||||
|
@ -38,7 +38,7 @@
|
|||
<label for="user_base">{{ctx.Locale.Tr "admin.auths.user_base"}}</label>
|
||||
<input id="user_base" name="user_base" value="{{.user_base}}" placeholder="ou=Users,dc=mydomain,dc=com">
|
||||
</div>
|
||||
<div class="dldap required field {{if not (eq .type 5)}}gt-hidden{{end}}">
|
||||
<div class="dldap required field {{if not (eq .type 5)}}tw-hidden{{end}}">
|
||||
<label for="user_dn">{{ctx.Locale.Tr "admin.auths.user_dn"}}</label>
|
||||
<input id="user_dn" name="user_dn" value="{{.user_dn}}" placeholder="uid=%s,ou=Users,dc=mydomain,dc=com">
|
||||
</div>
|
||||
|
@ -115,13 +115,13 @@
|
|||
</div>
|
||||
<!-- ldap group end -->
|
||||
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}gt-hidden{{end}}">
|
||||
<div class="ldap inline field {{if not (eq .type 2)}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label for="use_paged_search"><strong>{{ctx.Locale.Tr "admin.auths.use_paged_search"}}</strong></label>
|
||||
<input id="use_paged_search" name="use_paged_search" class="use-paged-search" type="checkbox" {{if .use_paged_search}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ldap field search-page-size required {{if or (not (eq .type 2)) (not .use_paged_search)}}gt-hidden{{end}}">
|
||||
<div class="ldap field search-page-size required {{if or (not (eq .type 2)) (not .use_paged_search)}}tw-hidden{{end}}">
|
||||
<label for="search_page_size">{{ctx.Locale.Tr "admin.auths.search_page_size"}}</label>
|
||||
<input id="search_page_size" name="search_page_size" value="{{.search_page_size}}">
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="oauth2 field {{if not (eq .type 6)}}gt-hidden{{end}}">
|
||||
<div class="oauth2 field {{if not (eq .type 6)}}tw-hidden{{end}}">
|
||||
<div class="inline required field">
|
||||
<label>{{ctx.Locale.Tr "admin.auths.oauth2_provider"}}</label>
|
||||
<div class="ui selection type dropdown">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="smtp field {{if not (eq .type 3)}}gt-hidden{{end}}">
|
||||
<div class="smtp field {{if not (eq .type 3)}}tw-hidden{{end}}">
|
||||
<div class="inline required field">
|
||||
<label>{{ctx.Locale.Tr "admin.auths.smtp_auth"}}</label>
|
||||
<div class="ui selection type dropdown">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="sspi field {{if not (eq .type 7)}}gt-hidden{{end}}">
|
||||
<div class="sspi field {{if not (eq .type 7)}}tw-hidden{{end}}">
|
||||
<div class="field">
|
||||
<div class="ui checkbox">
|
||||
<label for="sspi_auto_create_users"><strong>{{ctx.Locale.Tr "admin.auths.sspi_auto_create_users"}}</strong></label>
|
||||
|
|
|
@ -231,7 +231,7 @@
|
|||
<dt>{{ctx.Locale.Tr "admin.config.mailer_user"}}</dt>
|
||||
<dd>{{if .Mailer.User}}{{.Mailer.User}}{{else}}(empty){{end}}</dd>
|
||||
<div class="divider"></div>
|
||||
<dt class="gt-py-2">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt>
|
||||
<dt class="tw-py-1">{{ctx.Locale.Tr "admin.config.send_test_mail"}}</dt>
|
||||
<dd>
|
||||
<form class="ui form ignore-dirty" action="{{AppSubUrl}}/admin/config/test_mail" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
|
@ -334,7 +334,7 @@
|
|||
{{range $loggerName, $loggerDetail := .Loggers}}
|
||||
<dt>{{ctx.Locale.Tr "admin.config.logger_name_fmt" $loggerName}}</dt>
|
||||
{{if $loggerDetail.IsEnabled}}
|
||||
<dd><pre class="gt-m-0">{{$loggerDetail.EventWriters | JsonUtils.EncodeToString | JsonUtils.PrettyIndent}}</pre></dd>
|
||||
<dd><pre class="tw-m-0">{{$loggerDetail.EventWriters | JsonUtils.EncodeToString | JsonUtils.PrettyIndent}}</pre></dd>
|
||||
{{else}}
|
||||
<dd>{{ctx.Locale.Tr "admin.config.disabled_logger"}}</dd>
|
||||
{{end}}
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<div class="field">
|
||||
<details>
|
||||
<summary>{{ctx.Locale.Tr "admin.config.open_with_editor_app_help"}}</summary>
|
||||
<pre class="gt-px-4">{{.DefaultOpenWithEditorAppsString}}</pre>
|
||||
<pre class="tw-px-4">{{.DefaultOpenWithEditorAppsString}}</pre>
|
||||
</details>
|
||||
</div>
|
||||
<div class="field">
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
</h4>
|
||||
<div class="ui attached table segment">
|
||||
<form method="post" action="{{AppSubUrl}}/admin">
|
||||
<table class="ui very basic striped table unstackable gt-mb-0">
|
||||
<table class="ui very basic striped table unstackable tw-mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<div class="ui attached table segment">
|
||||
<form method="post" action="{{AppSubUrl}}/admin">
|
||||
{{.CsrfTokenHtml}}
|
||||
<table class="ui very basic table gt-mt-0 gt-px-4">
|
||||
<table class="ui very basic table tw-mt-0 tw-px-4">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>{{ctx.Locale.Tr "admin.dashboard.delete_inactive_accounts"}}</td>
|
||||
|
|
|
@ -4,12 +4,12 @@
|
|||
{{ctx.Locale.Tr "admin.emails.email_manage_panel"}} ({{ctx.Locale.Tr "admin.total" .Total}})
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="ui secondary filter menu tw-items-center gt-mx-0">
|
||||
<div class="ui secondary filter menu tw-items-center tw-mx-0">
|
||||
<form class="ui form ignore-dirty tw-flex-1">
|
||||
{{template "shared/search/combo" dict "Value" .Keyword}}
|
||||
</form>
|
||||
<!-- Sort -->
|
||||
<div class="ui dropdown type jump item gt-mr-0">
|
||||
<div class="ui dropdown type jump item tw-mr-0">
|
||||
<span class="text">
|
||||
{{ctx.Locale.Tr "repo.issues.filter_sort"}}
|
||||
</span>
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
</div>
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="ui secondary filter menu tw-items-center gt-mx-0">
|
||||
<div class="ui secondary filter menu tw-items-center tw-mx-0">
|
||||
<form class="ui form ignore-dirty tw-flex-1">
|
||||
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.org_kind")}}
|
||||
</form>
|
||||
<!-- Sort -->
|
||||
<div class="ui dropdown type jump item gt-mr-0">
|
||||
<div class="ui dropdown type jump item tw-mr-0">
|
||||
<span class="text">
|
||||
{{ctx.Locale.Tr "repo.issues.filter_sort"}}
|
||||
</span>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
-
|
||||
{{else}}
|
||||
{{$sum}}
|
||||
<form action="{{$.Link}}/remove-all-items" method="post" class="tw-inline-block gt-ml-4">
|
||||
<form action="{{$.Link}}/remove-all-items" method="post" class="tw-inline-block tw-ml-4">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<button class="ui tiny basic red button">{{ctx.Locale.Tr "admin.monitor.queue.settings.remove_all_items"}}</button>
|
||||
</form>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
<div class="item tw-flex tw-items-center">
|
||||
<span class="tw-flex-1"> {{svg "octicon-file-directory-fill"}} {{$dir}}</span>
|
||||
<div>
|
||||
<button class="ui button primary show-modal gt-p-3" data-modal="#adopt-unadopted-modal-{{$dirI}}">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</button>
|
||||
<button class="ui button primary show-modal tw-p-2" data-modal="#adopt-unadopted-modal-{{$dirI}}">{{svg "octicon-plus"}} {{ctx.Locale.Tr "repo.adopt_preexisting_label"}}</button>
|
||||
<div class="ui g-modal-confirm modal" id="adopt-unadopted-modal-{{$dirI}}">
|
||||
<div class="header">
|
||||
<span class="label">{{ctx.Locale.Tr "repo.adopt_preexisting"}}</span>
|
||||
|
@ -40,7 +40,7 @@
|
|||
{{template "base/modal_actions_confirm"}}
|
||||
</form>
|
||||
</div>
|
||||
<button class="ui button red show-modal gt-p-3" data-modal="#delete-unadopted-modal-{{$dirI}}">{{svg "octicon-x"}} {{ctx.Locale.Tr "repo.delete_preexisting_label"}}</button>
|
||||
<button class="ui button red show-modal tw-p-2" data-modal="#delete-unadopted-modal-{{$dirI}}">{{svg "octicon-x"}} {{ctx.Locale.Tr "repo.delete_preexisting_label"}}</button>
|
||||
<div class="ui g-modal-confirm modal" id="delete-unadopted-modal-{{$dirI}}">
|
||||
<div class="header">
|
||||
<span class="label">{{ctx.Locale.Tr "repo.delete_preexisting"}}</span>
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
<div class="ui attached segment">
|
||||
{{if .DatabaseCheckHasProblems}}
|
||||
{{if .DatabaseType.IsMySQL}}
|
||||
<div class="gt-p-3">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
|
||||
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mysql"}}</div>
|
||||
{{else if .DatabaseType.IsMSSQL}}
|
||||
<div class="gt-p-3">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
|
||||
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.database_fix_mssql"}}</div>
|
||||
{{end}}
|
||||
{{if .DatabaseCheckCollationMismatch}}
|
||||
<div class="ui red message">{{ctx.Locale.Tr "admin.self_check.database_collation_mismatch" .DatabaseCheckResult.ExpectedCollation}}</div>
|
||||
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="gt-p-3">{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}</div>
|
||||
<div class="tw-p-2">{{ctx.Locale.Tr "admin.self_check.no_problem_found"}}</div>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="item">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<div class="icon gt-ml-3 gt-mr-3">
|
||||
<div class="icon tw-ml-2 tw-mr-2">
|
||||
{{if eq .Process.Type "request"}}
|
||||
{{svg "octicon-globe" 16}}
|
||||
{{else if eq .Process.Type "system"}}
|
||||
|
@ -22,14 +22,14 @@
|
|||
</div>
|
||||
</div>
|
||||
{{if .Process.Stacks}}
|
||||
<div class="divided list gt-ml-3">
|
||||
<div class="divided list tw-ml-2">
|
||||
{{range .Process.Stacks}}
|
||||
<div class="item">
|
||||
<details>
|
||||
<summary>
|
||||
<div class="flex-text-inline">
|
||||
<div class="header gt-ml-3">
|
||||
<span class="icon gt-mr-3">{{svg "octicon-code" 16}}</span>{{.Description}}{{if gt .Count 1}} * {{.Count}}{{end}}
|
||||
<div class="header tw-ml-2">
|
||||
<span class="icon tw-mr-2">{{svg "octicon-code" 16}}</span>{{.Description}}{{if gt .Count 1}} * {{.Count}}{{end}}
|
||||
</div>
|
||||
<div class="description">
|
||||
{{range .Labels}}
|
||||
|
@ -41,7 +41,7 @@
|
|||
<div class="list">
|
||||
{{range .Entry}}
|
||||
<div class="item tw-flex tw-items-center">
|
||||
<span class="icon gt-mr-4">{{svg "octicon-dot-fill" 16}}</span>
|
||||
<span class="icon tw-mr-4">{{svg "octicon-dot-fill" 16}}</span>
|
||||
<div class="content tw-flex-1">
|
||||
<div class="header"><code>{{.Function}}</code></div>
|
||||
<div class="description"><code>{{.File}}:{{.Line}}</code></div>
|
||||
|
|
|
@ -53,7 +53,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}gt-hidden{{end}}">
|
||||
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .User.LoginSource 0}}tw-hidden{{end}}">
|
||||
<label for="login_name">{{ctx.Locale.Tr "admin.users.auth_login_name"}}</label>
|
||||
<input id="login_name" name="login_name" value="{{.User.LoginName}}" autofocus>
|
||||
</div>
|
||||
|
@ -65,7 +65,7 @@
|
|||
<label for="email">{{ctx.Locale.Tr "email"}}</label>
|
||||
<input id="email" name="email" type="email" value="{{.User.Email}}" autofocus required>
|
||||
</div>
|
||||
<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}gt-hidden{{end}}">
|
||||
<div class="local field {{if .Err_Password}}error{{end}} {{if not (or (.User.IsLocal) (.User.IsOAuth2))}}tw-hidden{{end}}">
|
||||
<label for="password">{{ctx.Locale.Tr "password"}}</label>
|
||||
<input id="password" name="password" type="password" autocomplete="new-password">
|
||||
<p class="help">{{ctx.Locale.Tr "admin.users.password_helper"}}</p>
|
||||
|
@ -128,13 +128,13 @@
|
|||
<input name="restricted" type="checkbox" {{if .User.IsRestricted}}checked{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline field {{if DisableGitHooks}}gt-hidden{{end}}">
|
||||
<div class="inline field {{if DisableGitHooks}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox" data-tooltip-content="{{ctx.Locale.Tr "admin.users.allow_git_hook_tooltip"}}">
|
||||
<label><strong>{{ctx.Locale.Tr "admin.users.allow_git_hook"}}</strong></label>
|
||||
<input name="allow_git_hook" type="checkbox" {{if .User.CanEditGitHook}}checked{{end}} {{if DisableGitHooks}}disabled{{end}}>
|
||||
</div>
|
||||
</div>
|
||||
<div class="inline field {{if or (DisableImportLocal) (.DisableMigrations)}}gt-hidden{{end}}">
|
||||
<div class="inline field {{if or (DisableImportLocal) (.DisableMigrations)}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{ctx.Locale.Tr "admin.users.allow_import_local"}}</strong></label>
|
||||
<input name="allow_import_local" type="checkbox" {{if .User.CanImportLocal}}checked{{end}} {{if DisableImportLocal}}disabled{{end}}>
|
||||
|
@ -181,7 +181,7 @@
|
|||
<label>{{ctx.Locale.Tr "settings.lookup_avatar_by_mail"}}</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field gt-pl-4 {{if .Err_Gravatar}}error{{end}}">
|
||||
<div class="field tw-pl-4 {{if .Err_Gravatar}}error{{end}}">
|
||||
<label for="gravatar">Avatar {{ctx.Locale.Tr "email"}}</label>
|
||||
<input id="gravatar" name="gravatar" value="{{.User.AvatarEmail}}">
|
||||
</div>
|
||||
|
@ -194,7 +194,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="inline field gt-pl-4">
|
||||
<div class="inline field tw-pl-4">
|
||||
<label for="avatar">{{ctx.Locale.Tr "settings.choose_new_avatar"}}</label>
|
||||
<input name="avatar" type="file" accept="image/png,image/jpeg,image/gif,image/webp">
|
||||
</div>
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .login_type "0-0"}}gt-hidden{{end}}">
|
||||
<div class="required non-local field {{if .Err_LoginName}}error{{end}} {{if eq .login_type "0-0"}}tw-hidden{{end}}">
|
||||
<label for="login_name">{{ctx.Locale.Tr "admin.users.auth_login_name"}}</label>
|
||||
<input id="login_name" name="login_name" value="{{.login_name}}">
|
||||
</div>
|
||||
|
@ -59,12 +59,12 @@
|
|||
<label for="email">{{ctx.Locale.Tr "email"}}</label>
|
||||
<input id="email" name="email" type="email" value="{{.email}}" required>
|
||||
</div>
|
||||
<div class="required local field {{if .Err_Password}}error{{end}} {{if not (eq .login_type "0-0")}}gt-hidden{{end}}">
|
||||
<div class="required local field {{if .Err_Password}}error{{end}} {{if not (eq .login_type "0-0")}}tw-hidden{{end}}">
|
||||
<label for="password">{{ctx.Locale.Tr "password"}}</label>
|
||||
<input id="password" name="password" type="password" autocomplete="new-password" value="{{.password}}" {{if eq .login_type "0-0"}}required{{end}}>
|
||||
</div>
|
||||
|
||||
<div class="inline field local {{if ne .login_type "0-0"}}gt-hidden{{end}}">
|
||||
<div class="inline field local {{if ne .login_type "0-0"}}tw-hidden{{end}}">
|
||||
<div class="ui checkbox">
|
||||
<label><strong>{{ctx.Locale.Tr "auth.allow_password_change"}}</strong></label>
|
||||
<input name="must_change_password" type="checkbox" checked>
|
||||
|
|
|
@ -13,14 +13,14 @@
|
|||
<!-- mobile right menu, it must be here because in mobile view, each item is a flex column, the first item is a full row column -->
|
||||
<div class="ui secondary menu item navbar-mobile-right only-mobile">
|
||||
{{if .IsSigned}}
|
||||
<a id="mobile-notifications-icon" class="item tw-w-auto gt-p-3" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<a id="mobile-notifications-icon" class="item tw-w-auto tw-p-2" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-bell"}}
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} gt-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
</div>
|
||||
</a>
|
||||
{{end}}
|
||||
<button class="item tw-w-auto ui icon mini button gt-p-3 gt-m-0" id="navbar-expand-toggle" aria-label="{{ctx.Locale.Tr "toggle_menu"}}">{{svg "octicon-three-bars"}}</button>
|
||||
<button class="item tw-w-auto ui icon mini button tw-p-2 tw-m-0" id="navbar-expand-toggle" aria-label="{{ctx.Locale.Tr "toggle_menu"}}">{{svg "octicon-three-bars"}}</button>
|
||||
</div>
|
||||
|
||||
<!-- navbar links non-mobile -->
|
||||
|
@ -57,8 +57,8 @@
|
|||
{{if and .IsSigned .MustChangePassword}}
|
||||
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span class="text tw-flex tw-items-center">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
|
||||
<span class="only-mobile gt-ml-3">{{.SignedUser.Name}}</span>
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
|
||||
<span class="only-mobile tw-ml-2">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu">
|
||||
|
@ -75,19 +75,19 @@
|
|||
</div><!-- end dropdown avatar menu -->
|
||||
{{else if .IsSigned}}
|
||||
{{if EnableTimetracking}}
|
||||
<a class="active-stopwatch-trigger item gt-mx-0{{if not .ActiveStopwatch}} gt-hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}">
|
||||
<a class="active-stopwatch-trigger item tw-mx-0{{if not .ActiveStopwatch}} tw-hidden{{end}}" href="{{.ActiveStopwatch.IssueLink}}" title="{{ctx.Locale.Tr "active_stopwatch"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-stopwatch"}}
|
||||
<span class="header-stopwatch-dot"></span>
|
||||
</div>
|
||||
<span class="only-mobile gt-ml-3">{{ctx.Locale.Tr "active_stopwatch"}}</span>
|
||||
<span class="only-mobile tw-ml-2">{{ctx.Locale.Tr "active_stopwatch"}}</span>
|
||||
</a>
|
||||
<div class="active-stopwatch-popup item tippy-target gt-p-3">
|
||||
<div class="active-stopwatch-popup item tippy-target tw-p-2">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<a class="stopwatch-link tw-flex tw-items-center" href="{{.ActiveStopwatch.IssueLink}}">
|
||||
{{svg "octicon-issue-opened" 16 "gt-mr-3"}}
|
||||
{{svg "octicon-issue-opened" 16 "tw-mr-2"}}
|
||||
<span class="stopwatch-issue">{{.ActiveStopwatch.RepoSlug}}#{{.ActiveStopwatch.IssueIndex}}</span>
|
||||
<span class="ui primary label stopwatch-time gt-my-0 gt-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
|
||||
<span class="ui primary label stopwatch-time tw-my-0 tw-mx-4" data-seconds="{{.ActiveStopwatch.Seconds}}">
|
||||
{{if .ActiveStopwatch}}{{Sec2Time .ActiveStopwatch.Seconds}}{{end}}
|
||||
</span>
|
||||
</a>
|
||||
|
@ -111,14 +111,14 @@
|
|||
</div>
|
||||
{{end}}
|
||||
|
||||
<a class="item not-mobile gt-mx-0" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<a class="item not-mobile tw-mx-0" href="{{AppSubUrl}}/notifications" data-tooltip-content="{{ctx.Locale.Tr "notifications"}}" aria-label="{{ctx.Locale.Tr "notifications"}}">
|
||||
<div class="tw-relative">
|
||||
{{svg "octicon-bell"}}
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} gt-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
<span class="notification_count{{if not $notificationUnreadCount}} tw-hidden{{end}}">{{$notificationUnreadCount}}</span>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="ui dropdown jump item gt-mx-0 gt-pr-3" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
|
||||
<div class="ui dropdown jump item tw-mx-0 tw-pr-2" data-tooltip-content="{{ctx.Locale.Tr "create_new"}}">
|
||||
<span class="text">
|
||||
{{svg "octicon-plus"}}
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
|
@ -141,10 +141,10 @@
|
|||
</div><!-- end content create new menu -->
|
||||
</div><!-- end dropdown menu create new -->
|
||||
|
||||
<div class="ui dropdown jump item gt-mx-0 gt-pr-3" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<div class="ui dropdown jump item tw-mx-0 tw-pr-2" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span class="text tw-flex tw-items-center">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
|
||||
<span class="only-mobile gt-ml-3">{{.SignedUser.Name}}</span>
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "tw-mr-1"}}
|
||||
<span class="only-mobile tw-ml-2">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
</span>
|
||||
<div class="menu user-menu">
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
<div class="center page buttons">
|
||||
<div class="ui borderless pagination menu">
|
||||
<a class="{{if .IsFirst}}disabled{{end}} item navigation" {{if not .IsFirst}}href="{{$paginationLink}}{{if $paginationParams}}?{{$paginationParams}}{{end}}"{{end}}>
|
||||
{{svg "gitea-double-chevron-left" 16 "gt-mr-2"}}
|
||||
{{svg "gitea-double-chevron-left" 16 "tw-mr-1"}}
|
||||
<span class="navigation_label">{{ctx.Locale.Tr "admin.first_page"}}</span>
|
||||
</a>
|
||||
<a class="{{if not .HasPrevious}}disabled{{end}} item navigation" {{if .HasPrevious}}href="{{$paginationLink}}?page={{.Previous}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
|
||||
{{svg "octicon-chevron-left" 16 "gt-mr-2"}}
|
||||
{{svg "octicon-chevron-left" 16 "tw-mr-1"}}
|
||||
<span class="navigation_label">{{ctx.Locale.Tr "repo.issues.previous"}}</span>
|
||||
</a>
|
||||
{{range .Pages}}
|
||||
|
@ -22,11 +22,11 @@
|
|||
{{end}}
|
||||
<a class="{{if not .HasNext}}disabled{{end}} item navigation" {{if .HasNext}}href="{{$paginationLink}}?page={{.Next}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
|
||||
<span class="navigation_label">{{ctx.Locale.Tr "repo.issues.next"}}</span>
|
||||
{{svg "octicon-chevron-right" 16 "gt-ml-2"}}
|
||||
{{svg "octicon-chevron-right" 16 "tw-ml-1"}}
|
||||
</a>
|
||||
<a class="{{if .IsLast}}disabled{{end}} item navigation" {{if not .IsLast}}href="{{$paginationLink}}?page={{.TotalPages}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
|
||||
<span class="navigation_label">{{ctx.Locale.Tr "admin.last_page"}}</span>
|
||||
{{svg "gitea-double-chevron-right" 16 "gt-ml-2"}}
|
||||
{{svg "gitea-double-chevron-right" 16 "tw-ml-1"}}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div><button name="btn">submit post</button></div>
|
||||
</form>
|
||||
<form method="post" action="/no-such-uri" class="form-fetch-action">
|
||||
<div class="gt-py-5">bad action url</div>
|
||||
<div class="tw-py-8">bad action url</div>
|
||||
<div><button name="btn">submit test</button></div>
|
||||
</form>
|
||||
</div>
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
<button class="ui tiny red button">
|
||||
{{svg "octicon-warning" 14}} CJK文本测试
|
||||
{{svg "octicon-alert" 14}} CJK文本测试
|
||||
</button>
|
||||
<button class="ui tiny primary button">
|
||||
{{svg "octicon-info" 14}} Button
|
||||
|
@ -54,7 +54,7 @@
|
|||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
<button class="ui tiny red button">
|
||||
{{svg "octicon-warning" 12}} CJK文本测试 <!-- single CJK text test, it shouldn't be horizontal -->
|
||||
{{svg "octicon-alert" 12}} CJK文本测试 <!-- single CJK text test, it shouldn't be horizontal -->
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -73,7 +73,7 @@
|
|||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
<a class="muted" href="{{$.Link}}">
|
||||
<span class="flex-text-inline"><i class="color-icon gt-mr-3 tw-bg-blue"></i>Go</span>
|
||||
<span class="flex-text-inline"><i class="color-icon tw-mr-2 tw-bg-blue"></i>Go</span>
|
||||
</a>
|
||||
<a class="text grey flex-text-inline" href="{{$.Link}}">{{svg "octicon-star" 16}}45000</a>
|
||||
<a class="text grey flex-text-inline" href="{{$.Link}}">{{svg "octicon-git-branch" 16}}1234</a>
|
||||
|
@ -104,7 +104,7 @@
|
|||
</div>
|
||||
|
||||
<h1>If parent provides the padding/margin space:</h1>
|
||||
<div class="tw-border tw-border-secondary gt-py-4">
|
||||
<div class="tw-border tw-border-secondary tw-py-4">
|
||||
<div class="flex-list flex-space-fitted">
|
||||
<div class="flex-item">item 1 (no padding top)</div>
|
||||
<div class="flex-item">item 2 (no padding bottom)</div>
|
||||
|
|
|
@ -67,10 +67,10 @@
|
|||
</li>
|
||||
<li class="sample-group">
|
||||
<h2>Inline / Plain:</h2>
|
||||
<div class="gt-my-2">
|
||||
<button class="btn gt-p-3">Plain button</button>
|
||||
<button class="btn interact-fg gt-p-3">Plain button with interact fg</button>
|
||||
<button class="btn interact-bg gt-p-3">Plain button with interact bg</button>
|
||||
<div class="tw-my-1">
|
||||
<button class="btn tw-p-2">Plain button</button>
|
||||
<button class="btn interact-fg tw-p-2">Plain button with interact fg</button>
|
||||
<button class="btn interact-bg tw-p-2">Plain button with interact bg</button>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -102,8 +102,8 @@
|
|||
|
||||
<div>
|
||||
<h1>Loading</h1>
|
||||
<div class="is-loading small-loading-icon tw-border tw-border-secondary gt-py-2"><span>loading ...</span></div>
|
||||
<div class="is-loading tw-border tw-border-secondary gt-py-4">
|
||||
<div class="is-loading small-loading-icon tw-border tw-border-secondary tw-py-1"><span>loading ...</span></div>
|
||||
<div class="is-loading tw-border tw-border-secondary tw-py-4">
|
||||
<p>loading ...</p>
|
||||
<p>loading ...</p>
|
||||
<p>loading ...</p>
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
<div class="flex-item-trailing muted-links">
|
||||
{{if .PrimaryLanguage}}
|
||||
<a class="flex-text-inline" href="?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
|
||||
<i class="color-icon gt-mr-3" style="background-color: {{.PrimaryLanguage.Color}}"></i>
|
||||
<i class="color-icon tw-mr-2" style="background-color: {{.PrimaryLanguage.Color}}"></i>
|
||||
{{.PrimaryLanguage.Language}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="ui small secondary filter menu tw-items-center gt-mx-0">
|
||||
<div class="ui small secondary filter menu tw-items-center tw-mx-0">
|
||||
<form class="ui form ignore-dirty tw-flex-1">
|
||||
{{if .PageIsExploreUsers}}
|
||||
{{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.user_kind")}}
|
||||
|
@ -7,7 +7,7 @@
|
|||
{{end}}
|
||||
</form>
|
||||
<!-- Sort -->
|
||||
<div class="ui small dropdown type jump item gt-mr-0">
|
||||
<div class="ui small dropdown type jump item tw-mr-0">
|
||||
<span class="text">
|
||||
{{ctx.Locale.Tr "repo.issues.filter_sort"}}
|
||||
</span>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{if .IsSigned}}{{ctx.Locale.Tr "dashboard"}}{{else}}{{ctx.Locale.Tr "home"}}{{end}}" class="page-content home">
|
||||
<div class="gt-mb-5 gt-px-5">
|
||||
<div class="tw-mb-8 tw-px-8">
|
||||
<div class="center">
|
||||
<img class="logo" width="220" height="220" src="{{AssetUrlPrefix}}/img/logo.svg" alt="{{ctx.Locale.Tr "logo"}}">
|
||||
<div class="hero">
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gt-mt-4 gt-hidden" data-db-setting-for="common-host">
|
||||
<div class="tw-mt-4 tw-hidden" data-db-setting-for="common-host">
|
||||
<div class="inline required field {{if .Err_DbSetting}}error{{end}}">
|
||||
<label for="db_host">{{ctx.Locale.Tr "install.host"}}</label>
|
||||
<input id="db_host" name="db_host" value="{{.db_host}}">
|
||||
|
@ -47,7 +47,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gt-mt-4 gt-hidden" data-db-setting-for="postgres">
|
||||
<div class="tw-mt-4 tw-hidden" data-db-setting-for="postgres">
|
||||
<div class="inline required field">
|
||||
<label>{{ctx.Locale.Tr "install.ssl_mode"}}</label>
|
||||
<div class="ui selection database type dropdown">
|
||||
|
@ -68,7 +68,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="gt-mt-4 gt-hidden" data-db-setting-for="sqlite3">
|
||||
<div class="tw-mt-4 tw-hidden" data-db-setting-for="sqlite3">
|
||||
<div class="inline required field {{if or .Err_DbPath .Err_DbSetting}}error{{end}}">
|
||||
<label for="db_path">{{ctx.Locale.Tr "install.path"}}</label>
|
||||
<input id="db_path" name="db_path" value="{{.db_path}}">
|
||||
|
@ -160,7 +160,7 @@
|
|||
|
||||
<!-- Email -->
|
||||
<details class="optional field">
|
||||
<summary class="right-content gt-py-3{{if .Err_SMTP}} text red{{end}}">
|
||||
<summary class="right-content tw-py-2{{if .Err_SMTP}} text red{{end}}">
|
||||
{{ctx.Locale.Tr "install.email_title"}}
|
||||
</summary>
|
||||
<div class="inline field">
|
||||
|
@ -200,7 +200,7 @@
|
|||
|
||||
<!-- Server and other services -->
|
||||
<details class="optional field">
|
||||
<summary class="right-content gt-py-3{{if .Err_Services}} text red{{end}}">
|
||||
<summary class="right-content tw-py-2{{if .Err_Services}} text red{{end}}">
|
||||
{{ctx.Locale.Tr "install.server_service_title"}}
|
||||
</summary>
|
||||
<div class="inline field">
|
||||
|
@ -298,7 +298,7 @@
|
|||
|
||||
<!-- Admin -->
|
||||
<details class="optional field">
|
||||
<summary class="right-content gt-py-3{{if .Err_Admin}} text red{{end}}">
|
||||
<summary class="right-content tw-py-2{{if .Err_Admin}} text red{{end}}">
|
||||
{{ctx.Locale.Tr "install.admin_title"}}
|
||||
</summary>
|
||||
<p class="center">{{ctx.Locale.Tr "install.admin_setting_desc"}}</p>
|
||||
|
@ -327,7 +327,7 @@
|
|||
<div class="right-content">
|
||||
{{ctx.Locale.Tr "install.env_config_keys_prompt"}}
|
||||
</div>
|
||||
<div class="right-content gt-mt-3">
|
||||
<div class="right-content tw-mt-2">
|
||||
{{range .EnvConfigKeys}}<span class="ui label">{{.}}</span>{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -338,7 +338,7 @@
|
|||
<div class="right-content">
|
||||
{{ctx.Locale.Tr "install.config_location_hint"}} {{.CustomConfFile}}
|
||||
</div>
|
||||
<div class="right-content gt-mt-3">
|
||||
<div class="right-content tw-mt-2">
|
||||
<button class="ui primary button">{{ctx.Locale.Tr "install.install_btn_confirm"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -347,5 +347,5 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<img class="gt-hidden" src="{{AssetUrlPrefix}}/img/forgejo-loading.svg" width="256" height="256">
|
||||
<img class="tw-hidden" src="{{AssetUrlPrefix}}/img/forgejo-loading.svg" width="256" height="256">
|
||||
{{template "base/footer" .}}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<button class="ui basic button gt-mr-0" hx-post="{{.Org.HomeLink}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}">
|
||||
<button class="ui basic button tw-mr-0" hx-post="{{.Org.HomeLink}}?action={{if $.IsFollowing}}unfollow{{else}}follow{{end}}">
|
||||
{{if $.IsFollowing}}
|
||||
{{ctx.Locale.Tr "user.unfollow"}}
|
||||
{{else}}
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
</span>
|
||||
<span class="tw-flex tw-items-center tw-gap-1 tw-ml-auto tw-text-16 tw-whitespace-nowrap">
|
||||
{{if .EnableFeed}}
|
||||
<a class="ui basic label button gt-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
|
||||
<a class="ui basic label button tw-mr-0" href="{{.Org.HomeLink}}.rss" data-tooltip-content="{{ctx.Locale.Tr "rss_feed"}}">
|
||||
{{svg "octicon-rss" 24}}
|
||||
</a>
|
||||
{{end}}
|
||||
|
@ -19,7 +19,7 @@
|
|||
</span>
|
||||
</div>
|
||||
{{if .RenderedDescription}}<div class="render-content markup">{{.RenderedDescription}}</div>{{end}}
|
||||
<div class="text light meta gt-mt-2">
|
||||
<div class="text light meta tw-mt-1">
|
||||
{{if .Org.Location}}<div class="flex-text-block">{{svg "octicon-location"}} <span>{{.Org.Location}}</span></div>{{end}}
|
||||
{{if .Org.Website}}<div class="flex-text-block">{{svg "octicon-link"}} <a class="muted" target="_blank" rel="noopener noreferrer me" href="{{.Org.Website}}">{{.Org.Website}}</a></div>{{end}}
|
||||
{{if .IsSigned}}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{{template "base/head" .}}
|
||||
<div role="main" aria-label="{{.Title}}" class="page-content organization profile">
|
||||
{{if .Flash}}
|
||||
<div class="ui container gt-mb-5">
|
||||
<div class="ui container tw-mb-8">
|
||||
{{template "base/alert" .}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
@ -21,7 +21,7 @@
|
|||
{{if .ShowMemberAndTeamTab}}
|
||||
<div class="ui five wide column">
|
||||
{{if .CanCreateOrgRepo}}
|
||||
<div class="center aligned">
|
||||
<div class="center aligned tw-mb-4">
|
||||
<a class="ui primary button" href="{{AppSubUrl}}/repo/create?org={{.Org.ID}}">{{ctx.Locale.Tr "new_repo"}}</a>
|
||||
{{if not .DisableNewPullMirrors}}
|
||||
<a class="ui primary button" href="{{AppSubUrl}}/repo/migrate?org={{.Org.ID}}&mirror=1">{{ctx.Locale.Tr "new_migrate"}}</a>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="ui container">
|
||||
<overflow-menu class="ui secondary pointing tabular borderless menu">
|
||||
<overflow-menu class="ui secondary pointing tabular borderless menu tw-mb-4">
|
||||
<div class="overflow-menu-items">
|
||||
<a class="{{if .PageIsViewRepositories}}active {{end}}item" href="{{$.Org.HomeLink}}">
|
||||
{{svg "octicon-repo"}} {{ctx.Locale.Tr "user.repositories"}}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
{{template "shared/user/profile_big_avatar" .}}
|
||||
</div>
|
||||
<div class="ui twelve wide column">
|
||||
<div class="gt-mb-4">
|
||||
<div class="tw-mb-4">
|
||||
{{template "user/overview/header" .}}
|
||||
</div>
|
||||
{{template "projects/list" .}}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{{.CsrfTokenHtml}}
|
||||
<div class="required field {{if .Err_Name}}error{{end}}">
|
||||
<label for="org_name">{{ctx.Locale.Tr "org.org_name_holder"}}
|
||||
<span class="text red gt-hidden" id="org-name-change-prompt">
|
||||
<span class="text red tw-hidden" id="org-name-change-prompt">
|
||||
<br>{{ctx.Locale.Tr "org.settings.change_orgname_prompt"}}<br>{{ctx.Locale.Tr "org.settings.change_orgname_redirect_prompt"}}
|
||||
</span>
|
||||
</label>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<form class="ui form ignore-dirty tw-flex tw-flex-wrap tw-gap-2" action="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/add" method="post">
|
||||
{{.CsrfTokenHtml}}
|
||||
<input type="hidden" name="uid" value="{{.SignedUser.ID}}">
|
||||
<div id="search-user-box" class="ui search gt-mr-3"{{if .IsEmailInviteEnabled}} data-allow-email="true" data-allow-email-description="{{ctx.Locale.Tr "org.teams.invite_team_member" $.Team.Name}}"{{end}}>
|
||||
<div id="search-user-box" class="ui search tw-mr-2"{{if .IsEmailInviteEnabled}} data-allow-email="true" data-allow-email-description="{{ctx.Locale.Tr "org.teams.invite_team_member" $.Team.Name}}"{{end}}>
|
||||
<div class="ui input">
|
||||
<input class="prompt" name="uname" placeholder="{{ctx.Locale.Tr "search.user_kind"}}" autocomplete="off" required>
|
||||
</div>
|
||||
|
|
|
@ -71,18 +71,18 @@
|
|||
</div>
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="team-units required grouped field {{if eq .Team.AccessMode 3}}gt-hidden{{end}}">
|
||||
<div class="team-units required grouped field {{if eq .Team.AccessMode 3}}tw-hidden{{end}}">
|
||||
<label>{{ctx.Locale.Tr "org.team_unit_desc"}}</label>
|
||||
<table class="ui celled table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ctx.Locale.Tr "units.unit"}}</th>
|
||||
<th class="center aligned">{{ctx.Locale.Tr "org.teams.none_access"}}
|
||||
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
|
||||
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.none_access_helper"}}">{{svg "octicon-question" 16 "tw-ml-1"}}</span></th>
|
||||
<th class="center aligned">{{ctx.Locale.Tr "org.teams.read_access"}}
|
||||
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
|
||||
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.read_access_helper"}}">{{svg "octicon-question" 16 "tw-ml-1"}}</span></th>
|
||||
<th class="center aligned">{{ctx.Locale.Tr "org.teams.write_access"}}
|
||||
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "gt-ml-2"}}</span></th>
|
||||
<span class="tw-align-middle" data-tooltip-content="{{ctx.Locale.Tr "org.teams.write_access_helper"}}">{{svg "octicon-question" 16 "tw-ml-1"}}</span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<input class="prompt" name="repo_name" placeholder="{{ctx.Locale.Tr "search.repo_kind"}}" autocomplete="off" required>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui primary button gt-ml-3">{{ctx.Locale.Tr "add"}}</button>
|
||||
<button class="ui primary button tw-ml-2">{{ctx.Locale.Tr "add"}}</button>
|
||||
</form>
|
||||
<div class="tw-inline-block">
|
||||
<button class="ui primary button link-action" data-modal-confirm="{{ctx.Locale.Tr "org.teams.add_all_repos_desc"}}" data-url="{{$.OrgLink}}/teams/{{$.Team.LowerName | PathEscape}}/action/repo/addall">{{ctx.Locale.Tr "add_all"}}</button>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{{if eq .PackageDescriptor.Package.Type "alpine"}}
|
||||
{{if .PackageDescriptor.Metadata.Maintainer}}<div class="item" title="{{ctx.Locale.Tr "packages.details.author"}}">{{svg "octicon-person" 16 "mr-3"}} {{.PackageDescriptor.Metadata.Maintainer}}</div>{{end}}
|
||||
{{if .PackageDescriptor.Metadata.ProjectURL}}<div class="item">{{svg "octicon-link-external" 16 "mr-3"}} <a href="{{.PackageDescriptor.Metadata.ProjectURL}}" target="_blank" rel="noopener noreferrer me">{{ctx.Locale.Tr "packages.details.project_site"}}</a></div>{{end}}
|
||||
{{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{ctx.Locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "gt-mr-3"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}}
|
||||
{{if .PackageDescriptor.Metadata.License}}<div class="item" title="{{ctx.Locale.Tr "packages.details.license"}}">{{svg "octicon-law" 16 "tw-mr-2"}} {{.PackageDescriptor.Metadata.License}}</div>{{end}}
|
||||
{{end}}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue