Merge pull request '[gitea] week 13 cherry-pick' (#2769) from earl-warren/forgejo:wip-gitea-cherry-pick into forgejo
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2769 Reviewed-by: oliverpool <oliverpool@noreply.codeberg.org>
This commit is contained in:
commit
8339b24d61
302 changed files with 2569 additions and 5061 deletions
11
.air.toml
11
.air.toml
|
@ -8,6 +8,15 @@ delay = 1000
|
|||
include_ext = ["go", "tmpl"]
|
||||
include_file = ["main.go"]
|
||||
include_dir = ["cmd", "models", "modules", "options", "routers", "services"]
|
||||
exclude_dir = ["modules/git/tests", "services/gitdiff/testdata", "modules/avatar/testdata", "models/fixtures", "models/migrations/fixtures", "modules/migration/file_format_testdata", "modules/avatar/identicon/testdata"]
|
||||
exclude_dir = [
|
||||
"models/fixtures",
|
||||
"models/migrations/fixtures",
|
||||
"modules/avatar/identicon/testdata",
|
||||
"modules/avatar/testdata",
|
||||
"modules/git/tests",
|
||||
"modules/migration/file_format_testdata",
|
||||
"routers/private/tests",
|
||||
"services/gitdiff/testdata",
|
||||
]
|
||||
exclude_regex = ["_test.go$", "_gen.go$"]
|
||||
stop_on_error = true
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"features": {
|
||||
// installs nodejs into container
|
||||
"ghcr.io/devcontainers/features/node:1": {
|
||||
"version":"20"
|
||||
"version": "20"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git-lfs:1.1.0": {},
|
||||
"ghcr.io/devcontainers-contrib/features/poetry:2": {},
|
||||
|
@ -24,7 +24,7 @@
|
|||
"DavidAnson.vscode-markdownlint",
|
||||
"Vue.volar",
|
||||
"ms-azuretools.vscode-docker",
|
||||
"zixuanchen.vitest-explorer",
|
||||
"vitest.explorer",
|
||||
"qwtel.sqlite-viewer",
|
||||
"GitHub.vscode-pull-request-github"
|
||||
]
|
||||
|
|
|
@ -62,7 +62,6 @@ cpu.out
|
|||
/data
|
||||
/indexers
|
||||
/log
|
||||
/public/img/avatar
|
||||
/tests/integration/gitea-integration-*
|
||||
/tests/integration/indexers-*
|
||||
/tests/e2e/gitea-e2e-*
|
||||
|
@ -77,6 +76,7 @@ cpu.out
|
|||
/public/assets/js
|
||||
/public/assets/css
|
||||
/public/assets/fonts
|
||||
/public/assets/img/avatar
|
||||
/public/assets/img/webpack
|
||||
/vendor
|
||||
/web_src/fomantic/node_modules
|
||||
|
|
|
@ -42,10 +42,6 @@ overrides:
|
|||
worker: true
|
||||
rules:
|
||||
no-restricted-globals: [2, addEventListener, blur, close, closed, confirm, defaultStatus, defaultstatus, error, event, external, find, focus, frameElement, frames, history, innerHeight, innerWidth, isFinite, isNaN, length, locationbar, menubar, moveBy, moveTo, name, onblur, onerror, onfocus, onload, onresize, onunload, open, opener, opera, outerHeight, outerWidth, pageXOffset, pageYOffset, parent, print, removeEventListener, resizeBy, resizeTo, screen, screenLeft, screenTop, screenX, screenY, scroll, scrollbars, scrollBy, scrollTo, scrollX, scrollY, status, statusbar, stop, toolbar, top]
|
||||
- files: ["build/generate-images.js"]
|
||||
rules:
|
||||
i/no-unresolved: [0]
|
||||
i/no-extraneous-dependencies: [0]
|
||||
- files: ["*.config.*"]
|
||||
rules:
|
||||
i/no-unused-modules: [0]
|
||||
|
@ -123,7 +119,7 @@ rules:
|
|||
"@stylistic/js/arrow-spacing": [2, {before: true, after: true}]
|
||||
"@stylistic/js/block-spacing": [0]
|
||||
"@stylistic/js/brace-style": [2, 1tbs, {allowSingleLine: true}]
|
||||
"@stylistic/js/comma-dangle": [2, only-multiline]
|
||||
"@stylistic/js/comma-dangle": [2, always-multiline]
|
||||
"@stylistic/js/comma-spacing": [2, {before: false, after: true}]
|
||||
"@stylistic/js/comma-style": [2, last]
|
||||
"@stylistic/js/computed-property-spacing": [2, never]
|
||||
|
@ -290,7 +286,7 @@ rules:
|
|||
jquery/no-class: [0]
|
||||
jquery/no-clone: [2]
|
||||
jquery/no-closest: [0]
|
||||
jquery/no-css: [0]
|
||||
jquery/no-css: [2]
|
||||
jquery/no-data: [0]
|
||||
jquery/no-deferred: [2]
|
||||
jquery/no-delegate: [2]
|
||||
|
@ -413,7 +409,7 @@ rules:
|
|||
no-jquery/no-constructor-attributes: [2]
|
||||
no-jquery/no-contains: [2]
|
||||
no-jquery/no-context-prop: [2]
|
||||
no-jquery/no-css: [0]
|
||||
no-jquery/no-css: [2]
|
||||
no-jquery/no-data: [0]
|
||||
no-jquery/no-deferred: [2]
|
||||
no-jquery/no-delegate: [2]
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -64,7 +64,7 @@ cpu.out
|
|||
/data
|
||||
/indexers
|
||||
/log
|
||||
/public/img/avatar
|
||||
/public/assets/img/avatar
|
||||
/tests/integration/gitea-integration-*
|
||||
/tests/integration/indexers-*
|
||||
/tests/e2e/gitea-e2e-*
|
||||
|
|
|
@ -42,7 +42,7 @@ vscode:
|
|||
- DavidAnson.vscode-markdownlint
|
||||
- Vue.volar
|
||||
- ms-azuretools.vscode-docker
|
||||
- zixuanchen.vitest-explorer
|
||||
- vitest.explorer
|
||||
- qwtel.sqlite-viewer
|
||||
- GitHub.vscode-pull-request-github
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ rules:
|
|||
"@stylistic/block-opening-brace-newline-after": null
|
||||
"@stylistic/block-opening-brace-newline-before": null
|
||||
"@stylistic/block-opening-brace-space-after": null
|
||||
"@stylistic/block-opening-brace-space-before": null
|
||||
"@stylistic/block-opening-brace-space-before": always
|
||||
"@stylistic/color-hex-case": lower
|
||||
"@stylistic/declaration-bang-space-after": never
|
||||
"@stylistic/declaration-bang-space-before": null
|
||||
|
@ -140,7 +140,7 @@ rules:
|
|||
function-disallowed-list: null
|
||||
function-linear-gradient-no-nonstandard-direction: true
|
||||
function-name-case: lower
|
||||
function-no-unknown: null
|
||||
function-no-unknown: true
|
||||
function-url-no-scheme-relative: null
|
||||
function-url-quotes: always
|
||||
function-url-scheme-allowed-list: null
|
||||
|
@ -168,7 +168,7 @@ rules:
|
|||
no-duplicate-selectors: true
|
||||
no-empty-source: true
|
||||
no-invalid-double-slash-comments: true
|
||||
no-invalid-position-at-import-rule: null
|
||||
no-invalid-position-at-import-rule: [true, ignoreAtRules: [tailwind]]
|
||||
no-irregular-whitespace: true
|
||||
no-unknown-animations: null
|
||||
no-unknown-custom-properties: null
|
||||
|
@ -181,6 +181,7 @@ rules:
|
|||
rule-empty-line-before: null
|
||||
rule-selector-property-disallowed-list: null
|
||||
scale-unlimited/declaration-strict-value: [[/color$/, font-weight], {ignoreValues: /^(inherit|transparent|unset|initial|currentcolor|none)$/, ignoreFunctions: false, disableFix: true, expandShorthand: true}]
|
||||
selector-anb-no-unmatchable: true
|
||||
selector-attribute-name-disallowed-list: null
|
||||
selector-attribute-operator-allowed-list: null
|
||||
selector-attribute-operator-disallowed-list: null
|
||||
|
|
19
Makefile
19
Makefile
|
@ -44,9 +44,6 @@ DOCKER_TAG ?= latest
|
|||
DOCKER_REF := $(DOCKER_IMAGE):$(DOCKER_TAG)
|
||||
|
||||
ifeq ($(HAS_GO), yes)
|
||||
GOPATH ?= $(shell $(GO) env GOPATH)
|
||||
export PATH := $(GOPATH)/bin:$(PATH)
|
||||
|
||||
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
|
||||
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
|
||||
endif
|
||||
|
@ -148,6 +145,8 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN
|
|||
GO_DIRS := build cmd models modules routers services tests
|
||||
WEB_DIRS := web_src/js web_src/css
|
||||
|
||||
ESLINT_FILES := web_src/js tools *.config.js tests/e2e
|
||||
STYLELINT_FILES := web_src/css web_src/js/components/*.vue
|
||||
SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github
|
||||
|
||||
GO_SOURCES := $(wildcard *.go)
|
||||
|
@ -396,19 +395,19 @@ lint-backend-fix: lint-go-fix lint-go-vet lint-editorconfig
|
|||
|
||||
.PHONY: lint-js
|
||||
lint-js: node_modules
|
||||
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js tests/e2e
|
||||
npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES)
|
||||
|
||||
.PHONY: lint-js-fix
|
||||
lint-js-fix: node_modules
|
||||
npx eslint --color --max-warnings=0 --ext js,vue web_src/js build *.config.js tests/e2e --fix
|
||||
npx eslint --color --max-warnings=0 --ext js,vue $(ESLINT_FILES) --fix
|
||||
|
||||
.PHONY: lint-css
|
||||
lint-css: node_modules
|
||||
npx stylelint --color --max-warnings=0 web_src/css web_src/js/components/*.vue
|
||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES)
|
||||
|
||||
.PHONY: lint-css-fix
|
||||
lint-css-fix: node_modules
|
||||
npx stylelint --color --max-warnings=0 web_src/css web_src/js/components/*.vue --fix
|
||||
npx stylelint --color --max-warnings=0 $(STYLELINT_FILES) --fix
|
||||
|
||||
.PHONY: lint-swagger
|
||||
lint-swagger: node_modules
|
||||
|
@ -468,7 +467,7 @@ lint-yaml: .venv
|
|||
|
||||
.PHONY: watch
|
||||
watch:
|
||||
@bash build/watch.sh
|
||||
@bash tools/watch.sh
|
||||
|
||||
.PHONY: watch-frontend
|
||||
watch-frontend: node-check node_modules
|
||||
|
@ -962,7 +961,7 @@ $(WEBPACK_DEST): $(WEBPACK_SOURCES) $(WEBPACK_CONFIGS) package-lock.json
|
|||
.PHONY: svg
|
||||
svg: node-check | node_modules
|
||||
rm -rf $(SVG_DEST_DIR)
|
||||
node build/generate-svg.js
|
||||
node tools/generate-svg.js
|
||||
|
||||
.PHONY: svg-check
|
||||
svg-check: svg
|
||||
|
@ -997,7 +996,7 @@ generate-gitignore:
|
|||
.PHONY: generate-images
|
||||
generate-images: | node_modules
|
||||
npm install --no-save fabric@6.0.0-beta19 imagemin-zopfli@7
|
||||
node build/generate-images.js $(TAGS)
|
||||
node tools/generate-images.js $(TAGS)
|
||||
|
||||
.PHONY: generate-manpage
|
||||
generate-manpage:
|
||||
|
|
|
@ -37,7 +37,7 @@ gitea embedded list [--include-vendored] [patterns...]
|
|||
|
||||
- 列出所有模板文件,无论在哪个虚拟目录下:`**.tmpl`
|
||||
- 列出所有邮件模板文件:`templates/mail/**.tmpl`
|
||||
- 列出 `public/img` 目录下的所有文件:`public/img/**`
|
||||
列出 `public/assets/img` 目录下的所有文件:`public/assets/img/**`
|
||||
|
||||
不要忘记为模式使用引号,因为空格、`*` 和其他字符可能对命令行解释器有特殊含义。
|
||||
|
||||
|
@ -49,8 +49,8 @@ gitea embedded list [--include-vendored] [patterns...]
|
|||
|
||||
```sh
|
||||
$ gitea embedded list '**openid**'
|
||||
public/img/auth/openid_connect.svg
|
||||
public/img/openid-16x16.png
|
||||
public/assets/img/auth/openid_connect.svg
|
||||
public/assets/img/openid-16x16.png
|
||||
templates/user/auth/finalize_openid.tmpl
|
||||
templates/user/auth/signin_openid.tmpl
|
||||
templates/user/auth/signup_openid_connect.tmpl
|
||||
|
|
|
@ -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-df`), 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-mono`), 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
|
||||
|
|
|
@ -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-df`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
|
||||
12. 建议使用 Tailwind CSS,它可以通过 `tw-` 前缀获得,例如 `tw-relative`. Gitea 自身的助手类 CSS 使用 `gt-` 前缀(`gt-mono`),Gitea 自身的私有框架级 CSS 类使用 `g-` 前缀(`g-modal-confirm`)。
|
||||
13. 尽量避免内联脚本和样式,建议将JS代码放入JS文件中并使用CSS类。如果内联脚本和样式不可避免,请解释无法避免的原因。
|
||||
|
||||
### 可访问性 / ARIA
|
||||
|
|
|
@ -214,7 +214,7 @@ REPO_INDEXER_CONN_STR = http://elastic:changeme@localhost:9200
|
|||
|
||||
### Building and adding SVGs
|
||||
|
||||
SVG icons are built using the `make svg` target which compiles the icon sources defined in `build/generate-svg.js` into the output directory `public/assets/img/svg`. Custom icons can be added in the `web_src/svg` directory.
|
||||
SVG icons are built using the `make svg` target which compiles the icon sources into the output directory `public/assets/img/svg`. Custom icons can be added in the `web_src/svg` directory.
|
||||
|
||||
### Building the Logo
|
||||
|
||||
|
|
|
@ -201,7 +201,7 @@ REPO_INDEXER_CONN_STR = http://elastic:changeme@localhost:9200
|
|||
|
||||
### 构建和添加 SVGs
|
||||
|
||||
SVG 图标是使用 `make svg` 目标构建的,该目标将 `build/generate-svg.js` 中定义的图标源编译到输出目录 `public/img/svg` 中。可以在 `web_src/svg` 目录中添加自定义图标。
|
||||
SVG 图标是使用 `make svg` 命令构建的,该命令将图标资源编译到输出目录 `public/assets/img/svg` 中。可以在 `web_src/svg` 目录中添加自定义图标。
|
||||
|
||||
### 构建 Logo
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/modules/timeutil"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
"xorm.io/builder"
|
||||
)
|
||||
|
@ -841,3 +842,31 @@ func UpdateNotificationStatuses(ctx context.Context, user *user_model.User, curr
|
|||
Update(n)
|
||||
return err
|
||||
}
|
||||
|
||||
// LoadIssuePullRequests loads all issues' pull requests if possible
|
||||
func (nl NotificationList) LoadIssuePullRequests(ctx context.Context) error {
|
||||
issues := make(map[int64]*issues_model.Issue, len(nl))
|
||||
for _, notification := range nl {
|
||||
if notification.Issue != nil && notification.Issue.IsPull && notification.Issue.PullRequest == nil {
|
||||
issues[notification.Issue.ID] = notification.Issue
|
||||
}
|
||||
}
|
||||
|
||||
if len(issues) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pulls, err := issues_model.GetPullRequestByIssueIDs(ctx, util.KeysOfMap(issues))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, pull := range pulls {
|
||||
if issue := issues[pull.IssueID]; issue != nil {
|
||||
issue.PullRequest = pull
|
||||
issue.PullRequest.Issue = issue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -198,6 +198,8 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
@ -207,11 +209,12 @@ func RegeneratePublicKeys(ctx context.Context, t io.StringWriter) error {
|
|||
}
|
||||
_, err = t.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
if err = scanner.Err(); err != nil {
|
||||
return fmt.Errorf("RegeneratePublicKeys scan: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -120,6 +120,8 @@ func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
@ -129,11 +131,12 @@ func regeneratePrincipalKeys(ctx context.Context, t io.StringWriter) error {
|
|||
}
|
||||
_, err = t.WriteString(line + "\n")
|
||||
if err != nil {
|
||||
f.Close()
|
||||
return err
|
||||
}
|
||||
}
|
||||
f.Close()
|
||||
if err = scanner.Err(); err != nil {
|
||||
return fmt.Errorf("regeneratePrincipalKeys scan: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
const (
|
||||
// DefaultAvatarClass is the default class of a rendered avatar
|
||||
DefaultAvatarClass = "ui avatar gt-vm"
|
||||
DefaultAvatarClass = "ui avatar tw-align-middle"
|
||||
// DefaultAvatarPixelSize is the default size in pixels of a rendered avatar
|
||||
DefaultAvatarPixelSize = 28
|
||||
)
|
||||
|
|
|
@ -194,20 +194,6 @@ func (issue *Issue) IsTimetrackerEnabled(ctx context.Context) bool {
|
|||
return issue.Repo.IsTimetrackerEnabled(ctx)
|
||||
}
|
||||
|
||||
// GetPullRequest returns the issue pull request
|
||||
func (issue *Issue) GetPullRequest(ctx context.Context) (pr *PullRequest, err error) {
|
||||
if !issue.IsPull {
|
||||
return nil, fmt.Errorf("Issue is not a pull request")
|
||||
}
|
||||
|
||||
pr, err = GetPullRequestByIssueID(ctx, issue.ID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pr.Issue = issue
|
||||
return pr, err
|
||||
}
|
||||
|
||||
// LoadPoster loads poster
|
||||
func (issue *Issue) LoadPoster(ctx context.Context) (err error) {
|
||||
if issue.Poster == nil && issue.PosterID != 0 {
|
||||
|
|
|
@ -370,6 +370,9 @@ func (issues IssueList) LoadPullRequests(ctx context.Context) error {
|
|||
|
||||
for _, issue := range issues {
|
||||
issue.PullRequest = pullRequestMaps[issue.ID]
|
||||
if issue.PullRequest != nil {
|
||||
issue.PullRequest.Issue = issue
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -11,7 +11,6 @@ import (
|
|||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
|
||||
|
@ -23,7 +22,7 @@ type PullRequestsOptions struct {
|
|||
db.ListOptions
|
||||
State string
|
||||
SortType string
|
||||
Labels []string
|
||||
Labels []int64
|
||||
MilestoneID int64
|
||||
}
|
||||
|
||||
|
@ -36,11 +35,9 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR
|
|||
sess.And("issue.is_closed=?", opts.State == "closed")
|
||||
}
|
||||
|
||||
if labelIDs, err := base.StringsToInt64s(opts.Labels); err != nil {
|
||||
return nil, err
|
||||
} else if len(labelIDs) > 0 {
|
||||
if len(opts.Labels) > 0 {
|
||||
sess.Join("INNER", "issue_label", "issue.id = issue_label.issue_id").
|
||||
In("issue_label.label_id", labelIDs)
|
||||
In("issue_label.label_id", opts.Labels)
|
||||
}
|
||||
|
||||
if opts.MilestoneID > 0 {
|
||||
|
@ -220,3 +217,12 @@ func HasMergedPullRequestInRepo(ctx context.Context, repoID, posterID int64) (bo
|
|||
Limit(1).
|
||||
Get(new(Issue))
|
||||
}
|
||||
|
||||
// GetPullRequestByIssueIDs returns all pull requests by issue ids
|
||||
func GetPullRequestByIssueIDs(ctx context.Context, issueIDs []int64) (PullRequestList, error) {
|
||||
prs := make([]*PullRequest, 0, len(issueIDs))
|
||||
return prs, db.GetEngine(ctx).
|
||||
Where("issue_id > 0").
|
||||
In("issue_id", issueIDs).
|
||||
Find(&prs)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,6 @@ func TestPullRequestsNewest(t *testing.T) {
|
|||
},
|
||||
State: "open",
|
||||
SortType: "newest",
|
||||
Labels: []string{},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, count)
|
||||
|
@ -114,7 +113,6 @@ func TestPullRequestsOldest(t *testing.T) {
|
|||
},
|
||||
State: "open",
|
||||
SortType: "oldest",
|
||||
Labels: []string{},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
assert.EqualValues(t, 3, count)
|
||||
|
|
|
@ -239,11 +239,11 @@ type CreateReviewOptions struct {
|
|||
|
||||
// IsOfficialReviewer check if at least one of the provided reviewers can make official reviews in issue (counts towards required approvals)
|
||||
func IsOfficialReviewer(ctx context.Context, issue *Issue, reviewer *user_model.User) (bool, error) {
|
||||
pr, err := GetPullRequestByIssueID(ctx, issue.ID)
|
||||
if err != nil {
|
||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
pr := issue.PullRequest
|
||||
rule, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
|
||||
if err != nil {
|
||||
return false, err
|
||||
|
@ -271,11 +271,10 @@ func IsOfficialReviewer(ctx context.Context, issue *Issue, reviewer *user_model.
|
|||
|
||||
// IsOfficialReviewerTeam check if reviewer in this team can make official reviews in issue (counts towards required approvals)
|
||||
func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organization.Team) (bool, error) {
|
||||
pr, err := GetPullRequestByIssueID(ctx, issue.ID)
|
||||
if err != nil {
|
||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||
return false, err
|
||||
}
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch)
|
||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, issue.PullRequest.BaseRepoID, issue.PullRequest.BaseBranch)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
|
@ -321,6 +321,7 @@ func CreateOrganization(ctx context.Context, org *Organization, owner *user_mode
|
|||
if err = db.Insert(ctx, &OrgUser{
|
||||
UID: owner.ID,
|
||||
OrgID: org.ID,
|
||||
IsPublic: setting.Service.DefaultOrgMemberVisible,
|
||||
}); err != nil {
|
||||
return fmt.Errorf("insert org-user relation: %w", err)
|
||||
}
|
||||
|
|
|
@ -443,7 +443,7 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
|
|||
cond = cond.And(builder.Eq{"email_address.is_activated": opts.IsActivated.Value()})
|
||||
}
|
||||
|
||||
count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.ID = email_address.uid").
|
||||
count, err := db.GetEngine(ctx).Join("INNER", "`user`", "`user`.id = email_address.uid").
|
||||
Where(cond).Count(new(EmailAddress))
|
||||
if err != nil {
|
||||
return nil, 0, fmt.Errorf("Count: %w", err)
|
||||
|
@ -459,7 +459,7 @@ func SearchEmails(ctx context.Context, opts *SearchEmailOptions) ([]*SearchEmail
|
|||
emails := make([]*SearchEmailResult, 0, opts.PageSize)
|
||||
err = db.GetEngine(ctx).Table("email_address").
|
||||
Select("email_address.*, `user`.name, `user`.full_name").
|
||||
Join("INNER", "`user`", "`user`.ID = email_address.uid").
|
||||
Join("INNER", "`user`", "`user`.id = email_address.uid").
|
||||
Where(cond).
|
||||
OrderBy(orderby).
|
||||
Limit(opts.PageSize, (opts.Page-1)*opts.PageSize).
|
||||
|
|
|
@ -100,7 +100,7 @@ func ReadLogs(ctx context.Context, inStorage bool, filename string, offset, limi
|
|||
}
|
||||
|
||||
if err := scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("scan: %w", err)
|
||||
return nil, fmt.Errorf("ReadLogs scan: %w", err)
|
||||
}
|
||||
|
||||
return rows, nil
|
||||
|
|
|
@ -41,6 +41,12 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
|
|||
}
|
||||
logIndex += preStep.LogLength
|
||||
|
||||
// lastHasRunStep is the last step that has run.
|
||||
// For example,
|
||||
// 1. preStep(Success) -> step1(Success) -> step2(Running) -> step3(Waiting) -> postStep(Waiting): lastHasRunStep is step1.
|
||||
// 2. preStep(Success) -> step1(Success) -> step2(Success) -> step3(Success) -> postStep(Success): lastHasRunStep is step3.
|
||||
// 3. preStep(Success) -> step1(Success) -> step2(Failure) -> step3 -> postStep(Waiting): lastHasRunStep is step2.
|
||||
// So its Stopped is the Started of postStep when there are no more steps to run.
|
||||
var lastHasRunStep *actions_model.ActionTaskStep
|
||||
for _, step := range task.Steps {
|
||||
if step.Status.HasRun() {
|
||||
|
@ -56,11 +62,15 @@ func FullSteps(task *actions_model.ActionTask) []*actions_model.ActionTaskStep {
|
|||
Name: postStepName,
|
||||
Status: actions_model.StatusWaiting,
|
||||
}
|
||||
if task.Status.IsDone() {
|
||||
// If the lastHasRunStep is the last step, or it has failed, postStep has started.
|
||||
if lastHasRunStep.Status.IsFailure() || lastHasRunStep == task.Steps[len(task.Steps)-1] {
|
||||
postStep.LogIndex = logIndex
|
||||
postStep.LogLength = task.LogLength - postStep.LogIndex
|
||||
postStep.Status = task.Status
|
||||
postStep.Started = lastHasRunStep.Stopped
|
||||
postStep.Status = actions_model.StatusRunning
|
||||
}
|
||||
if task.Status.IsDone() {
|
||||
postStep.Status = task.Status
|
||||
postStep.Stopped = task.Stopped
|
||||
}
|
||||
ret := make([]*actions_model.ActionTaskStep, 0, len(task.Steps)+2)
|
||||
|
|
|
@ -103,6 +103,40 @@ func TestFullSteps(t *testing.T) {
|
|||
{Name: postStepName, Status: actions_model.StatusSuccess, LogIndex: 100, LogLength: 0, Started: 10100, Stopped: 10100},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "all steps finished but task is running",
|
||||
task: &actions_model.ActionTask{
|
||||
Steps: []*actions_model.ActionTaskStep{
|
||||
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
|
||||
},
|
||||
Status: actions_model.StatusRunning,
|
||||
Started: 10000,
|
||||
Stopped: 0,
|
||||
LogLength: 100,
|
||||
},
|
||||
want: []*actions_model.ActionTaskStep{
|
||||
{Name: preStepName, Status: actions_model.StatusSuccess, LogIndex: 0, LogLength: 10, Started: 10000, Stopped: 10010},
|
||||
{Status: actions_model.StatusSuccess, LogIndex: 10, LogLength: 80, Started: 10010, Stopped: 10090},
|
||||
{Name: postStepName, Status: actions_model.StatusRunning, LogIndex: 90, LogLength: 10, Started: 10090, Stopped: 0},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "skipped task",
|
||||
task: &actions_model.ActionTask{
|
||||
Steps: []*actions_model.ActionTaskStep{
|
||||
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||
},
|
||||
Status: actions_model.StatusSkipped,
|
||||
Started: 0,
|
||||
Stopped: 0,
|
||||
LogLength: 0,
|
||||
},
|
||||
want: []*actions_model.ActionTaskStep{
|
||||
{Name: preStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||
{Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||
{Name: postStepName, Status: actions_model.StatusSkipped, LogIndex: 0, LogLength: 0, Started: 0, Stopped: 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
|
|
@ -150,13 +150,16 @@ func TruncateString(str string, limit int) string {
|
|||
|
||||
// StringsToInt64s converts a slice of string to a slice of int64.
|
||||
func StringsToInt64s(strs []string) ([]int64, error) {
|
||||
ints := make([]int64, len(strs))
|
||||
for i := range strs {
|
||||
n, err := strconv.ParseInt(strs[i], 10, 64)
|
||||
if err != nil {
|
||||
return ints, err
|
||||
if strs == nil {
|
||||
return nil, nil
|
||||
}
|
||||
ints[i] = n
|
||||
ints := make([]int64, 0, len(strs))
|
||||
for _, s := range strs {
|
||||
n, err := strconv.ParseInt(s, 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ints = append(ints, n)
|
||||
}
|
||||
return ints, nil
|
||||
}
|
||||
|
|
|
@ -138,12 +138,13 @@ func TestStringsToInt64s(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
assert.Equal(t, expected, result)
|
||||
}
|
||||
testSuccess(nil, nil)
|
||||
testSuccess([]string{}, []int64{})
|
||||
testSuccess([]string{"-1234"}, []int64{-1234})
|
||||
testSuccess([]string{"1", "4", "16", "64", "256"},
|
||||
[]int64{1, 4, 16, 64, 256})
|
||||
testSuccess([]string{"1", "4", "16", "64", "256"}, []int64{1, 4, 16, 64, 256})
|
||||
|
||||
_, err := StringsToInt64s([]string{"-1", "a", "$"})
|
||||
ints, err := StringsToInt64s([]string{"-1", "a"})
|
||||
assert.Len(t, ints, 0)
|
||||
assert.Error(t, err)
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
|
@ -390,6 +391,9 @@ func (c *Commit) GetSubModules() (*ObjectCache, error) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("GetSubModules scan: %w", err)
|
||||
}
|
||||
|
||||
return c.submoduleCache, nil
|
||||
}
|
||||
|
|
|
@ -283,7 +283,7 @@ type DivergeObject struct {
|
|||
// GetDivergingCommits returns the number of commits a targetBranch is ahead or behind a baseBranch
|
||||
func GetDivergingCommits(ctx context.Context, repoPath, baseBranch, targetBranch string) (do DivergeObject, err error) {
|
||||
cmd := NewCommand(ctx, "rev-list", "--count", "--left-right").
|
||||
AddDynamicArguments(baseBranch + "..." + targetBranch)
|
||||
AddDynamicArguments(baseBranch + "..." + targetBranch).AddArguments("--")
|
||||
stdout, _, err := cmd.RunStdString(&RunOpts{Dir: repoPath})
|
||||
if err != nil {
|
||||
return do, err
|
||||
|
|
|
@ -124,6 +124,10 @@ func (repo *Repository) GetCodeActivityStats(fromTime time.Time, branch string)
|
|||
}
|
||||
}
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
_ = stdoutReader.Close()
|
||||
return fmt.Errorf("GetCodeActivityStats scan: %w", err)
|
||||
}
|
||||
a := make([]*CodeActivityAuthor, 0, len(authors))
|
||||
for _, v := range authors {
|
||||
a = append(a, v)
|
||||
|
|
|
@ -59,8 +59,8 @@ func (g *Manager) start() {
|
|||
go func() {
|
||||
defer func() {
|
||||
close(startupDone)
|
||||
// Close the unused listeners and ignore the error here there's not much we can do with it, they're logged in the CloseProvidedListeners function
|
||||
_ = CloseProvidedListeners()
|
||||
// Close the unused listeners
|
||||
closeProvidedListeners()
|
||||
}()
|
||||
// Wait for all servers to be created
|
||||
g.createServerCond.L.Lock()
|
||||
|
|
|
@ -129,25 +129,17 @@ func getProvidedFDs() (savedErr error) {
|
|||
return savedErr
|
||||
}
|
||||
|
||||
// CloseProvidedListeners closes all unused provided listeners.
|
||||
func CloseProvidedListeners() error {
|
||||
// closeProvidedListeners closes all unused provided listeners.
|
||||
func closeProvidedListeners() {
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
var returnableError error
|
||||
for _, l := range providedListeners {
|
||||
err := l.Close()
|
||||
if err != nil {
|
||||
log.Error("Error in closing unused provided listener: %v", err)
|
||||
if returnableError != nil {
|
||||
returnableError = fmt.Errorf("%v & %w", returnableError, err)
|
||||
} else {
|
||||
returnableError = err
|
||||
}
|
||||
}
|
||||
}
|
||||
providedListeners = []net.Listener{}
|
||||
|
||||
return returnableError
|
||||
}
|
||||
|
||||
// DefaultGetListener obtains a listener for the stream-oriented local network address:
|
||||
|
|
|
@ -39,6 +39,8 @@ import (
|
|||
const (
|
||||
unicodeNormalizeName = "unicodeNormalize"
|
||||
maxBatchSize = 16
|
||||
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
|
||||
fuzzyDenominator = 4
|
||||
)
|
||||
|
||||
func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
|
||||
|
@ -239,15 +241,12 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int
|
|||
keywordQuery query.Query
|
||||
)
|
||||
|
||||
if opts.IsKeywordFuzzy {
|
||||
phraseQuery := bleve.NewMatchPhraseQuery(opts.Keyword)
|
||||
phraseQuery.FieldVal = "Content"
|
||||
phraseQuery.Analyzer = repoIndexerAnalyzer
|
||||
keywordQuery = phraseQuery
|
||||
} else {
|
||||
prefixQuery := bleve.NewPrefixQuery(opts.Keyword)
|
||||
prefixQuery.FieldVal = "Content"
|
||||
keywordQuery = prefixQuery
|
||||
if opts.IsKeywordFuzzy {
|
||||
phraseQuery.Fuzziness = len(opts.Keyword) / fuzzyDenominator
|
||||
}
|
||||
|
||||
if len(opts.RepoIDs) > 0 {
|
||||
|
|
|
@ -20,17 +20,11 @@ func NumericEqualityQuery(value int64, field string) *query.NumericRangeQuery {
|
|||
}
|
||||
|
||||
// MatchPhraseQuery generates a match phrase query for the given phrase, field and analyzer
|
||||
func MatchPhraseQuery(matchPhrase, field, analyzer string) *query.MatchPhraseQuery {
|
||||
func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchPhraseQuery {
|
||||
q := bleve.NewMatchPhraseQuery(matchPhrase)
|
||||
q.FieldVal = field
|
||||
q.Analyzer = analyzer
|
||||
return q
|
||||
}
|
||||
|
||||
// PrefixQuery generates a match prefix query for the given prefix and field
|
||||
func PrefixQuery(matchPrefix, field string) *query.PrefixQuery {
|
||||
q := bleve.NewPrefixQuery(matchPrefix)
|
||||
q.FieldVal = field
|
||||
q.Fuzziness = fuzziness
|
||||
return q
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,11 @@ func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
|
|||
})
|
||||
}
|
||||
|
||||
const maxBatchSize = 16
|
||||
const (
|
||||
maxBatchSize = 16
|
||||
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
|
||||
fuzzyDenominator = 4
|
||||
)
|
||||
|
||||
// IndexerData an update to the issue indexer
|
||||
type IndexerData internal.IndexerData
|
||||
|
@ -156,19 +160,16 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
|
|||
var queries []query.Query
|
||||
|
||||
if options.Keyword != "" {
|
||||
fuzziness := 0
|
||||
if options.IsFuzzyKeyword {
|
||||
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer),
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer),
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer),
|
||||
}...))
|
||||
} else {
|
||||
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
|
||||
inner_bleve.PrefixQuery(options.Keyword, "title"),
|
||||
inner_bleve.PrefixQuery(options.Keyword, "content"),
|
||||
inner_bleve.PrefixQuery(options.Keyword, "comments"),
|
||||
}...))
|
||||
fuzziness = len(options.Keyword) / fuzzyDenominator
|
||||
}
|
||||
|
||||
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
|
||||
inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
|
||||
}...))
|
||||
}
|
||||
|
||||
if len(options.RepoIDs) > 0 || options.AllPublic {
|
||||
|
|
|
@ -515,9 +515,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByCreatedDesc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByCreatedDesc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -533,9 +531,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByUpdatedDesc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByUpdatedDesc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -551,9 +547,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByCommentsDesc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByCommentsDesc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -569,9 +563,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByDeadlineDesc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByDeadlineDesc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -587,9 +579,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByCreatedAsc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByCreatedAsc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -605,9 +595,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByUpdatedAsc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByUpdatedAsc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -623,9 +611,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByCommentsAsc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByCommentsAsc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
@ -641,9 +627,7 @@ var cases = []*testIndexerCase{
|
|||
{
|
||||
Name: "SortByDeadlineAsc",
|
||||
SearchOptions: &internal.SearchOptions{
|
||||
Paginator: &db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
Paginator: &db.ListOptionsAll,
|
||||
SortBy: internal.SortByDeadlineAsc,
|
||||
},
|
||||
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
|
||||
|
|
|
@ -61,9 +61,7 @@ func getIssueIndexerData(ctx context.Context, issueID int64) (*internal.IndexerD
|
|||
)
|
||||
{
|
||||
reviews, err := issue_model.FindReviews(ctx, issue_model.FindReviewOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IssueID: issueID,
|
||||
OfficialOnly: false,
|
||||
})
|
||||
|
|
|
@ -6,6 +6,7 @@ package markup
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"html"
|
||||
"io"
|
||||
"regexp"
|
||||
|
@ -123,6 +124,9 @@ func (Renderer) fallbackRender(input io.Reader, tmpBlock *bufio.Writer) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
if err = scan.Err(); err != nil {
|
||||
return fmt.Errorf("fallbackRender scan: %w", err)
|
||||
}
|
||||
|
||||
_, err = tmpBlock.WriteString("</pre>")
|
||||
if err != nil {
|
||||
|
|
|
@ -54,7 +54,7 @@ func Sorted[S ~[]E, E cmp.Ordered](values S) S {
|
|||
return values
|
||||
}
|
||||
|
||||
// TODO: Replace with "maps.Values" once available
|
||||
// TODO: Replace with "maps.Values" once available, current it only in golang.org/x/exp/maps but not in standard library
|
||||
func ValuesOfMap[K comparable, V any](m map[K]V) []V {
|
||||
values := make([]V, 0, len(m))
|
||||
for _, v := range m {
|
||||
|
@ -62,3 +62,12 @@ func ValuesOfMap[K comparable, V any](m map[K]V) []V {
|
|||
}
|
||||
return values
|
||||
}
|
||||
|
||||
// TODO: Replace with "maps.Keys" once available, current it only in golang.org/x/exp/maps but not in standard library
|
||||
func KeysOfMap[K comparable, V any](m map[K]V) []K {
|
||||
keys := make([]K, 0, len(m))
|
||||
for k := range m {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
return keys
|
||||
}
|
||||
|
|
3
options/license/threeparttable
Normal file
3
options/license/threeparttable
Normal file
|
@ -0,0 +1,3 @@
|
|||
This file may be distributed, modified, and used in other works with just
|
||||
one restriction: modified versions must clearly indicate the modification
|
||||
(a name change, or a displayed message, or ?).
|
|
@ -115,6 +115,7 @@ loading = Loading…
|
|||
error = Error
|
||||
error404 = The page you are trying to reach either <strong>does not exist</strong> or <strong>you are not authorized</strong> to view it.
|
||||
go_back = Go Back
|
||||
invalid_data = Invalid data: %v
|
||||
|
||||
never = Never
|
||||
unknown = Unknown
|
||||
|
@ -1333,6 +1334,8 @@ editor.file_editing_no_longer_exists = The file being edited, "%s", no longer ex
|
|||
editor.file_deleting_no_longer_exists = The file being deleted, "%s", no longer exists in this repository.
|
||||
editor.file_changed_while_editing = The file contents have changed since you started editing. <a target="_blank" rel="noopener noreferrer" href="%s">Click here</a> to see them or <strong>Commit Changes again</strong> to overwrite them.
|
||||
editor.file_already_exists = A file named "%s" already exists in this repository.
|
||||
editor.commit_id_not_matching = The Commit ID does not match the ID when you began editing. Commit into a patch branch and then merge.
|
||||
editor.push_out_of_date = The push appears to be out of date.
|
||||
editor.commit_empty_file_header = Commit an empty file
|
||||
editor.commit_empty_file_text = The file you're about to commit is empty. Proceed?
|
||||
editor.no_changes_to_show = There are no changes to show.
|
||||
|
@ -3133,7 +3136,7 @@ auths.tip.nextcloud = Register a new OAuth consumer on your instance using the f
|
|||
auths.tip.dropbox = Create a new application at https://www.dropbox.com/developers/apps
|
||||
auths.tip.facebook = Register a new application at https://developers.facebook.com/apps and add the product "Facebook Login"
|
||||
auths.tip.github = Register a new OAuth application on https://github.com/settings/applications/new
|
||||
auths.tip.gitlab = Register a new application on https://gitlab.com/profile/applications
|
||||
auths.tip.gitlab_new = Register a new application on https://gitlab.com/-/profile/applications
|
||||
auths.tip.google_plus = Obtain OAuth2 client credentials from the Google API console at https://console.developers.google.com/
|
||||
auths.tip.openid_connect = Use the OpenID Connect Discovery URL (<server>/.well-known/openid-configuration) to specify the endpoints
|
||||
auths.tip.twitter = Go to https://dev.twitter.com/apps, create an application and ensure that the “Allow this application to be used to Sign in with Twitter” option is enabled
|
||||
|
@ -3671,6 +3674,7 @@ runs.pushed_by = pushed by
|
|||
runs.workflow = Workflow
|
||||
runs.invalid_workflow_helper = Workflow config file is invalid. Please check your config file: %s
|
||||
runs.no_matching_online_runner_helper = No matching online runner with label: %s
|
||||
runs.no_job_without_needs = The workflow must contain at least one job without dependencies.
|
||||
runs.actor = Actor
|
||||
runs.status = Status
|
||||
runs.actors_no_select = All actors
|
||||
|
|
1383
package-lock.json
generated
1383
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
@ -84,7 +84,7 @@
|
|||
"eslint-plugin-vue": "9.23.0",
|
||||
"eslint-plugin-vue-scoped-css": "2.7.2",
|
||||
"eslint-plugin-wc": "2.0.4",
|
||||
"jsdom": "24.0.0",
|
||||
"happy-dom": "14.2.0",
|
||||
"markdownlint-cli": "0.39.0",
|
||||
"postcss-html": "1.6.0",
|
||||
"stylelint": "16.2.1",
|
||||
|
|
|
@ -27,7 +27,7 @@ export default {
|
|||
* Maximum time expect() should wait for the condition to be met.
|
||||
* For example in `await expect(locator).toHaveText();`
|
||||
*/
|
||||
timeout: 2000
|
||||
timeout: 2000,
|
||||
},
|
||||
|
||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
||||
|
|
|
@ -874,10 +874,11 @@ func EditIssue(ctx *context.APIContext) {
|
|||
}
|
||||
if form.State != nil {
|
||||
if issue.IsPull {
|
||||
if pr, err := issue.GetPullRequest(ctx); err != nil {
|
||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
|
||||
return
|
||||
} else if pr.HasMerged {
|
||||
}
|
||||
if issue.PullRequest.HasMerged {
|
||||
ctx.Error(http.StatusPreconditionFailed, "MergedPRState", "cannot change state of this pull request, it was already merged")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -240,18 +240,12 @@ func ListPinnedPullRequests(ctx *context.APIContext) {
|
|||
}
|
||||
|
||||
apiPrs := make([]*api.PullRequest, len(issues))
|
||||
if err := issues.LoadPullRequests(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadPullRequests", err)
|
||||
return
|
||||
}
|
||||
for i, currentIssue := range issues {
|
||||
pr, err := currentIssue.GetPullRequest(ctx)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetPullRequest", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = pr.LoadIssue(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadIssue", err)
|
||||
return
|
||||
}
|
||||
|
||||
pr := currentIssue.PullRequest
|
||||
if err = pr.LoadAttributes(ctx); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
|
@ -96,13 +97,17 @@ func ListPullRequests(ctx *context.APIContext) {
|
|||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
labelIDs, err := base.StringsToInt64s(ctx.FormStrings("labels"))
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "PullRequests", err)
|
||||
return
|
||||
}
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
prs, maxResults, err := issues_model.PullRequests(ctx, ctx.Repo.Repository.ID, &issues_model.PullRequestsOptions{
|
||||
ListOptions: listOptions,
|
||||
State: ctx.FormTrim("state"),
|
||||
SortType: ctx.FormTrim("sort"),
|
||||
Labels: ctx.FormStrings("labels"),
|
||||
Labels: labelIDs,
|
||||
MilestoneID: ctx.FormInt64("milestone"),
|
||||
})
|
||||
if err != nil {
|
||||
|
|
|
@ -75,6 +75,10 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) {
|
|||
updates = append(updates, option)
|
||||
if repo.IsEmpty && (refFullName.BranchName() == "master" || refFullName.BranchName() == "main") {
|
||||
// put the master/main branch first
|
||||
// FIXME: It doesn't always work, since the master/main branch may not be the first batch of updates.
|
||||
// If the user pushes many branches at once, the Git hook will call the internal API in batches, rather than all at once.
|
||||
// See https://github.com/go-gitea/gitea/blob/cb52b17f92e2d2293f7c003649743464492bca48/cmd/hook.go#L27
|
||||
// If the user executes `git push origin --all` and pushes more than 30 branches, the master/main may not be the default branch.
|
||||
copy(updates[1:], updates)
|
||||
updates[0] = option
|
||||
}
|
||||
|
|
|
@ -275,9 +275,7 @@ func ViewUser(ctx *context.Context) {
|
|||
}
|
||||
|
||||
repos, count, err := repo_model.SearchRepository(ctx, &repo_model.SearchRepoOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
OwnerID: u.ID,
|
||||
OrderBy: db.SearchOrderByAlphabetically,
|
||||
Private: true,
|
||||
|
@ -300,9 +298,7 @@ func ViewUser(ctx *context.Context) {
|
|||
ctx.Data["EmailsTotal"] = len(emails)
|
||||
|
||||
orgs, err := db.Find[org_model.Organization](ctx, org_model.FindOrgOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
UserID: u.ID,
|
||||
IncludePrivate: true,
|
||||
})
|
||||
|
|
|
@ -104,8 +104,13 @@ func List(ctx *context.Context) {
|
|||
workflows = append(workflows, workflow)
|
||||
continue
|
||||
}
|
||||
// Check whether have matching runner
|
||||
// The workflow must contain at least one job without "needs". Otherwise, a deadlock will occur and no jobs will be able to run.
|
||||
hasJobWithoutNeeds := false
|
||||
// Check whether have matching runner and a job without "needs"
|
||||
for _, j := range wf.Jobs {
|
||||
if !hasJobWithoutNeeds && len(j.Needs()) == 0 {
|
||||
hasJobWithoutNeeds = true
|
||||
}
|
||||
runsOnList := j.RunsOn()
|
||||
for _, ro := range runsOnList {
|
||||
if strings.Contains(ro, "${{") {
|
||||
|
@ -123,6 +128,9 @@ func List(ctx *context.Context) {
|
|||
break
|
||||
}
|
||||
}
|
||||
if !hasJobWithoutNeeds {
|
||||
workflow.ErrMsg = ctx.Locale.TrString("actions.runs.no_job_without_needs")
|
||||
}
|
||||
workflows = append(workflows, workflow)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,12 +353,25 @@ func Rerun(ctx *context_module.Context) {
|
|||
return
|
||||
}
|
||||
|
||||
if jobIndexStr != "" {
|
||||
jobs = []*actions_model.ActionRunJob{job}
|
||||
if jobIndexStr == "" { // rerun all jobs
|
||||
for _, j := range jobs {
|
||||
// if the job has needs, it should be set to "blocked" status to wait for other jobs
|
||||
shouldBlock := len(j.Needs) > 0
|
||||
if err := rerunJob(ctx, j, shouldBlock); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx.JSON(http.StatusOK, struct{}{})
|
||||
return
|
||||
}
|
||||
|
||||
for _, j := range jobs {
|
||||
if err := rerunJob(ctx, j); err != nil {
|
||||
rerunJobs := actions_service.GetAllRerunJobs(job, jobs)
|
||||
|
||||
for _, j := range rerunJobs {
|
||||
// jobs other than the specified one should be set to "blocked" status
|
||||
shouldBlock := j.JobID != job.JobID
|
||||
if err := rerunJob(ctx, j, shouldBlock); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
@ -367,7 +380,7 @@ func Rerun(ctx *context_module.Context) {
|
|||
ctx.JSON(http.StatusOK, struct{}{})
|
||||
}
|
||||
|
||||
func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) error {
|
||||
func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob, shouldBlock bool) error {
|
||||
status := job.Status
|
||||
if !status.IsDone() {
|
||||
return nil
|
||||
|
@ -375,6 +388,9 @@ func rerunJob(ctx *context_module.Context, job *actions_model.ActionRunJob) erro
|
|||
|
||||
job.TaskID = 0
|
||||
job.Status = actions_model.StatusWaiting
|
||||
if shouldBlock {
|
||||
job.Status = actions_model.StatusBlocked
|
||||
}
|
||||
job.Started = 0
|
||||
job.Stopped = 0
|
||||
|
||||
|
|
|
@ -367,7 +367,7 @@ func Diff(ctx *context.Context) {
|
|||
ctx.Data["Commit"] = commit
|
||||
ctx.Data["Diff"] = diff
|
||||
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptions{ListAll: true})
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, commitID, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
log.Error("GetLatestCommitStatus: %v", err)
|
||||
}
|
||||
|
|
|
@ -698,9 +698,7 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor
|
|||
|
||||
branches, err = git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: repo.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -755,9 +753,7 @@ func CompareDiff(ctx *context.Context) {
|
|||
|
||||
headBranches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: ci.HeadRepo.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -980,5 +976,8 @@ func getExcerptLines(commit *git.Commit, filePath string, idxLeft, idxRight, chu
|
|||
}
|
||||
diffLines = append(diffLines, diffLine)
|
||||
}
|
||||
if err = scanner.Err(); err != nil {
|
||||
return nil, fmt.Errorf("getExcerptLines scan: %w", err)
|
||||
}
|
||||
return diffLines, nil
|
||||
}
|
||||
|
|
|
@ -374,9 +374,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b
|
|||
ctx.Error(http.StatusInternalServerError, err.Error())
|
||||
}
|
||||
} else if models.IsErrCommitIDDoesNotMatch(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(ctx.Repo.CommitID)), tplEditFile, &form)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.commit_id_not_matching"), tplEditFile, &form)
|
||||
} else if git.IsErrPushOutOfDate(err) {
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.file_changed_while_editing", ctx.Repo.RepoLink+"/compare/"+util.PathEscapeSegments(form.LastCommit)+"..."+util.PathEscapeSegments(form.NewBranchName)), tplEditFile, &form)
|
||||
ctx.RenderWithErr(ctx.Tr("repo.editor.push_out_of_date"), tplEditFile, &form)
|
||||
} else if git.IsErrPushRejected(err) {
|
||||
errPushRej := err.(*git.ErrPushRejected)
|
||||
if len(errPushRej.Message) == 0 {
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/modules/base"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
)
|
||||
|
||||
|
@ -17,7 +18,7 @@ const (
|
|||
// FindFiles render the page to find repository files
|
||||
func FindFiles(ctx *context.Context) {
|
||||
path := ctx.Params("*")
|
||||
ctx.Data["TreeLink"] = ctx.Repo.RepoLink + "/src/" + path
|
||||
ctx.Data["DataLink"] = ctx.Repo.RepoLink + "/tree-list/" + path
|
||||
ctx.Data["TreeLink"] = ctx.Repo.RepoLink + "/src/" + util.PathEscapeSegments(path)
|
||||
ctx.Data["DataLink"] = ctx.Repo.RepoLink + "/tree-list/" + util.PathEscapeSegments(path)
|
||||
ctx.HTML(http.StatusOK, tplFindFiles)
|
||||
}
|
||||
|
|
|
@ -192,8 +192,7 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
if len(selectLabels) > 0 {
|
||||
labelIDs, err = base.StringsToInt64s(strings.Split(selectLabels, ","))
|
||||
if err != nil {
|
||||
ctx.ServerError("StringsToInt64s", err)
|
||||
return
|
||||
ctx.Flash.Error(ctx.Tr("invalid_data", selectLabels), true)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -451,13 +450,13 @@ func issues(ctx *context.Context, milestoneID, projectID int64, isPullOption opt
|
|||
linkStr := "%s?q=%s&type=%s&sort=%s&state=%s&labels=%s&milestone=%d&project=%d&assignee=%d&poster=%d&archived=%t"
|
||||
ctx.Data["AllStatesLink"] = fmt.Sprintf(linkStr, ctx.Link,
|
||||
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "all", url.QueryEscape(selectLabels),
|
||||
mentionedID, projectID, assigneeID, posterID, archived)
|
||||
milestoneID, projectID, assigneeID, posterID, archived)
|
||||
ctx.Data["OpenLink"] = fmt.Sprintf(linkStr, ctx.Link,
|
||||
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "open", url.QueryEscape(selectLabels),
|
||||
mentionedID, projectID, assigneeID, posterID, archived)
|
||||
milestoneID, projectID, assigneeID, posterID, archived)
|
||||
ctx.Data["ClosedLink"] = fmt.Sprintf(linkStr, ctx.Link,
|
||||
url.QueryEscape(keyword), url.QueryEscape(viewType), url.QueryEscape(sortType), "closed", url.QueryEscape(selectLabels),
|
||||
mentionedID, projectID, assigneeID, posterID, archived)
|
||||
milestoneID, projectID, assigneeID, posterID, archived)
|
||||
ctx.Data["SelLabelIDs"] = labelIDs
|
||||
ctx.Data["SelectLabels"] = selectLabels
|
||||
ctx.Data["ViewType"] = viewType
|
||||
|
|
|
@ -505,7 +505,7 @@ func PrepareMergedViewPullInfo(ctx *context.Context, issue *issues_model.Issue)
|
|||
|
||||
if len(compareInfo.Commits) != 0 {
|
||||
sha := compareInfo.Commits[0].ID.String()
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptions{ListAll: true})
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
|
@ -567,7 +567,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
|||
ctx.ServerError(fmt.Sprintf("GetRefCommitID(%s)", pull.GetGitRefName()), err)
|
||||
return nil
|
||||
}
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true})
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
|
@ -659,7 +659,7 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C
|
|||
return nil
|
||||
}
|
||||
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true})
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetLatestCommitStatus", err)
|
||||
return nil
|
||||
|
|
|
@ -136,7 +136,7 @@ func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions)
|
|||
}
|
||||
|
||||
if canReadActions {
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptions{ListAll: true})
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
|
@ -618,26 +618,31 @@ func SearchRepo(ctx *context.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
var err error
|
||||
// To improve performance when only the count is requested
|
||||
if ctx.FormBool("count_only") {
|
||||
if count, err := repo_model.CountRepository(ctx, opts); err != nil {
|
||||
log.Error("CountRepository: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, nil) // frontend JS doesn't handle error response (same as below)
|
||||
} else {
|
||||
ctx.SetTotalCountHeader(count)
|
||||
ctx.JSONOK()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
repos, count, err := repo_model.SearchRepository(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, api.SearchError{
|
||||
OK: false,
|
||||
Error: err.Error(),
|
||||
})
|
||||
log.Error("SearchRepository: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
ctx.SetTotalCountHeader(count)
|
||||
|
||||
// To improve performance when only the count is requested
|
||||
if ctx.FormBool("count_only") {
|
||||
return
|
||||
}
|
||||
|
||||
latestCommitStatuses, err := commitstatus_service.FindReposLastestCommitStatuses(ctx, repos)
|
||||
if err != nil {
|
||||
log.Error("FindReposLastestCommitStatuses: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, nil)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -679,9 +684,7 @@ func GetBranchesList(ctx *context.Context) {
|
|||
branchOpts := git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
}
|
||||
branches, err := git_model.FindBranchNames(ctx, branchOpts)
|
||||
if err != nil {
|
||||
|
@ -714,9 +717,7 @@ func PrepareBranchList(ctx *context.Context) {
|
|||
branchOpts := git_model.FindBranchOptions{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
}
|
||||
brs, err := git_model.FindBranchNames(ctx, branchOpts)
|
||||
if err != nil {
|
||||
|
|
|
@ -364,7 +364,7 @@ func loadLatestCommitData(ctx *context.Context, latestCommit *git.Commit) bool {
|
|||
ctx.Data["LatestCommitVerification"] = verification
|
||||
ctx.Data["LatestCommitUser"] = user_model.ValidateCommitWithEmail(ctx, latestCommit)
|
||||
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptions{ListAll: true})
|
||||
statuses, _, err := git_model.GetLatestCommitStatus(ctx, ctx.Repo.Repository.ID, latestCommit.ID.String(), db.ListOptionsAll)
|
||||
if err != nil {
|
||||
log.Error("GetLatestCommitStatus: %v", err)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ import (
|
|||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/gitrepo"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
"code.gitea.io/gitea/modules/markup"
|
||||
"code.gitea.io/gitea/modules/markup/markdown"
|
||||
"code.gitea.io/gitea/modules/optional"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
|
@ -35,6 +37,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
|||
prepareContextForCommonProfile(ctx)
|
||||
|
||||
ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
|
||||
ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID)
|
||||
ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate
|
||||
if setting.Service.UserLocationMapURL != "" {
|
||||
ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location)
|
||||
|
@ -46,6 +49,17 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) {
|
|||
return
|
||||
}
|
||||
ctx.Data["OpenIDs"] = openIDs
|
||||
if len(ctx.ContextUser.Description) != 0 {
|
||||
content, err := markdown.RenderString(&markup.RenderContext{
|
||||
Metas: map[string]string{"mode": "document"},
|
||||
Ctx: ctx,
|
||||
}, ctx.ContextUser.Description)
|
||||
if err != nil {
|
||||
ctx.ServerError("RenderString", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["RenderedDescription"] = content
|
||||
}
|
||||
|
||||
showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
||||
orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{
|
||||
|
|
|
@ -529,17 +529,14 @@ func buildIssueOverview(ctx *context.Context, unitType unit.Type) {
|
|||
|
||||
// Get IDs for labels (a filter option for issues/pulls).
|
||||
// Required for IssuesOptions.
|
||||
var labelIDs []int64
|
||||
selectedLabels := ctx.FormString("labels")
|
||||
if len(selectedLabels) > 0 && selectedLabels != "0" {
|
||||
var err error
|
||||
labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
|
||||
opts.LabelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
|
||||
if err != nil {
|
||||
ctx.ServerError("StringsToInt64s", err)
|
||||
return
|
||||
ctx.Flash.Error(ctx.Tr("invalid_data", selectedLabels), true)
|
||||
}
|
||||
}
|
||||
opts.LabelIDs = labelIDs
|
||||
|
||||
// ------------------------------
|
||||
// Get issues as defined by opts.
|
||||
|
|
|
@ -144,6 +144,12 @@ func getNotifications(ctx *context.Context) {
|
|||
ctx.ServerError("LoadIssues", err)
|
||||
return
|
||||
}
|
||||
|
||||
if err = notifications.LoadIssuePullRequests(ctx); err != nil {
|
||||
ctx.ServerError("LoadIssuePullRequests", err)
|
||||
return
|
||||
}
|
||||
|
||||
notifications = notifications.Without(failures)
|
||||
failCount += len(failures)
|
||||
|
||||
|
@ -262,8 +268,7 @@ func NotificationSubscriptions(ctx *context.Context) {
|
|||
var err error
|
||||
labelIDs, err = base.StringsToInt64s(strings.Split(selectedLabels, ","))
|
||||
if err != nil {
|
||||
ctx.ServerError("StringsToInt64s", err)
|
||||
return
|
||||
ctx.Flash.Error(ctx.Tr("invalid_data", selectedLabels), true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,7 +79,7 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er
|
|||
}
|
||||
ctxname := fmt.Sprintf("%s / %s (%s)", runName, job.Name, event)
|
||||
state := toCommitStatus(job.Status)
|
||||
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptions{ListAll: true}); err == nil {
|
||||
if statuses, _, err := git_model.GetLatestCommitStatus(ctx, repo.ID, sha, db.ListOptionsAll); err == nil {
|
||||
for _, v := range statuses {
|
||||
if v.Context == ctxname {
|
||||
if v.State == state {
|
||||
|
|
|
@ -515,6 +515,12 @@ func (*actionsNotifier) MergePullRequest(ctx context.Context, doer *user_model.U
|
|||
}
|
||||
|
||||
func (n *actionsNotifier) PushCommits(ctx context.Context, pusher *user_model.User, repo *repo_model.Repository, opts *repository.PushUpdateOptions, commits *repository.PushCommits) {
|
||||
commitID, _ := git.NewIDFromString(opts.NewCommitID)
|
||||
if commitID.IsZero() {
|
||||
log.Trace("new commitID is empty")
|
||||
return
|
||||
}
|
||||
|
||||
ctx = withMethod(ctx, "PushCommits")
|
||||
|
||||
apiPusher := convert.ToUser(ctx, pusher, nil)
|
||||
|
@ -547,9 +553,9 @@ func (n *actionsNotifier) CreateRef(ctx context.Context, pusher *user_model.User
|
|||
apiRepo := convert.ToRepo(ctx, repo, access_model.Permission{AccessMode: perm_model.AccessModeNone})
|
||||
|
||||
newNotifyInput(repo, pusher, webhook_module.HookEventCreate).
|
||||
WithRef(refFullName.ShortName()). // FIXME: should we use a full ref name
|
||||
WithRef(refFullName.String()).
|
||||
WithPayload(&api.CreatePayload{
|
||||
Ref: refFullName.ShortName(),
|
||||
Ref: refFullName.String(),
|
||||
Sha: refID,
|
||||
RefType: refFullName.RefType(),
|
||||
Repo: apiRepo,
|
||||
|
@ -566,7 +572,7 @@ func (n *actionsNotifier) DeleteRef(ctx context.Context, pusher *user_model.User
|
|||
|
||||
newNotifyInput(repo, pusher, webhook_module.HookEventDelete).
|
||||
WithPayload(&api.DeletePayload{
|
||||
Ref: refFullName.ShortName(),
|
||||
Ref: refFullName.String(),
|
||||
RefType: refFullName.RefType(),
|
||||
PusherType: api.PusherTypeUser,
|
||||
Repo: apiRepo,
|
||||
|
@ -623,6 +629,10 @@ func (n *actionsNotifier) UpdateRelease(ctx context.Context, doer *user_model.Us
|
|||
}
|
||||
|
||||
func (n *actionsNotifier) DeleteRelease(ctx context.Context, doer *user_model.User, rel *repo_model.Release) {
|
||||
if rel.IsTag {
|
||||
// has sent same action in `PushCommits`, so skip it.
|
||||
return
|
||||
}
|
||||
ctx = withMethod(ctx, "DeleteRelease")
|
||||
notifyRelease(ctx, doer, rel, api.HookReleaseDeleted)
|
||||
}
|
||||
|
|
38
services/actions/rerun.go
Normal file
38
services/actions/rerun.go
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
"code.gitea.io/gitea/modules/container"
|
||||
)
|
||||
|
||||
// GetAllRerunJobs get all jobs that need to be rerun when job should be rerun
|
||||
func GetAllRerunJobs(job *actions_model.ActionRunJob, allJobs []*actions_model.ActionRunJob) []*actions_model.ActionRunJob {
|
||||
rerunJobs := []*actions_model.ActionRunJob{job}
|
||||
rerunJobsIDSet := make(container.Set[string])
|
||||
rerunJobsIDSet.Add(job.JobID)
|
||||
|
||||
for {
|
||||
found := false
|
||||
for _, j := range allJobs {
|
||||
if rerunJobsIDSet.Contains(j.JobID) {
|
||||
continue
|
||||
}
|
||||
for _, need := range j.Needs {
|
||||
if rerunJobsIDSet.Contains(need) {
|
||||
found = true
|
||||
rerunJobs = append(rerunJobs, j)
|
||||
rerunJobsIDSet.Add(j.JobID)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return rerunJobs
|
||||
}
|
48
services/actions/rerun_test.go
Normal file
48
services/actions/rerun_test.go
Normal file
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2024 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package actions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestGetAllRerunJobs(t *testing.T) {
|
||||
job1 := &actions_model.ActionRunJob{JobID: "job1"}
|
||||
job2 := &actions_model.ActionRunJob{JobID: "job2", Needs: []string{"job1"}}
|
||||
job3 := &actions_model.ActionRunJob{JobID: "job3", Needs: []string{"job2"}}
|
||||
job4 := &actions_model.ActionRunJob{JobID: "job4", Needs: []string{"job2", "job3"}}
|
||||
|
||||
jobs := []*actions_model.ActionRunJob{job1, job2, job3, job4}
|
||||
|
||||
testCases := []struct {
|
||||
job *actions_model.ActionRunJob
|
||||
rerunJobs []*actions_model.ActionRunJob
|
||||
}{
|
||||
{
|
||||
job1,
|
||||
[]*actions_model.ActionRunJob{job1, job2, job3, job4},
|
||||
},
|
||||
{
|
||||
job2,
|
||||
[]*actions_model.ActionRunJob{job2, job3, job4},
|
||||
},
|
||||
{
|
||||
job3,
|
||||
[]*actions_model.ActionRunJob{job3, job4},
|
||||
},
|
||||
{
|
||||
job4,
|
||||
[]*actions_model.ActionRunJob{job4},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
rerunJobs := GetAllRerunJobs(tc.job, jobs)
|
||||
assert.ElementsMatch(t, tc.rerunJobs, rerunJobs)
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
|
@ -29,40 +28,33 @@ func (s *Session) Name() string {
|
|||
// object for that uid.
|
||||
// Returns nil if there is no user uid stored in the session.
|
||||
func (s *Session) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) {
|
||||
user := SessionUser(req.Context(), sess)
|
||||
if user != nil {
|
||||
return user, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SessionUser returns the user object corresponding to the "uid" session variable.
|
||||
func SessionUser(ctx context.Context, sess SessionStore) *user_model.User {
|
||||
if sess == nil {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get user ID
|
||||
uid := sess.Get("uid")
|
||||
if uid == nil {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
log.Trace("Session Authorization: Found user[%d]", uid)
|
||||
|
||||
id, ok := uid.(int64)
|
||||
if !ok {
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Get user object
|
||||
user, err := user_model.GetUserByID(ctx, id)
|
||||
user, err := user_model.GetUserByID(req.Context(), id)
|
||||
if err != nil {
|
||||
if !user_model.IsErrUserNotExist(err) {
|
||||
log.Error("GetUserById: %v", err)
|
||||
log.Error("GetUserByID: %v", err)
|
||||
// Return the err as-is to keep current signed-in session, in case the err is something like context.Canceled. Otherwise non-existing user (nil, nil) will make the caller clear the signed-in session.
|
||||
return nil, err
|
||||
}
|
||||
return nil
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
log.Trace("Session Authorization: Logged in user %-v", user)
|
||||
return user
|
||||
return user, nil
|
||||
}
|
||||
|
|
|
@ -61,8 +61,9 @@ func ToNotificationThread(ctx context.Context, n *activities_model.Notification)
|
|||
result.Subject.LatestCommentHTMLURL = comment.HTMLURL(ctx)
|
||||
}
|
||||
|
||||
pr, _ := n.Issue.GetPullRequest(ctx)
|
||||
if pr != nil && pr.HasMerged {
|
||||
if err := n.Issue.LoadPullRequest(ctx); err == nil &&
|
||||
n.Issue.PullRequest != nil &&
|
||||
n.Issue.PullRequest.HasMerged {
|
||||
result.Subject.State = "merged"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,11 @@ func checkAuthorizedKeys(ctx context.Context, logger log.Logger, autofix bool) e
|
|||
}
|
||||
linesInAuthorizedKeys.Add(line)
|
||||
}
|
||||
f.Close()
|
||||
if err = scanner.Err(); err != nil {
|
||||
return fmt.Errorf("scan: %w", err)
|
||||
}
|
||||
// although there is a "defer close" above, here close explicitly before the generating, because it needs to open the file for writing again
|
||||
_ = f.Close()
|
||||
|
||||
// now we regenerate and check if there are any lines missing
|
||||
regenerated := &bytes.Buffer{}
|
||||
|
|
|
@ -153,7 +153,7 @@ func GetPullRequestCommitStatusState(ctx context.Context, pr *issues_model.PullR
|
|||
return "", fmt.Errorf("LoadBaseRepo: %w", err)
|
||||
}
|
||||
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
|
||||
commitStatuses, _, err := git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("GetLatestCommitStatus: %w", err)
|
||||
}
|
||||
|
|
|
@ -893,7 +893,7 @@ func getAllCommitStatus(ctx context.Context, gitRepo *git.Repository, pr *issues
|
|||
return nil, nil, shaErr
|
||||
}
|
||||
|
||||
statuses, _, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptions{ListAll: true})
|
||||
statuses, _, err = git_model.GetLatestCommitStatus(ctx, pr.BaseRepo.ID, sha, db.ListOptionsAll)
|
||||
lastStatus = git_model.CalcCommitStatus(statuses)
|
||||
return statuses, lastStatus, err
|
||||
}
|
||||
|
|
|
@ -52,9 +52,7 @@ func InvalidateCodeComments(ctx context.Context, prs issues_model.PullRequestLis
|
|||
issueIDs := prs.GetIssueIDs()
|
||||
|
||||
codeComments, err := db.Find[issues_model.Comment](ctx, issues_model.FindCommentsOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
Type: issues_model.CommentTypeCode,
|
||||
Invalidated: optional.Some(false),
|
||||
IssueIDs: issueIDs,
|
||||
|
@ -268,11 +266,11 @@ func CreateCodeCommentKnownReviewID(ctx context.Context, doer *user_model.User,
|
|||
|
||||
// SubmitReview creates a review out of the existing pending review or creates a new one if no pending review exist
|
||||
func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, issue *issues_model.Issue, reviewType issues_model.ReviewType, content, commitID string, attachmentUUIDs []string) (*issues_model.Review, *issues_model.Comment, error) {
|
||||
pr, err := issue.GetPullRequest(ctx)
|
||||
if err != nil {
|
||||
if err := issue.LoadPullRequest(ctx); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
pr := issue.PullRequest
|
||||
var stale bool
|
||||
if reviewType != issues_model.ReviewTypeApprove && reviewType != issues_model.ReviewTypeReject {
|
||||
stale = false
|
||||
|
@ -322,9 +320,7 @@ func SubmitReview(ctx context.Context, doer *user_model.User, gitRepo *git.Repos
|
|||
// DismissApprovalReviews dismiss all approval reviews because of new commits
|
||||
func DismissApprovalReviews(ctx context.Context, doer *user_model.User, pull *issues_model.PullRequest) error {
|
||||
reviews, err := issues_model.FindReviews(ctx, issues_model.FindReviewOptions{
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IssueID: pull.IssueID,
|
||||
Type: issues_model.ReviewTypeApprove,
|
||||
Dismissed: optional.Some(false),
|
||||
|
|
|
@ -145,9 +145,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r
|
|||
|
||||
branches, _ := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{
|
||||
RepoID: repo.ID,
|
||||
ListOptions: db.ListOptions{
|
||||
ListAll: true,
|
||||
},
|
||||
ListOptions: db.ListOptionsAll,
|
||||
IsDeletedBranch: optional.Some(false),
|
||||
})
|
||||
|
||||
|
|
|
@ -127,10 +127,7 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g
|
|||
p := protectedBranches.GetFirstMatched(branchName)
|
||||
isProtected := p != nil
|
||||
|
||||
divergence := &git.DivergeObject{
|
||||
Ahead: -1,
|
||||
Behind: -1,
|
||||
}
|
||||
var divergence *git.DivergeObject
|
||||
|
||||
// it's not default branch
|
||||
if repo.DefaultBranch != dbBranch.Name && !dbBranch.IsDeleted {
|
||||
|
@ -141,6 +138,11 @@ func loadOneBranch(ctx context.Context, repo *repo_model.Repository, dbBranch *g
|
|||
}
|
||||
}
|
||||
|
||||
if divergence == nil {
|
||||
// tolerate the error that we cannot get divergence
|
||||
divergence = &git.DivergeObject{Ahead: -1, Behind: -1}
|
||||
}
|
||||
|
||||
pr, err := issues_model.GetLatestPullRequestByHeadInfo(ctx, repo.ID, branchName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetLatestPullRequestByHeadInfo: %v", err)
|
||||
|
|
|
@ -182,6 +182,8 @@ func (t telegramConvertor) Package(p *api.PackagePayload) (TelegramPayload, erro
|
|||
func createTelegramPayload(message string) TelegramPayload {
|
||||
return TelegramPayload{
|
||||
Message: strings.TrimSpace(message),
|
||||
ParseMode: "HTML",
|
||||
DisableWebPreview: true,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,15 @@ import (
|
|||
|
||||
func TestTelegramPayload(t *testing.T) {
|
||||
tc := telegramConvertor{}
|
||||
|
||||
t.Run("Correct webhook params", func(t *testing.T) {
|
||||
p := createTelegramPayload("testMsg ")
|
||||
|
||||
assert.Equal(t, "HTML", p.ParseMode)
|
||||
assert.Equal(t, true, p.DisableWebPreview)
|
||||
assert.Equal(t, "testMsg", p.Message)
|
||||
})
|
||||
|
||||
t.Run("Create", func(t *testing.T) {
|
||||
p := createTestPayload()
|
||||
|
||||
|
|
|
@ -55,5 +55,41 @@ export default {
|
|||
current: 'currentcolor',
|
||||
transparent: 'transparent',
|
||||
},
|
||||
borderRadius: {
|
||||
'none': '0',
|
||||
'sm': '2px',
|
||||
'DEFAULT': 'var(--border-radius)', // 4px
|
||||
'md': 'var(--border-radius-medium)', // 6px
|
||||
'lg': '8px',
|
||||
'xl': '12px',
|
||||
'2xl': '16px',
|
||||
'3xl': '24px',
|
||||
'full': 'var(--border-radius-circle)', // 50%
|
||||
},
|
||||
fontWeight: {
|
||||
light: 'var(--font-weight-light)',
|
||||
normal: 'var(--font-weight-normal)',
|
||||
medium: 'var(--font-weight-medium)',
|
||||
semibold: 'var(--font-weight-semibold)',
|
||||
bold: 'var(--font-weight-bold)',
|
||||
},
|
||||
fontSize: { // not using `rem` units because our root is currently 14px
|
||||
'xs': '12px',
|
||||
'sm': '14px',
|
||||
'base': '16px',
|
||||
'lg': '18px',
|
||||
'xl': '20px',
|
||||
'2xl': '24px',
|
||||
'3xl': '30px',
|
||||
'4xl': '36px',
|
||||
'5xl': '48px',
|
||||
'6xl': '60px',
|
||||
'7xl': '72px',
|
||||
'8xl': '96px',
|
||||
'9xl': '128px',
|
||||
...Object.fromEntries(Array.from({length: 100}, (_, i) => {
|
||||
return [`${i}`, `${i === 0 ? '0' : `${i}px`}`];
|
||||
})),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
<li>GitHub</li>
|
||||
<span>{{ctx.Locale.Tr "admin.auths.tip.github"}}</span>
|
||||
<li>GitLab</li>
|
||||
<span>{{ctx.Locale.Tr "admin.auths.tip.gitlab"}}</span>
|
||||
<span>{{ctx.Locale.Tr "admin.auths.tip.gitlab_new"}}</span>
|
||||
<li>Google</li>
|
||||
<span>{{ctx.Locale.Tr "admin.auths.tip.google_plus"}}</span>
|
||||
<li>OpenID Connect</li>
|
||||
|
|
|
@ -4,8 +4,8 @@
|
|||
{{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 gt-ac gt-mx-0">
|
||||
<form class="ui form ignore-dirty gt-f1">
|
||||
<div class="ui secondary filter menu tw-items-center gt-mx-0">
|
||||
<form class="ui form ignore-dirty tw-flex-1">
|
||||
{{template "shared/search/combo" dict "Value" .Keyword}}
|
||||
</form>
|
||||
<!-- Sort -->
|
||||
|
@ -15,10 +15,10 @@
|
|||
</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
<a class="{{if or (eq .SortType "email") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=email&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email"}}</a>
|
||||
<a class="{{if eq .SortType "reverseemail"}}active {{end}}item" href="{{$.Link}}?sort=reverseemail&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email_reverse"}}</a>
|
||||
<a class="{{if eq .SortType "username"}}active {{end}}item" href="{{$.Link}}?sort=username&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name"}}</a>
|
||||
<a class="{{if eq .SortType "reverseusername"}}active {{end}}item" href="{{$.Link}}?sort=reverseusername&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name_reverse"}}</a>
|
||||
<a class="{{if or (eq .SortType "email") (not .SortType)}}active {{end}}item" href="?sort=email&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email"}}</a>
|
||||
<a class="{{if eq .SortType "reverseemail"}}active {{end}}item" href="?sort=reverseemail&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.email_reverse"}}</a>
|
||||
<a class="{{if eq .SortType "username"}}active {{end}}item" href="?sort=username&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name"}}</a>
|
||||
<a class="{{if eq .SortType "reverseusername"}}active {{end}}item" href="?sort=reverseusername&q={{$.Keyword}}">{{ctx.Locale.Tr "admin.emails.filter_sort.name_reverse"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
<tbody>
|
||||
{{range .Notices}}
|
||||
<tr>
|
||||
<td><div class="ui checkbox gt-df" data-id="{{.ID}}"><input type="checkbox"></div></td>
|
||||
<td><div class="ui checkbox tw-flex" data-id="{{.ID}}"><input type="checkbox"></div></td>
|
||||
<td>{{.ID}}</td>
|
||||
<td>{{ctx.Locale.Tr .TrStr}}</td>
|
||||
<td class="view-detail auto-ellipsis" style="width: 80%;"><span class="notice-description">{{.Description}}</span></td>
|
||||
<td class="view-detail auto-ellipsis tw-w-4/5"><span class="notice-description">{{.Description}}</span></td>
|
||||
<td nowrap>{{DateTime "short" .CreatedUnix}}</td>
|
||||
<td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td>
|
||||
</tr>
|
||||
|
@ -49,8 +49,8 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="{{.Link}}?page={{.Page.Paginater.Current}}">
|
||||
{{ctx.Locale.Tr "admin.notices.delete_selected"}}
|
||||
<button class="ui small teal button" id="delete-selection" data-link="{{.Link}}/delete" data-redirect="?page={{.Page.Paginater.Current}}">
|
||||
<span class="text">{{ctx.Locale.Tr "admin.notices.delete_selected"}}</span>
|
||||
</button>
|
||||
</th>
|
||||
</tr>
|
||||
|
|
|
@ -7,8 +7,8 @@
|
|||
</div>
|
||||
</h4>
|
||||
<div class="ui attached segment">
|
||||
<div class="ui secondary filter menu gt-ac gt-mx-0">
|
||||
<form class="ui form ignore-dirty gt-f1">
|
||||
<div class="ui secondary filter menu tw-items-center gt-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 -->
|
||||
|
@ -18,12 +18,12 @@
|
|||
</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
<a class="{{if or (eq .SortType "oldest") (not .SortType)}}active {{end}}item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="{{$.Link}}?sort=recentupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="{{$.Link}}?sort=leastupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
<a class="{{if or (eq .SortType "oldest") (not .SortType)}}active {{end}}item" href="?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?sort=recentupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a>
|
||||
<a class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?sort=leastupdate&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
-
|
||||
{{else}}
|
||||
{{$sum}}
|
||||
<form action="{{$.Link}}/remove-all-items" method="post" class="gt-dib gt-ml-4">
|
||||
<form action="{{$.Link}}/remove-all-items" method="post" class="tw-inline-block gt-ml-4">
|
||||
{{$.CsrfTokenHtml}}
|
||||
<button class="ui tiny basic red button">{{ctx.Locale.Tr "admin.monitor.queue.settings.remove_all_items"}}</button>
|
||||
</form>
|
||||
|
|
|
@ -20,8 +20,8 @@
|
|||
{{if .Dirs}}
|
||||
<div class="ui aligned divided list">
|
||||
{{range $dirI, $dir := .Dirs}}
|
||||
<div class="item gt-df gt-ac">
|
||||
<span class="gt-f1"> {{svg "octicon-file-directory-fill"}} {{$dir}}</span>
|
||||
<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>
|
||||
<div class="ui g-modal-confirm modal" id="adopt-unadopted-modal-{{$dirI}}">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="item">
|
||||
<div class="gt-df gt-ac">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<div class="icon gt-ml-3 gt-mr-3">
|
||||
{{if eq .Process.Type "request"}}
|
||||
{{svg "octicon-globe" 16}}
|
||||
|
@ -11,7 +11,7 @@
|
|||
{{svg "octicon-code" 16}}
|
||||
{{end}}
|
||||
</div>
|
||||
<div class="content gt-f1">
|
||||
<div class="content tw-flex-1">
|
||||
<div class="header">{{.Process.Description}}</div>
|
||||
<div class="description">{{if ne .Process.Type "none"}}{{TimeSince .Process.Start ctx.Locale}}{{end}}</div>
|
||||
</div>
|
||||
|
@ -40,9 +40,9 @@
|
|||
</summary>
|
||||
<div class="list">
|
||||
{{range .Entry}}
|
||||
<div class="item gt-df gt-ac">
|
||||
<div class="item tw-flex tw-items-center">
|
||||
<span class="icon gt-mr-4">{{svg "octicon-dot-fill" 16}}</span>
|
||||
<div class="content gt-f1">
|
||||
<div class="content tw-flex-1">
|
||||
<div class="header"><code>{{.Function}}</code></div>
|
||||
<div class="description"><code>{{.File}}:{{.Line}}</code></div>
|
||||
</div>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
{{template "admin/layout_head" (dict "ctxData" . "pageClass" "admin monitor")}}
|
||||
<div class="admin-setting-content">
|
||||
|
||||
<div class="gt-df gt-ac">
|
||||
<div class="gt-f1">
|
||||
<div class="tw-flex tw-items-center">
|
||||
<div class="tw-flex-1">
|
||||
<div class="ui compact small menu">
|
||||
<a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="{{.Link}}?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
|
||||
<a class="{{if eq .ShowGoroutineList "stacktrace"}}active {{end}}item" href="{{.Link}}?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
|
||||
<a class="{{if eq .ShowGoroutineList "process"}}active {{end}}item" href="?show=process">{{ctx.Locale.Tr "admin.monitor.process"}}</a>
|
||||
<a class="{{if eq .ShowGoroutineList "stacktrace"}}active {{end}}item" href="?show=stacktrace">{{ctx.Locale.Tr "admin.monitor.stacktrace"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
<form target="_blank" action="{{AppSubUrl}}/admin/monitor/diagnosis" class="ui form">
|
||||
|
|
|
@ -103,7 +103,7 @@
|
|||
<td><span>{{ctx.Locale.Tr "admin.users.never_login"}}</span></td>
|
||||
{{end}}
|
||||
<td>
|
||||
<div class="gt-df gt-gap-3">
|
||||
<div class="tw-flex tw-gap-2">
|
||||
<a href="{{$.Link}}/{{.ID}}" data-tooltip-content="{{ctx.Locale.Tr "admin.users.details"}}">{{svg "octicon-person"}}</a>
|
||||
<a href="{{$.Link}}/{{.ID}}/edit" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a>
|
||||
</div>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
<div class="admin-setting-content">
|
||||
<div class="admin-responsive-columns">
|
||||
<div class="gt-f1">
|
||||
<div class="tw-flex-1">
|
||||
<h4 class="ui top attached header">
|
||||
{{.Title}}
|
||||
<div class="ui right">
|
||||
|
@ -13,7 +13,7 @@
|
|||
{{template "admin/user/view_details" .}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="gt-f1">
|
||||
<div class="tw-flex-1">
|
||||
<h4 class="ui top attached header">
|
||||
{{ctx.Locale.Tr "admin.emails"}}
|
||||
<div class="ui right">
|
||||
|
|
|
@ -56,7 +56,7 @@
|
|||
<div class="navbar-right ui secondary menu">
|
||||
{{if and .IsSigned .MustChangePassword}}
|
||||
<div class="ui dropdown jump item" data-tooltip-content="{{ctx.Locale.Tr "user_profile_and_more"}}">
|
||||
<span class="text gt-df gt-ac">
|
||||
<span class="text tw-flex tw-items-center">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
|
||||
<span class="mobile-only gt-ml-3">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
|
@ -83,8 +83,8 @@
|
|||
<span class="mobile-only gt-ml-3">{{ctx.Locale.Tr "active_stopwatch"}}</span>
|
||||
</a>
|
||||
<div class="active-stopwatch-popup item tippy-target gt-p-3">
|
||||
<div class="gt-df gt-ac">
|
||||
<a class="stopwatch-link gt-df gt-ac" href="{{.ActiveStopwatch.IssueLink}}">
|
||||
<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"}}
|
||||
<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}}">
|
||||
|
@ -142,7 +142,7 @@
|
|||
</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"}}">
|
||||
<span class="text gt-df gt-ac">
|
||||
<span class="text tw-flex tw-items-center">
|
||||
{{ctx.AvatarUtils.Avatar .SignedUser 24 "gt-mr-2"}}
|
||||
<span class="mobile-only gt-ml-3">{{.SignedUser.Name}}</span>
|
||||
<span class="not-mobile">{{svg "octicon-triangle-down"}}</span>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{{if eq .Num -1}}
|
||||
<a class="disabled item">...</a>
|
||||
{{else}}
|
||||
<a class="{{if .IsCurrent}}active {{end}}item tw-content-center" {{if not .IsCurrent}}href="{{$paginationLink}}?page={{.Num}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>{{.Num}}</a>
|
||||
<a class="{{if .IsCurrent}}active {{end}}item tw-items-center" {{if not .IsCurrent}}href="{{$paginationLink}}?page={{.Num}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>{{.Num}}</a>
|
||||
{{end}}
|
||||
{{end}}
|
||||
<a class="{{if not .HasNext}}disabled{{end}} item navigation" {{if .HasNext}}href="{{$paginationLink}}?page={{.Next}}{{if $paginationParams}}&{{$paginationParams}}{{end}}"{{end}}>
|
||||
|
|
|
@ -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" style="background-color: aqua"></i>Go</span>
|
||||
<span class="flex-text-inline"><i class="color-icon gt-mr-3 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="gt-border-secondary gt-py-4">
|
||||
<div class="tw-border tw-border-secondary gt-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>
|
||||
|
|
|
@ -73,7 +73,7 @@
|
|||
{{template "base/modal_actions_confirm" (dict "ModalButtonDangerText" "I know and must do this is dangerous operation")}}
|
||||
</div>
|
||||
|
||||
<div class="modal-buttons flex-text-block gt-fw"></div>
|
||||
<div class="modal-buttons flex-text-block tw-flex-wrap"></div>
|
||||
<script type="module">
|
||||
for (const el of $('.ui.modal')) {
|
||||
const $btn = $('<button>').text(`${el.id}`).on('click', () => {
|
||||
|
|
|
@ -95,8 +95,8 @@
|
|||
|
||||
<div>
|
||||
<h1>Loading</h1>
|
||||
<div class="is-loading small-loading-icon gt-border-secondary gt-py-2"><span>loading ...</span></div>
|
||||
<div class="is-loading gt-border-secondary gt-py-4">
|
||||
<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">
|
||||
<p>loading ...</p>
|
||||
<p>loading ...</p>
|
||||
<p>loading ...</p>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
{{template "base/head" .}}
|
||||
<div class="page-content devtest">
|
||||
<div class="gt-df">
|
||||
<div style="width: 80%; ">
|
||||
<div class="tw-flex">
|
||||
<div class="tw-w-4/5">
|
||||
hello hello hello hello hello hello hello hello hello hello
|
||||
</div>
|
||||
<div style="width: 20%;">
|
||||
<div class="tw-w-1/5">
|
||||
{{template "devtest/tmplerr-sub" .}}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</div>
|
||||
<div class="flex-item-trailing">
|
||||
{{if .PrimaryLanguage}}
|
||||
<a class="muted" href="{{$.Link}}?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
|
||||
<a class="muted" href="?q={{$.Keyword}}&sort={{$.SortType}}&language={{.PrimaryLanguage.Language}}{{if $.TabName}}&tab={{$.TabName}}{{end}}">
|
||||
<span class="flex-text-inline"><i class="color-icon gt-mr-3" style="background-color: {{.PrimaryLanguage.Color}}"></i>{{.PrimaryLanguage.Language}}</span>
|
||||
</a>
|
||||
{{end}}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<div class="ui small secondary filter menu gt-ac gt-mx-0">
|
||||
<form class="ui form ignore-dirty gt-f1">
|
||||
<div class="ui small secondary filter menu tw-items-center gt-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")}}
|
||||
{{else}}
|
||||
|
@ -13,10 +13,10 @@
|
|||
</span>
|
||||
{{svg "octicon-triangle-down" 14 "dropdown icon"}}
|
||||
<div class="menu">
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="{{$.Link}}?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="{{$.Link}}?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="{{$.Link}}?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="{{$.Link}}?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "newest"}}active {{end}}item" href="?sort=newest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a>
|
||||
<a class="{{if eq .SortType "oldest"}}active {{end}}item" href="?sort=oldest&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a>
|
||||
<a class="{{if eq .SortType "alphabetically"}}active {{end}}item" href="?sort=alphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.alphabetically"}}</a>
|
||||
<a class="{{if eq .SortType "reversealphabetically"}}active {{end}}item" href="?sort=reversealphabetically&q={{$.Keyword}}">{{ctx.Locale.Tr "repo.issues.label.filter_sort.reverse_alphabetically"}}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<div class="flex-list">
|
||||
{{range .Users}}
|
||||
<div class="flex-item gt-ac">
|
||||
<div class="flex-item tw-items-center">
|
||||
<div class="flex-item-leading">
|
||||
{{ctx.AvatarUtils.Avatar . 48}}
|
||||
</div>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<div class="ui container gt-df">
|
||||
<div class="ui container tw-flex">
|
||||
{{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}}
|
||||
<div id="org-info" class="gt-df gt-fc">
|
||||
<div id="org-info" class="tw-flex tw-flex-col">
|
||||
<div class="ui header">
|
||||
{{.Org.DisplayName}}
|
||||
<span class="org-visibility">
|
||||
{{if .Org.Visibility.IsLimited}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}</span>{{end}}
|
||||
{{if .Org.Visibility.IsPrivate}}<span class="ui large basic horizontal label">{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}</span>{{end}}
|
||||
</span>
|
||||
<span class="gt-df gt-ac gt-gap-2 gt-ml-auto gt-font-16 tw-whitespace-nowrap">
|
||||
<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"}}">
|
||||
{{svg "octicon-rss" 24}}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue