mirror of
1
Fork 0

Merge branch 'forgejo' into ts-test

This commit is contained in:
Otto 2024-11-11 14:23:11 +00:00
commit 9e929bd709
142 changed files with 1221 additions and 1276 deletions

View File

@ -12,8 +12,10 @@
# whatever is in the default branch instead # whatever is in the default branch instead
# #
# - after it is merged, double check it works by setting the # - after it is merged, double check it works by setting the
# run-end-to-end-test on a pull request (any pull request will doe # run-end-to-end-test on a pull request (any pull request will do)
# #
name: end-to-end
on: on:
push: push:
branches: branches:
@ -33,10 +35,9 @@ jobs:
run: | run: |
echo github.event.pull_request.head.repo.fork = ${{ github.event.pull_request.head.repo.fork }} echo github.event.pull_request.head.repo.fork = ${{ github.event.pull_request.head.repo.fork }}
echo github.event.action = ${{ github.event.action }} echo github.event.action = ${{ github.event.action }}
echo github.event.pull_request.merged = ${{ github.event.pull_request.merged }} echo github.event.label
echo github.event.pull_request.labels.*.name
cat <<'EOF' cat <<'EOF'
${{ toJSON(github.event.pull_request.labels.*.name) }} ${{ toJSON(github.event.label) }}
EOF EOF
cat <<'EOF' cat <<'EOF'
${{ toJSON(github.event) }} ${{ toJSON(github.event) }}
@ -47,7 +48,7 @@ jobs:
!startsWith(vars.ROLE, 'forgejo-') && ( !startsWith(vars.ROLE, 'forgejo-') && (
github.event_name == 'push' || github.event_name == 'push' ||
( (
github.event.action == 'label_updated' && contains(github.event.pull_request.labels.*.name, 'run-end-to-end-tests') github.event.action == 'label_updated' && github.event.label.name == 'run-end-to-end-tests'
) )
) )
runs-on: docker runs-on: docker

View File

@ -0,0 +1,44 @@
# Copyright 2024 The Forgejo Authors
# SPDX-License-Identifier: MIT
name: requirements
on:
pull_request:
types:
- labeled
- edited
- opened
- synchronize
jobs:
merge-conditions:
runs-on: docker
container:
image: 'code.forgejo.org/oci/node:20-bookworm'
steps:
- name: Debug output
run: |
cat <<'EOF'
${{ toJSON(github.event) }}
EOF
- name: Missing test label
if: >
!(
contains(toJSON(github.event.pull_request.labels), 'test/present')
|| contains(toJSON(github.event.pull_request.labels), 'test/not-needed')
|| contains(toJSON(github.event.pull_request.labels), 'test/manual')
)
run: |
echo "Test label must be set to either 'present', 'not-needed' or 'manual'."
exit 1
- name: Missing manual test instructions
if: >
(
contains(toJSON(github.event.pull_request.labels), 'test/manual')
&& !contains(toJSON(github.event.pull_request.body), '# Test')
)
run: |
echo "Manual test label is set. The PR description needs to contain test steps introduced by a heading like:"
echo "# Testing"
exit 1

View File

@ -8,7 +8,9 @@ name: renovate
on: on:
push: push:
branches: branches:
- 'renovate/**' # self-test updates - renovate/** # self-test updates
paths:
- .forgejo/workflows/renovate.yml
schedule: schedule:
- cron: '0 0/2 * * *' - cron: '0 0/2 * * *'
workflow_dispatch: workflow_dispatch:
@ -21,9 +23,9 @@ jobs:
renovate: renovate:
if: ${{ secrets.RENOVATE_TOKEN != '' }} if: ${{ secrets.RENOVATE_TOKEN != '' }}
runs-on: docker runs-on: docker-runner-one
container: container:
image: code.forgejo.org/forgejo-contrib/renovate:39.5.0 image: code.forgejo.org/forgejo-contrib/renovate:39.9.1
steps: steps:
- name: Load renovate repo cache - name: Load renovate repo cache

View File

@ -82,7 +82,7 @@ jobs:
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-backend test-check' su forgejo -c 'make test-backend test-check'
timeout-minutes: 50 timeout-minutes: 120
env: env:
RACE_ENABLED: 'true' RACE_ENABLED: 'true'
TAGS: bindata TAGS: bindata
@ -116,7 +116,7 @@ jobs:
separator: '\n' separator: '\n'
- run: | - run: |
su forgejo -c 'make generate test-e2e-sqlite' su forgejo -c 'make generate test-e2e-sqlite'
timeout-minutes: 40 timeout-minutes: 120
env: env:
USE_REPO_TEST_DIR: 1 USE_REPO_TEST_DIR: 1
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1
@ -156,7 +156,7 @@ jobs:
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-remote-cacher test-check' su forgejo -c 'make test-remote-cacher test-check'
timeout-minutes: 50 timeout-minutes: 120
env: env:
RACE_ENABLED: 'true' RACE_ENABLED: 'true'
TAGS: bindata TAGS: bindata
@ -187,7 +187,7 @@ jobs:
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-mysql-migration test-mysql' su forgejo -c 'make test-mysql-migration test-mysql'
timeout-minutes: 50 timeout-minutes: 120
env: env:
USE_REPO_TEST_DIR: 1 USE_REPO_TEST_DIR: 1
test-pgsql: test-pgsql:
@ -219,7 +219,7 @@ jobs:
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-pgsql-migration test-pgsql' su forgejo -c 'make test-pgsql-migration test-pgsql'
timeout-minutes: 50 timeout-minutes: 120
env: env:
RACE_ENABLED: true RACE_ENABLED: true
USE_REPO_TEST_DIR: 1 USE_REPO_TEST_DIR: 1
@ -240,7 +240,7 @@ jobs:
- uses: ./.forgejo/workflows-composite/build-backend - uses: ./.forgejo/workflows-composite/build-backend
- run: | - run: |
su forgejo -c 'make test-sqlite-migration test-sqlite' su forgejo -c 'make test-sqlite-migration test-sqlite'
timeout-minutes: 50 timeout-minutes: 120
env: env:
TAGS: sqlite sqlite_unlock_notify TAGS: sqlite sqlite_unlock_notify
RACE_ENABLED: true RACE_ENABLED: true

View File

@ -77,6 +77,8 @@ linters-settings:
- name: unreachable-code - name: unreachable-code
- name: var-declaration - name: var-declaration
- name: var-naming - name: var-naming
- name: redefines-builtin-id
disabled: true
gofumpt: gofumpt:
extra-rules: true extra-rules: true
depguard: depguard:

View File

@ -39,7 +39,7 @@ XGO_VERSION := go-1.21.x
AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go AIR_PACKAGE ?= github.com/air-verse/air@v1 # renovate: datasource=go
EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.0.3 # renovate: datasource=go EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v3/cmd/editorconfig-checker@v3.0.3 # renovate: datasource=go
GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.7.0 # renovate: datasource=go
GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.61.0 # renovate: datasource=go GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.62.0 # renovate: datasource=go
GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 # renovate: datasource=go
MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.6.0 # renovate: datasource=go
SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.31.0 # renovate: datasource=go
@ -49,7 +49,7 @@ GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasour
DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.26.0 # renovate: datasource=go DEADCODE_PACKAGE ?= golang.org/x/tools/cmd/deadcode@v0.26.0 # renovate: datasource=go
GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go GOMOCK_PACKAGE ?= go.uber.org/mock/mockgen@v0.4.0 # renovate: datasource=go
GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.16.2 # renovate: datasource=go GOPLS_PACKAGE ?= golang.org/x/tools/gopls@v0.16.2 # renovate: datasource=go
RENOVATE_NPM_PACKAGE ?= renovate@39.5.0 # renovate: datasource=docker packageName=code.forgejo.org/forgejo-contrib/renovate RENOVATE_NPM_PACKAGE ?= renovate@39.9.1 # renovate: datasource=docker packageName=code.forgejo.org/forgejo-contrib/renovate
ifeq ($(HAS_GO), yes) ifeq ($(HAS_GO), yes)
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766 CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
@ -660,6 +660,7 @@ generate-ini-pgsql:
-e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \ -e 's|{{REPO_TEST_DIR}}|${REPO_TEST_DIR}|g' \
-e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \ -e 's|{{TEST_LOGGER}}|$(or $(TEST_LOGGER),test$(COMMA)file)|g' \
-e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \ -e 's|{{TEST_TYPE}}|$(or $(TEST_TYPE),integration)|g' \
-e 's|{{TEST_STORAGE_TYPE}}|$(or $(TEST_STORAGE_TYPE),minio)|g' \
tests/pgsql.ini.tmpl > tests/pgsql.ini tests/pgsql.ini.tmpl > tests/pgsql.ini
.PHONY: test-pgsql .PHONY: test-pgsql

View File

@ -1,4 +1,6 @@
{ {
"go.buildTags": "sqlite,sqlite_unlock_notify", "go.buildTags": "sqlite,sqlite_unlock_notify",
"go.testFlags": ["-v"] "go.testFlags": ["-v"],
"go.lintTool": "golangci-lint",
"go.lintFlags": ["--fast"]
} }

View File

@ -353,16 +353,25 @@ RUN_USER = ; git
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; Database to use. Either "mysql", "postgres", "mssql" or "sqlite3". ;; Database to use. Either "sqlite3", "mySQL" or "postgres".
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; SQLite Configuration
;;
DB_TYPE = sqlite3
;PATH= ; defaults to data/forgejo.db
;SQLITE_TIMEOUT = ; Query timeout defaults to: 500
;SQLITE_JOURNAL_MODE = ; defaults to sqlite database default (often DELETE), can be used to enable WAL mode. https://www.sqlite.org/pragma.html#pragma_journal_mode
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; MySQL Configuration ;; MySQL Configuration
;; ;;
DB_TYPE = mysql ;DB_TYPE = mysql
HOST = 127.0.0.1:3306 ; can use socket e.g. /var/run/mysqld/mysqld.sock ;HOST = 127.0.0.1:3306 ; can use socket e.g. /var/run/mysqld/mysqld.sock
NAME = gitea ;NAME = gitea
USER = root ;USER = root
;PASSWD = ;Use PASSWD = `your password` for quoting if you use special characters in the password. ;PASSWD = ;Use PASSWD = `your password` for quoting if you use special characters in the password.
;SSL_MODE = false ; either "false" (default), "true", or "skip-verify" ;SSL_MODE = false ; either "false" (default), "true", or "skip-verify"
;CHARSET_COLLATION = ; Empty as default, Gitea will try to find a case-sensitive collation. Don't change it unless you clearly know what you need. ;CHARSET_COLLATION = ; Empty as default, Gitea will try to find a case-sensitive collation. Don't change it unless you clearly know what you need.
@ -381,26 +390,6 @@ USER = root
;; ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; ;;
;; SQLite Configuration
;;
;DB_TYPE = sqlite3
;PATH= ; defaults to data/forgejo.db
;SQLITE_TIMEOUT = ; Query timeout defaults to: 500
;SQLITE_JOURNAL_MODE = ; defaults to sqlite database default (often DELETE), can be used to enable WAL mode. https://www.sqlite.org/pragma.html#pragma_journal_mode
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; MSSQL Configuration
;;
;DB_TYPE = mssql
;HOST = 172.17.0.2:1433
;NAME = gitea
;USER = SA
;PASSWD = MwantsaSecurePassword1
;CHARSET_COLLATION = ; Empty as default, Gitea will try to find a case-sensitive collation. Don't change it unless you clearly know what you need.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Other settings ;; Other settings
;; ;;
;; For iterate buffer, default is 50 ;; For iterate buffer, default is 50

20
go.mod
View File

@ -6,10 +6,10 @@ require (
code.forgejo.org/f3/gof3/v3 v3.7.0 code.forgejo.org/f3/gof3/v3 v3.7.0
code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251 code.forgejo.org/forgejo-contrib/go-libravatar v0.0.0-20191008002943-06d1c002b251
code.forgejo.org/forgejo/reply v1.0.2 code.forgejo.org/forgejo/reply v1.0.2
code.forgejo.org/go-chi/binding v0.0.0-20241105210225-1f8bf5136720 code.forgejo.org/go-chi/binding v1.0.0
code.forgejo.org/go-chi/cache v0.0.0-20240912103640-dcb08fba860d code.forgejo.org/go-chi/cache v1.0.0
code.forgejo.org/go-chi/captcha v0.0.0-20240905153133-df43b9250ed5 code.forgejo.org/go-chi/captcha v1.0.0
code.forgejo.org/go-chi/session v0.0.0-20241017103059-2a992261fc26 code.forgejo.org/go-chi/session v1.0.0
code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/actions-proto-go v0.4.0
code.gitea.io/gitea-vet v0.2.3 code.gitea.io/gitea-vet v0.2.3
code.gitea.io/sdk/gitea v0.17.1 code.gitea.io/sdk/gitea v0.17.1
@ -100,12 +100,12 @@ require (
github.com/yuin/goldmark v1.7.8 github.com/yuin/goldmark v1.7.8
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
go.uber.org/mock v0.4.0 go.uber.org/mock v0.4.0
golang.org/x/crypto v0.28.0 golang.org/x/crypto v0.29.0
golang.org/x/image v0.21.0 golang.org/x/image v0.21.0
golang.org/x/net v0.30.0 golang.org/x/net v0.31.0
golang.org/x/oauth2 v0.23.0 golang.org/x/oauth2 v0.23.0
golang.org/x/sys v0.26.0 golang.org/x/sys v0.27.0
golang.org/x/text v0.19.0 golang.org/x/text v0.20.0
golang.org/x/tools v0.26.0 golang.org/x/tools v0.26.0
google.golang.org/grpc v1.67.1 google.golang.org/grpc v1.67.1
google.golang.org/protobuf v1.35.1 google.golang.org/protobuf v1.35.1
@ -281,7 +281,7 @@ require (
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/mod v0.21.0 // indirect golang.org/x/mod v0.21.0 // indirect
golang.org/x/sync v0.8.0 // indirect golang.org/x/sync v0.9.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240814211410-ddb44dafa142 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
@ -296,3 +296,5 @@ replace github.com/shurcooL/vfsgen => github.com/lunny/vfsgen v0.0.0-20220105142
replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.21.5 replace github.com/nektos/act => code.forgejo.org/forgejo/act v1.21.5
replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1 replace github.com/mholt/archiver/v3 => code.forgejo.org/forgejo/archiver/v3 v3.5.1
replace github.com/goccy/go-json => github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c

44
go.sum
View File

@ -10,14 +10,14 @@ code.forgejo.org/forgejo/archiver/v3 v3.5.1 h1:UmmbA7D5550uf71SQjarmrn6yKwOGxtEj
code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4= code.forgejo.org/forgejo/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ= code.forgejo.org/forgejo/reply v1.0.2 h1:dMhQCHV6/O3L5CLWNTol+dNzDAuyCK88z4J/lCdgFuQ=
code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U= code.forgejo.org/forgejo/reply v1.0.2/go.mod h1:RyZUfzQLc+fuLIGjTSQWDAJWPiL4WtKXB/FifT5fM7U=
code.forgejo.org/go-chi/binding v0.0.0-20241105210225-1f8bf5136720 h1:DDdvrdov1a6HK0xiJIUTfGh1RpKP0prkHi2guxhDKIU= code.forgejo.org/go-chi/binding v1.0.0 h1:EIDJtk9brK7WsT7rvS/D4cxX8XlnhY3LMy8ex1jeHu0=
code.forgejo.org/go-chi/binding v0.0.0-20241105210225-1f8bf5136720/go.mod h1:fWwqaHj0H1/KeCpBqdvKunflq8pYfciEHI5v3UUeE2E= code.forgejo.org/go-chi/binding v1.0.0/go.mod h1:fWwqaHj0H1/KeCpBqdvKunflq8pYfciEHI5v3UUeE2E=
code.forgejo.org/go-chi/cache v0.0.0-20240912103640-dcb08fba860d h1:nOu/2GX571t4intmtfvpctS148OqsBYrGUySVm93ifc= code.forgejo.org/go-chi/cache v1.0.0 h1:akLfGxNlHcacmtutovNtYFSTMsbdcp5MGjAEsP4pxnE=
code.forgejo.org/go-chi/cache v0.0.0-20240912103640-dcb08fba860d/go.mod h1:OVlZ/TqDYJ+RUJ+R+J+OLxtlyjo3pbjBeK7LAWAB+Vk= code.forgejo.org/go-chi/cache v1.0.0/go.mod h1:OVlZ/TqDYJ+RUJ+R+J+OLxtlyjo3pbjBeK7LAWAB+Vk=
code.forgejo.org/go-chi/captcha v0.0.0-20240905153133-df43b9250ed5 h1:A7P1liXCpJBHEJ5KIDsF0ujnQ8FQ/aX1UixTW0vGrDQ= code.forgejo.org/go-chi/captcha v1.0.0 h1:ZKVznXrPfruc1RMavCFBEINGxB3RAYr4I+WQAK8+eP0=
code.forgejo.org/go-chi/captcha v0.0.0-20240905153133-df43b9250ed5/go.mod h1:YLOsiln/arX3egGtxG4QNp49G2CIqP9pqD2VL56obLc= code.forgejo.org/go-chi/captcha v1.0.0/go.mod h1:YLOsiln/arX3egGtxG4QNp49G2CIqP9pqD2VL56obLc=
code.forgejo.org/go-chi/session v0.0.0-20241017103059-2a992261fc26 h1:6XCLWu8A0nPM862Uh68kQmxsBsaQNfPPFwYjzG8M8QE= code.forgejo.org/go-chi/session v1.0.0 h1:1hjLWHpXkb4vs/g9rk/unhRL4AoKEqVUPJ+opI8UO3Y=
code.forgejo.org/go-chi/session v0.0.0-20241017103059-2a992261fc26/go.mod h1:lS76JFHZqGXYJTBHqwZ910UG046hIXAaYIN6J0Lf8sI= code.forgejo.org/go-chi/session v1.0.0/go.mod h1:lS76JFHZqGXYJTBHqwZ910UG046hIXAaYIN6J0Lf8sI=
code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU=
code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas=
code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI= code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI=
@ -307,8 +307,6 @@ github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJA
github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY= github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14= github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
@ -383,6 +381,8 @@ github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kX
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ= github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik= github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c h1:yKBKEC347YZpgii1KazRCfxHsTaxMqWZzoivM1OTT50=
github.com/grafana/go-json v0.0.0-20241106155216-71a03f133f5c/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE= github.com/h2non/gock v1.2.0 h1:K6ol8rfrRkUOefooBC8elXoaNGYkpp7y2qcxGG6BzUE=
github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk= github.com/h2non/gock v1.2.0/go.mod h1:tNhoxHYW2W42cYkYb1WqzdbYIieALC99kpYr7rH/BQk=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw=
@ -733,8 +733,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s= golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
@ -760,8 +760,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -771,8 +771,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -801,8 +801,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@ -812,8 +812,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@ -825,8 +825,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

View File

@ -96,6 +96,6 @@ func TestGetUserHeatmapDataByUser(t *testing.T) {
// Test JSON rendering // Test JSON rendering
jsonData, err := json.Marshal(heatmap) jsonData, err := json.Marshal(heatmap)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, tc.JSONResult, string(jsonData)) assert.JSONEq(t, tc.JSONResult, string(jsonData))
} }
} }

View File

@ -29,7 +29,8 @@
size: 7597 size: 7597
is_fsck_enabled: true is_fsck_enabled: true
close_issues_via_commit_in_any_branch: false close_issues_via_commit_in_any_branch: false
created_unix: 1731254961
updated_unix: 1731254961
- -
id: 2 id: 2
owner_id: 2 owner_id: 2

View File

@ -5,7 +5,6 @@ package project
import ( import (
"fmt" "fmt"
"strings"
"testing" "testing"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -124,5 +123,5 @@ func Test_NewColumn(t *testing.T) {
ProjectID: project1.ID, ProjectID: project1.ID,
}) })
require.Error(t, err) require.Error(t, err)
assert.True(t, strings.Contains(err.Error(), "maximum number of columns reached")) assert.Contains(t, err.Error(), "maximum number of columns reached")
} }

View File

@ -4,99 +4,12 @@
package unittest package unittest
import ( import (
"errors"
"io"
"os" "os"
"path"
"strings"
"code.gitea.io/gitea/modules/util"
) )
// Copy copies file from source to target path.
func Copy(src, dest string) error {
// Gather file information to set back later.
si, err := os.Lstat(src)
if err != nil {
return err
}
// Handle symbolic link.
if si.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(src)
if err != nil {
return err
}
// NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
// which will lead "no such file or directory" error.
return os.Symlink(target, dest)
}
sr, err := os.Open(src)
if err != nil {
return err
}
defer sr.Close()
dw, err := os.Create(dest)
if err != nil {
return err
}
defer dw.Close()
if _, err = io.Copy(dw, sr); err != nil {
return err
}
// Set back file information.
if err = os.Chtimes(dest, si.ModTime(), si.ModTime()); err != nil {
return err
}
return os.Chmod(dest, si.Mode())
}
// CopyDir copy files recursively from source to target directory. // CopyDir copy files recursively from source to target directory.
// //
// The filter accepts a function that process the path info.
// and should return true for need to filter.
//
// It returns error when error occurs in underlying functions. // It returns error when error occurs in underlying functions.
func CopyDir(srcPath, destPath string, filters ...func(filePath string) bool) error { func CopyDir(srcPath, destPath string) error {
// Check if target directory exists. return os.CopyFS(destPath, os.DirFS(srcPath))
if _, err := os.Stat(destPath); !errors.Is(err, os.ErrNotExist) {
return util.NewAlreadyExistErrorf("file or directory already exists: %s", destPath)
}
err := os.MkdirAll(destPath, os.ModePerm)
if err != nil {
return err
}
// Gather directory info.
infos, err := util.StatDir(srcPath, true)
if err != nil {
return err
}
var filter func(filePath string) bool
if len(filters) > 0 {
filter = filters[0]
}
for _, info := range infos {
if filter != nil && filter(info) {
continue
}
curPath := path.Join(destPath, info)
if strings.HasSuffix(info, "/") {
err = os.MkdirAll(curPath, os.ModePerm)
} else {
err = Copy(path.Join(srcPath, info), curPath)
}
if err != nil {
return err
}
}
return nil
} }

View File

@ -9,7 +9,6 @@ import (
"io" "io"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
"regexp"
"testing" "testing"
"time" "time"
@ -119,7 +118,7 @@ func TestActivityPubSignedPost(t *testing.T) {
expected := "BODY" expected := "BODY"
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
assert.Regexp(t, regexp.MustCompile("^"+setting.Federation.DigestAlgorithm), r.Header.Get("Digest")) assert.Regexp(t, "^"+setting.Federation.DigestAlgorithm, r.Header.Get("Digest"))
assert.Contains(t, r.Header.Get("Signature"), pubID) assert.Contains(t, r.Header.Get("Signature"), pubID)
assert.Equal(t, ActivityStreamsContentType, r.Header.Get("Content-Type")) assert.Equal(t, ActivityStreamsContentType, r.Header.Get("Content-Type"))
body, err := io.ReadAll(r.Body) body, err := io.ReadAll(r.Body)

View File

@ -16,7 +16,7 @@ import (
// GetCommitGraph return a list of commit (GraphItems) from all branches // GetCommitGraph return a list of commit (GraphItems) from all branches
func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) { func GetCommitGraph(r *git.Repository, page, maxAllowedColors int, hidePRRefs bool, branches, files []string) (*Graph, error) {
format := "DATA:%D|%H|%ad|%h|%s" format := "DATA:%D|%H|%aD|%h|%s"
if page == 0 { if page == 0 {
page = 1 page = 1

View File

@ -8,6 +8,7 @@ import (
"context" "context"
"fmt" "fmt"
"strings" "strings"
"time"
asymkey_model "code.gitea.io/gitea/models/asymkey" asymkey_model "code.gitea.io/gitea/models/asymkey"
"code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/db"
@ -198,6 +199,11 @@ func NewCommit(row, column int, line []byte) (*Commit, error) {
if len(data) < 5 { if len(data) < 5 {
return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line)) return nil, fmt.Errorf("malformed data section on line %d with commit: %s", row, string(line))
} }
// Format is a slight modifcation from RFC1123Z
t, err := time.Parse("Mon, _2 Jan 2006 15:04:05 -0700", string(data[2]))
if err != nil {
return nil, fmt.Errorf("could not parse date of commit: %w", err)
}
return &Commit{ return &Commit{
Row: row, Row: row,
Column: column, Column: column,
@ -205,8 +211,8 @@ func NewCommit(row, column int, line []byte) (*Commit, error) {
Refs: newRefsFromRefNames(data[0]), Refs: newRefsFromRefNames(data[0]),
// 1 matches git log --pretty=format:%H => commit hash // 1 matches git log --pretty=format:%H => commit hash
Rev: string(data[1]), Rev: string(data[1]),
// 2 matches git log --pretty=format:%ad => author date (format respects --date= option) // 2 matches git log --pretty=format:%aD => author date, RFC2822 style
Date: string(data[2]), Date: t,
// 3 matches git log --pretty=format:%h => abbreviated commit hash // 3 matches git log --pretty=format:%h => abbreviated commit hash
ShortRev: string(data[3]), ShortRev: string(data[3]),
// 4 matches git log --pretty=format:%s => subject // 4 matches git log --pretty=format:%s => subject
@ -245,7 +251,7 @@ type Commit struct {
Column int Column int
Refs []git.Reference Refs []git.Reference
Rev string Rev string
Date string Date time.Time
ShortRev string ShortRev string
Subject string Subject string
} }

View File

@ -241,7 +241,7 @@ func TestParseGlyphs(t *testing.T) {
} }
func TestCommitStringParsing(t *testing.T) { func TestCommitStringParsing(t *testing.T) {
dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|2016-12-20 21:10:41 +0100|4e61bac|" dataFirstPart := "* DATA:|4e61bacab44e9b4730e44a6615d04098dd3a8eaf|Tue, 20 Dec 2016 21:10:41 +0100|4e61bac|"
tests := []struct { tests := []struct {
shouldPass bool shouldPass bool
testName string testName string

View File

@ -19,6 +19,15 @@ func NumericEqualityQuery(value int64, field string) *query.NumericRangeQuery {
return q return q
} }
// MatchQuery generates a match query for the given phrase, field and analyzer
func MatchQuery(matchTerm, field, analyzer string, fuzziness int) *query.MatchQuery {
q := bleve.NewMatchQuery(matchTerm)
q.FieldVal = field
q.Analyzer = analyzer
q.Fuzziness = fuzziness
return q
}
// MatchPhraseQuery generates a match phrase query for the given phrase, field and analyzer // MatchPhraseQuery generates a match phrase query for the given phrase, field and analyzer
func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchPhraseQuery { func MatchPhraseQuery(matchPhrase, field, analyzer string, fuzziness int) *query.MatchPhraseQuery {
q := bleve.NewMatchPhraseQuery(matchPhrase) q := bleve.NewMatchPhraseQuery(matchPhrase)

View File

@ -35,13 +35,7 @@ func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error {
}) })
} }
const ( const maxBatchSize = 16
maxBatchSize = 16
// fuzzyDenominator determines the levenshtein distance per each character of a keyword
fuzzyDenominator = 4
// see https://github.com/blevesearch/bleve/issues/1563#issuecomment-786822311
maxFuzziness = 2
)
// IndexerData an update to the issue indexer // IndexerData an update to the issue indexer
type IndexerData internal.IndexerData type IndexerData internal.IndexerData
@ -162,16 +156,25 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
var queries []query.Query var queries []query.Query
if options.Keyword != "" { if options.Keyword != "" {
fuzziness := 0
if options.IsFuzzyKeyword { if options.IsFuzzyKeyword {
fuzziness = min(maxFuzziness, len(options.Keyword)/fuzzyDenominator) fuzziness := 1
if kl := len(options.Keyword); kl > 3 {
fuzziness = 2
} else if kl < 2 {
fuzziness = 0
}
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
inner_bleve.MatchQuery(options.Keyword, "title", issueIndexerAnalyzer, fuzziness),
inner_bleve.MatchQuery(options.Keyword, "content", issueIndexerAnalyzer, fuzziness),
inner_bleve.MatchQuery(options.Keyword, "comments", issueIndexerAnalyzer, fuzziness),
}...))
} else {
queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{
inner_bleve.MatchPhraseQuery(options.Keyword, "title", issueIndexerAnalyzer, 0),
inner_bleve.MatchPhraseQuery(options.Keyword, "content", issueIndexerAnalyzer, 0),
inner_bleve.MatchPhraseQuery(options.Keyword, "comments", issueIndexerAnalyzer, 0),
}...))
} }
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 { if len(options.RepoIDs) > 0 || options.AllPublic {

View File

@ -78,7 +78,9 @@ func ToSearchOptions(keyword string, opts *issues_model.IssuesOptions) *SearchOp
searchOpt.Paginator = opts.Paginator searchOpt.Paginator = opts.Paginator
switch opts.SortType { switch opts.SortType {
case "", "latest": case "", "relevance":
searchOpt.SortBy = SortByScore
case "latest":
searchOpt.SortBy = SortByCreatedDesc searchOpt.SortBy = SortByCreatedDesc
case "oldest": case "oldest":
searchOpt.SortBy = SortByCreatedAsc searchOpt.SortBy = SortByCreatedAsc

View File

@ -236,7 +236,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
} }
if options.SortBy == "" { if options.SortBy == "" {
options.SortBy = internal.SortByCreatedAsc options.SortBy = internal.SortByScore
} }
sortBy := []elastic.Sorter{ sortBy := []elastic.Sorter{
parseSortBy(options.SortBy), parseSortBy(options.SortBy),

View File

@ -269,6 +269,7 @@ func IsAvailable(ctx context.Context) bool {
type SearchOptions = internal.SearchOptions type SearchOptions = internal.SearchOptions
const ( const (
SortByScore = internal.SortByScore
SortByCreatedDesc = internal.SortByCreatedDesc SortByCreatedDesc = internal.SortByCreatedDesc
SortByUpdatedDesc = internal.SortByUpdatedDesc SortByUpdatedDesc = internal.SortByUpdatedDesc
SortByCommentsDesc = internal.SortByCommentsDesc SortByCommentsDesc = internal.SortByCommentsDesc

View File

@ -127,6 +127,7 @@ func (o *SearchOptions) Copy(edit ...func(options *SearchOptions)) *SearchOption
type SortBy string type SortBy string
const ( const (
SortByScore SortBy = "-_score"
SortByCreatedDesc SortBy = "-created_unix" SortByCreatedDesc SortBy = "-created_unix"
SortByUpdatedDesc SortBy = "-updated_unix" SortByUpdatedDesc SortBy = "-updated_unix"
SortByCommentsDesc SortBy = "-comment_count" SortByCommentsDesc SortBy = "-comment_count"

View File

@ -126,6 +126,7 @@ var cases = []*testIndexerCase{
}, },
SearchOptions: &internal.SearchOptions{ SearchOptions: &internal.SearchOptions{
Keyword: "hello", Keyword: "hello",
SortBy: internal.SortByCreatedDesc,
}, },
ExpectedIDs: []int64{1002, 1001, 1000}, ExpectedIDs: []int64{1002, 1001, 1000},
ExpectedTotal: 3, ExpectedTotal: 3,
@ -139,6 +140,7 @@ var cases = []*testIndexerCase{
}, },
SearchOptions: &internal.SearchOptions{ SearchOptions: &internal.SearchOptions{
Keyword: "hello world", Keyword: "hello world",
SortBy: internal.SortByCreatedDesc,
IsFuzzyKeyword: true, IsFuzzyKeyword: true,
}, },
ExpectedIDs: []int64{1002, 1001, 1000}, ExpectedIDs: []int64{1002, 1001, 1000},
@ -157,6 +159,7 @@ var cases = []*testIndexerCase{
}, },
SearchOptions: &internal.SearchOptions{ SearchOptions: &internal.SearchOptions{
Keyword: "hello", Keyword: "hello",
SortBy: internal.SortByCreatedDesc,
RepoIDs: []int64{1, 4}, RepoIDs: []int64{1, 4},
}, },
ExpectedIDs: []int64{1006, 1002, 1001}, ExpectedIDs: []int64{1006, 1002, 1001},
@ -175,6 +178,7 @@ var cases = []*testIndexerCase{
}, },
SearchOptions: &internal.SearchOptions{ SearchOptions: &internal.SearchOptions{
Keyword: "hello", Keyword: "hello",
SortBy: internal.SortByCreatedDesc,
RepoIDs: []int64{1, 4}, RepoIDs: []int64{1, 4},
AllPublic: true, AllPublic: true,
}, },
@ -597,6 +601,22 @@ var cases = []*testIndexerCase{
} }
}, },
}, },
{
Name: "SortByScore",
SearchOptions: &internal.SearchOptions{
Paginator: &db.ListOptionsAll,
SortBy: internal.SortByScore,
},
Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) {
assert.Equal(t, len(data), len(result.Hits))
assert.Equal(t, len(data), int(result.Total))
for i, v := range result.Hits {
if i < len(result.Hits)-1 {
assert.GreaterOrEqual(t, v.Score, result.Hits[i+1].Score)
}
}
},
},
{ {
Name: "SortByCreatedAsc", Name: "SortByCreatedAsc",
SearchOptions: &internal.SearchOptions{ SearchOptions: &internal.SearchOptions{

View File

@ -208,12 +208,18 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) (
query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value())) query.And(inner_meilisearch.NewFilterLte("updated_unix", options.UpdatedBeforeUnix.Value()))
} }
if options.SortBy == "" { var sortBy []string
options.SortBy = internal.SortByCreatedAsc switch options.SortBy {
} // sort by relevancy (no explicit sorting)
sortBy := []string{ case internal.SortByScore:
parseSortBy(options.SortBy), fallthrough
"id:desc", case "":
sortBy = []string{}
default:
sortBy = []string{
parseSortBy(options.SortBy),
"id:desc",
}
} }
skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits) skip, limit := indexer_internal.ParsePaginator(options.Paginator, maxTotalHits)

View File

@ -267,7 +267,7 @@ func TestHTTPClientDownload(t *testing.T) {
return nil return nil
}) })
if len(c.expectederror) > 0 { if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else { } else {
require.NoError(t, err, "case %d", n) require.NoError(t, err, "case %d", n)
} }
@ -369,7 +369,7 @@ func TestHTTPClientUpload(t *testing.T) {
return io.NopCloser(new(bytes.Buffer)), objectError return io.NopCloser(new(bytes.Buffer)), objectError
}) })
if len(c.expectederror) > 0 { if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else { } else {
require.NoError(t, err, "case %d", n) require.NoError(t, err, "case %d", n)
} }

View File

@ -97,7 +97,7 @@ func TestBasicTransferAdapter(t *testing.T) {
for n, c := range cases { for n, c := range cases {
_, err := a.Download(context.Background(), c.link) _, err := a.Download(context.Background(), c.link)
if len(c.expectederror) > 0 { if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else { } else {
require.NoError(t, err, "case %d", n) require.NoError(t, err, "case %d", n)
} }
@ -130,7 +130,7 @@ func TestBasicTransferAdapter(t *testing.T) {
for n, c := range cases { for n, c := range cases {
err := a.Upload(context.Background(), c.link, p, bytes.NewBufferString("dummy")) err := a.Upload(context.Background(), c.link, p, bytes.NewBufferString("dummy"))
if len(c.expectederror) > 0 { if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else { } else {
require.NoError(t, err, "case %d", n) require.NoError(t, err, "case %d", n)
} }
@ -163,7 +163,7 @@ func TestBasicTransferAdapter(t *testing.T) {
for n, c := range cases { for n, c := range cases {
err := a.Verify(context.Background(), c.link, p) err := a.Verify(context.Background(), c.link, p)
if len(c.expectederror) > 0 { if len(c.expectederror) > 0 {
assert.True(t, strings.Contains(err.Error(), c.expectederror), "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror) assert.Contains(t, err.Error(), c.expectederror, "case %d: '%s' should contain '%s'", n, err.Error(), c.expectederror)
} else { } else {
require.NoError(t, err, "case %d", n) require.NoError(t, err, "case %d", n)
} }

View File

@ -445,7 +445,7 @@ MINIO_BASE_PATH = /lfs
require.NoError(t, loadLFSFrom(cfg)) require.NoError(t, loadLFSFrom(cfg))
assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID) assert.EqualValues(t, "my_access_key", LFS.Storage.MinioConfig.AccessKeyID)
assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey) assert.EqualValues(t, "my_secret_key", LFS.Storage.MinioConfig.SecretAccessKey)
assert.True(t, true, LFS.Storage.MinioConfig.UseSSL) assert.True(t, LFS.Storage.MinioConfig.UseSSL)
assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath) assert.EqualValues(t, "/lfs", LFS.Storage.MinioConfig.BasePath)
cfg, err = NewConfigProviderFromData(` cfg, err = NewConfigProviderFromData(`

View File

@ -20,7 +20,6 @@ import (
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/svg" "code.gitea.io/gitea/modules/svg"
"code.gitea.io/gitea/modules/templates/eval" "code.gitea.io/gitea/modules/templates/eval"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/gitdiff" "code.gitea.io/gitea/services/gitdiff"
) )
@ -65,16 +64,18 @@ func NewFuncMap() template.FuncMap {
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// time / number / format // time / number / format
"FileSize": FileSizePanic, "FileSize": FileSizePanic,
"CountFmt": base.FormatNumberSI, "CountFmt": base.FormatNumberSI,
"TimeSince": timeutil.TimeSince, "Sec2Time": util.SecToTime,
"TimeSinceUnix": timeutil.TimeSinceUnix,
"DateTime": dateTimeLegacy, // for backward compatibility only, do not use it anymore
"Sec2Time": util.SecToTime,
"LoadTimes": func(startTime time.Time) string { "LoadTimes": func(startTime time.Time) string {
return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms" return fmt.Sprint(time.Since(startTime).Nanoseconds()/1e6) + "ms"
}, },
// for backward compatibility only, do not use them anymore
"TimeSince": timeSinceLegacy,
"TimeSinceUnix": timeSinceLegacy,
"DateTime": dateTimeLegacy,
// ----------------------------------------------------------------- // -----------------------------------------------------------------
// setting // setting
"AppName": func() string { "AppName": func() string {

View File

@ -4,35 +4,40 @@
package templates package templates
import ( import (
"context" "fmt"
"html"
"html/template" "html/template"
"strings"
"time" "time"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/translation"
) )
type DateUtils struct { type DateUtils struct{}
ctx context.Context
}
func NewDateUtils(ctx context.Context) *DateUtils { func NewDateUtils() *DateUtils {
return &DateUtils{ctx} return (*DateUtils)(nil) // the util is stateless, and we do not need to create an instance
} }
// AbsoluteShort renders in "Jan 01, 2006" format // AbsoluteShort renders in "Jan 01, 2006" format
func (du *DateUtils) AbsoluteShort(time any) template.HTML { func (du *DateUtils) AbsoluteShort(time any) template.HTML {
return timeutil.DateTime("short", time) return dateTimeFormat("short", time)
} }
// AbsoluteLong renders in "January 01, 2006" format // AbsoluteLong renders in "January 01, 2006" format
func (du *DateUtils) AbsoluteLong(time any) template.HTML { func (du *DateUtils) AbsoluteLong(time any) template.HTML {
return timeutil.DateTime("long", time) return dateTimeFormat("long", time)
} }
// FullTime renders in "Jan 01, 2006 20:33:44" format // FullTime renders in "Jan 01, 2006 20:33:44" format
func (du *DateUtils) FullTime(time any) template.HTML { func (du *DateUtils) FullTime(time any) template.HTML {
return timeutil.DateTime("full", time) return dateTimeFormat("full", time)
}
func (du *DateUtils) TimeSince(time any) template.HTML {
return TimeSince(time)
} }
// ParseLegacy parses the datetime in legacy format, eg: "2016-01-02" in server's timezone. // ParseLegacy parses the datetime in legacy format, eg: "2016-01-02" in server's timezone.
@ -56,5 +61,91 @@ func dateTimeLegacy(format string, datetime any, _ ...string) template.HTML {
if s, ok := datetime.(string); ok { if s, ok := datetime.(string); ok {
datetime = parseLegacy(s) datetime = parseLegacy(s)
} }
return timeutil.DateTime(format, datetime) return dateTimeFormat(format, datetime)
}
func timeSinceLegacy(time any, _ translation.Locale) template.HTML {
if !setting.IsProd || setting.IsInTesting {
panic("timeSinceLegacy is for backward compatibility only, do not use it in new code")
}
return TimeSince(time)
}
func anyToTime(any any) (t time.Time, isZero bool) {
switch v := any.(type) {
case nil:
// it is zero
case *time.Time:
if v != nil {
t = *v
}
case time.Time:
t = v
case timeutil.TimeStamp:
t = v.AsTime()
case timeutil.TimeStampNano:
t = v.AsTime()
case int:
t = timeutil.TimeStamp(v).AsTime()
case int64:
t = timeutil.TimeStamp(v).AsTime()
default:
panic(fmt.Sprintf("Unsupported time type %T", any))
}
return t, t.IsZero() || t.Unix() == 0
}
func dateTimeFormat(format string, datetime any) template.HTML {
t, isZero := anyToTime(datetime)
if isZero {
return "-"
}
var textEscaped string
datetimeEscaped := html.EscapeString(t.Format(time.RFC3339))
if format == "full" {
textEscaped = html.EscapeString(t.Format("2006-01-02 15:04:05 -07:00"))
} else {
textEscaped = html.EscapeString(t.Format("2006-01-02"))
}
attrs := []string{`weekday=""`, `year="numeric"`}
switch format {
case "short", "long": // date only
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
case "full": // full date including time
attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
default:
panic(fmt.Sprintf("Unsupported format %s", format))
}
}
func timeSinceTo(then any, now time.Time) template.HTML {
thenTime, isZero := anyToTime(then)
if isZero {
return "-"
}
friendlyText := thenTime.Format("2006-01-02 15:04:05 -07:00")
// document: https://github.com/github/relative-time-element
attrs := `tense="past"`
isFuture := now.Before(thenTime)
if isFuture {
attrs = `tense="future"`
}
// declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`,
attrs, thenTime.Format(time.RFC3339), friendlyText)
return template.HTML(htm)
}
// TimeSince renders relative time HTML given a time
func TimeSince(then any) template.HTML {
if setting.UI.PreferredTimestampTense == "absolute" {
return dateTimeFormat("full", then)
}
return timeSinceTo(then, time.Now())
} }

View File

@ -22,7 +22,7 @@ func TestDateTime(t *testing.T) {
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)() defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
defer test.MockVariableValue(&setting.IsInTesting, false)() defer test.MockVariableValue(&setting.IsInTesting, false)()
du := NewDateUtils(nil) du := NewDateUtils()
refTimeStr := "2018-01-01T00:00:00Z" refTimeStr := "2018-01-01T00:00:00Z"
refDateStr := "2018-01-01" refDateStr := "2018-01-01"
@ -59,3 +59,24 @@ func TestDateTime(t *testing.T) {
actual = du.FullTime(refTimeStamp) actual = du.FullTime(refTimeStamp)
assert.EqualValues(t, `<relative-time weekday="" year="numeric" format="datetime" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" data-tooltip-content data-tooltip-interactive="true" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual) assert.EqualValues(t, `<relative-time weekday="" year="numeric" format="datetime" month="short" day="numeric" hour="numeric" minute="numeric" second="numeric" data-tooltip-content data-tooltip-interactive="true" datetime="2017-12-31T19:00:00-05:00">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
} }
func TestTimeSince(t *testing.T) {
testTz, _ := time.LoadLocation("America/New_York")
defer test.MockVariableValue(&setting.DefaultUILocation, testTz)()
defer test.MockVariableValue(&setting.IsInTesting, false)()
du := NewDateUtils()
assert.EqualValues(t, "-", du.TimeSince(nil))
refTimeStr := "2018-01-01T00:00:00Z"
refTime, _ := time.Parse(time.RFC3339, refTimeStr)
actual := du.TimeSince(refTime)
assert.EqualValues(t, `<relative-time prefix="" tense="past" datetime="2018-01-01T00:00:00Z" data-tooltip-content data-tooltip-interactive="true">2018-01-01 00:00:00 +00:00</relative-time>`, actual)
actual = timeSinceTo(&refTime, time.Time{})
assert.EqualValues(t, `<relative-time prefix="" tense="future" datetime="2018-01-01T00:00:00Z" data-tooltip-content data-tooltip-interactive="true">2018-01-01 00:00:00 +00:00</relative-time>`, actual)
actual = timeSinceLegacy(timeutil.TimeStampNano(refTime.UnixNano()), nil)
assert.EqualValues(t, `<relative-time prefix="" tense="past" datetime="2017-12-31T19:00:00-05:00" data-tooltip-content data-tooltip-interactive="true">2017-12-31 19:00:00 -05:00</relative-time>`, actual)
}

View File

@ -19,6 +19,10 @@ func NewStringUtils() *StringUtils {
return &stringUtils return &stringUtils
} }
func (su *StringUtils) Make(arr ...string) []string {
return arr
}
func (su *StringUtils) HasPrefix(s any, prefix string) bool { func (su *StringUtils) HasPrefix(s any, prefix string) bool {
switch v := s.(type) { switch v := s.(type) {
case string: case string:

View File

@ -1,60 +0,0 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package timeutil
import (
"fmt"
"html"
"html/template"
"strings"
"time"
)
// DateTime renders an absolute time HTML element by datetime.
func DateTime(format string, datetime any) template.HTML {
if p, ok := datetime.(*time.Time); ok {
datetime = *p
}
if p, ok := datetime.(*TimeStamp); ok {
datetime = *p
}
switch v := datetime.(type) {
case TimeStamp:
datetime = v.AsTime()
case int:
datetime = TimeStamp(v).AsTime()
case int64:
datetime = TimeStamp(v).AsTime()
}
var datetimeEscaped, textEscaped string
switch v := datetime.(type) {
case nil:
return "-"
case time.Time:
if v.IsZero() || v.Unix() == 0 {
return "-"
}
datetimeEscaped = html.EscapeString(v.Format(time.RFC3339))
if format == "full" {
textEscaped = html.EscapeString(v.Format("2006-01-02 15:04:05 -07:00"))
} else {
textEscaped = html.EscapeString(v.Format("2006-01-02"))
}
default:
panic(fmt.Sprintf("Unsupported time type %T", datetime))
}
attrs := []string{`weekday=""`, `year="numeric"`}
switch format {
case "short", "long": // date only
attrs = append(attrs, `month="`+format+`"`, `day="numeric"`)
return template.HTML(fmt.Sprintf(`<absolute-date %s date="%s">%s</absolute-date>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
case "full": // full date including time
attrs = append(attrs, `format="datetime"`, `month="short"`, `day="numeric"`, `hour="numeric"`, `minute="numeric"`, `second="numeric"`, `data-tooltip-content`, `data-tooltip-interactive="true"`)
return template.HTML(fmt.Sprintf(`<relative-time %s datetime="%s">%s</relative-time>`, strings.Join(attrs, " "), datetimeEscaped, textEscaped))
default:
panic(fmt.Sprintf("Unsupported format %s", format))
}
}

View File

@ -4,12 +4,9 @@
package timeutil package timeutil
import ( import (
"fmt"
"html/template"
"strings" "strings"
"time" "time"
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/translation"
) )
@ -81,16 +78,11 @@ func computeTimeDiffFloor(diff int64, lang translation.Locale) (int64, string) {
return diff, diffStr return diff, diffStr
} }
// MinutesToFriendly returns a user friendly string with number of minutes // MinutesToFriendly returns a user-friendly string with number of minutes
// converted to hours and minutes. // converted to hours and minutes.
func MinutesToFriendly(minutes int, lang translation.Locale) string { func MinutesToFriendly(minutes int, lang translation.Locale) string {
duration := time.Duration(minutes) * time.Minute duration := time.Duration(minutes) * time.Minute
return TimeSincePro(time.Now().Add(-duration), lang) return timeSincePro(time.Now().Add(-duration), time.Now(), lang)
}
// TimeSincePro calculates the time interval and generate full user-friendly string.
func TimeSincePro(then time.Time, lang translation.Locale) string {
return timeSincePro(then, time.Now(), lang)
} }
func timeSincePro(then, now time.Time, lang translation.Locale) string { func timeSincePro(then, now time.Time, lang translation.Locale) string {
@ -114,32 +106,3 @@ func timeSincePro(then, now time.Time, lang translation.Locale) string {
} }
return strings.TrimPrefix(timeStr, ", ") return strings.TrimPrefix(timeStr, ", ")
} }
func timeSinceUnix(then, now time.Time, _ translation.Locale) template.HTML {
friendlyText := then.Format("2006-01-02 15:04:05 -07:00")
// document: https://github.com/github/relative-time-element
attrs := `tense="past"`
isFuture := now.Before(then)
if isFuture {
attrs = `tense="future"`
}
// declare data-tooltip-content attribute to switch from "title" tooltip to "tippy" tooltip
htm := fmt.Sprintf(`<relative-time prefix="" %s datetime="%s" data-tooltip-content data-tooltip-interactive="true">%s</relative-time>`,
attrs, then.Format(time.RFC3339), friendlyText)
return template.HTML(htm)
}
// TimeSince renders relative time HTML given a time.Time
func TimeSince(then time.Time, lang translation.Locale) template.HTML {
if setting.UI.PreferredTimestampTense == "absolute" {
return DateTime("full", then)
}
return timeSinceUnix(then, time.Now(), lang)
}
// TimeSinceUnix renders relative time HTML given a TimeStamp
func TimeSinceUnix(then TimeStamp, lang translation.Locale) template.HTML {
return TimeSince(then.AsLocalTime(), lang)
}

View File

@ -10,7 +10,6 @@ import (
"crypto/sha256" "crypto/sha256"
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"regexp"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
@ -24,8 +23,8 @@ func TestKeygen(t *testing.T) {
assert.NotEmpty(t, priv) assert.NotEmpty(t, priv)
assert.NotEmpty(t, pub) assert.NotEmpty(t, pub)
assert.Regexp(t, regexp.MustCompile("^-----BEGIN RSA PRIVATE KEY-----.*"), priv) assert.Regexp(t, "^-----BEGIN RSA PRIVATE KEY-----.*", priv)
assert.Regexp(t, regexp.MustCompile("^-----BEGIN PUBLIC KEY-----.*"), pub) assert.Regexp(t, "^-----BEGIN PUBLIC KEY-----.*", pub)
} }
func TestSignUsingKeys(t *testing.T) { func TestSignUsingKeys(t *testing.T) {

View File

@ -1586,6 +1586,7 @@ issues.filter_type.mentioning_you = Mentioning you
issues.filter_type.review_requested = Review requested issues.filter_type.review_requested = Review requested
issues.filter_type.reviewed_by_you = Reviewed by you issues.filter_type.reviewed_by_you = Reviewed by you
issues.filter_sort = Sort issues.filter_sort = Sort
issues.filter_sort.relevance = Relevance
issues.filter_sort.latest = Newest issues.filter_sort.latest = Newest
issues.filter_sort.oldest = Oldest issues.filter_sort.oldest = Oldest
issues.filter_sort.recentupdate = Recently updated issues.filter_sort.recentupdate = Recently updated

18
package-lock.json generated
View File

@ -37,7 +37,7 @@
"monaco-editor": "0.51.0", "monaco-editor": "0.51.0",
"monaco-editor-webpack-plugin": "7.1.0", "monaco-editor-webpack-plugin": "7.1.0",
"pdfobject": "2.3.0", "pdfobject": "2.3.0",
"postcss": "8.4.47", "postcss": "8.4.48",
"postcss-loader": "8.1.1", "postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1", "postcss-nesting": "13.0.1",
"pretty-ms": "9.0.0", "pretty-ms": "9.0.0",
@ -67,7 +67,7 @@
"@stylistic/eslint-plugin-js": "2.10.1", "@stylistic/eslint-plugin-js": "2.10.1",
"@stylistic/stylelint-plugin": "3.1.1", "@stylistic/stylelint-plugin": "3.1.1",
"@typescript-eslint/parser": "8.13.0", "@typescript-eslint/parser": "8.13.0",
"@vitejs/plugin-vue": "5.1.4", "@vitejs/plugin-vue": "5.1.5",
"@vitest/coverage-v8": "2.1.4", "@vitest/coverage-v8": "2.1.4",
"@vitest/eslint-plugin": "1.1.7", "@vitest/eslint-plugin": "1.1.7",
"@vue/test-utils": "2.4.6", "@vue/test-utils": "2.4.6",
@ -5085,9 +5085,9 @@
} }
}, },
"node_modules/@vitejs/plugin-vue": { "node_modules/@vitejs/plugin-vue": {
"version": "5.1.4", "version": "5.1.5",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.4.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.1.5.tgz",
"integrity": "sha512-N2XSI2n3sQqp5w7Y/AN/L2XDjBIRGqXko+eDp42sydYSBeJuSm5a1sLf8zakmo8u7tA8NmBgoDLA1HeOESjp9A==", "integrity": "sha512-dlnib73G05CDBAUR/YpuZcQQ47fpjihnnNouAAqN62z+oqSsWJ+kh52GRzIxpkgFG3q11eXK7Di7RMmoCwISZA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
@ -13083,9 +13083,9 @@
} }
}, },
"node_modules/postcss": { "node_modules/postcss": {
"version": "8.4.47", "version": "8.4.48",
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.48.tgz",
"integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "integrity": "sha512-GCRK8F6+Dl7xYniR5a4FYbpBzU8XnZVeowqsQFYdcXuSbChgiks7qybSkbvnaeqv0G0B+dd9/jJgH8kkLDQeEA==",
"funding": [ "funding": [
{ {
"type": "opencollective", "type": "opencollective",
@ -13103,7 +13103,7 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"nanoid": "^3.3.7", "nanoid": "^3.3.7",
"picocolors": "^1.1.0", "picocolors": "^1.1.1",
"source-map-js": "^1.2.1" "source-map-js": "^1.2.1"
}, },
"engines": { "engines": {

View File

@ -36,7 +36,7 @@
"monaco-editor": "0.51.0", "monaco-editor": "0.51.0",
"monaco-editor-webpack-plugin": "7.1.0", "monaco-editor-webpack-plugin": "7.1.0",
"pdfobject": "2.3.0", "pdfobject": "2.3.0",
"postcss": "8.4.47", "postcss": "8.4.48",
"postcss-loader": "8.1.1", "postcss-loader": "8.1.1",
"postcss-nesting": "13.0.1", "postcss-nesting": "13.0.1",
"pretty-ms": "9.0.0", "pretty-ms": "9.0.0",
@ -66,7 +66,7 @@
"@stylistic/eslint-plugin-js": "2.10.1", "@stylistic/eslint-plugin-js": "2.10.1",
"@stylistic/stylelint-plugin": "3.1.1", "@stylistic/stylelint-plugin": "3.1.1",
"@typescript-eslint/parser": "8.13.0", "@typescript-eslint/parser": "8.13.0",
"@vitejs/plugin-vue": "5.1.4", "@vitejs/plugin-vue": "5.1.5",
"@vitest/coverage-v8": "2.1.4", "@vitest/coverage-v8": "2.1.4",
"@vitest/eslint-plugin": "1.1.7", "@vitest/eslint-plugin": "1.1.7",
"@vue/test-utils": "2.4.6", "@vue/test-utils": "2.4.6",

View File

@ -83,6 +83,12 @@
"matchUpdateTypes": ["minor", "patch"], "matchUpdateTypes": ["minor", "patch"],
"groupName": "postcss" "groupName": "postcss"
}, },
{
"description": "Group Forgejo go-chi packages",
"matchUpdateTypes": ["minor", "patch", "digest"],
"matchPackageNames": ["code.forgejo.org/go-chi/**"],
"groupName": "forgejo go-chi packages"
},
{ {
"description": "Split minor and patch updates", "description": "Split minor and patch updates",
"matchPackageNames": [ "matchPackageNames": [

View File

@ -17,7 +17,6 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/util"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
files_service "code.gitea.io/gitea/services/repository/files" files_service "code.gitea.io/gitea/services/repository/files"
@ -254,7 +253,7 @@ func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames
commitCnt++ commitCnt++
// User avatar image // User avatar image
commitSince := timeutil.TimeSinceUnix(timeutil.TimeStamp(commit.Author.When.Unix()), ctx.Locale) commitSince := templates.TimeSince(commit.Author.When)
var avatar string var avatar string
if commit.User != nil { if commit.User != nil {

View File

@ -14,7 +14,6 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
"code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/templates"
"code.gitea.io/gitea/modules/timeutil"
"code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context"
"github.com/sergi/go-diff/diffmatchpatch" "github.com/sergi/go-diff/diffmatchpatch"
@ -73,10 +72,10 @@ func GetContentHistoryList(ctx *context.Context) {
class := avatars.DefaultAvatarClass + " tw-mr-2" class := avatars.DefaultAvatarClass + " tw-mr-2"
name := html.EscapeString(username) name := html.EscapeString(username)
avatarHTML := string(templates.AvatarHTML(src, 28, class, username)) avatarHTML := string(templates.AvatarHTML(src, 28, class, username))
timeSinceText := string(timeutil.TimeSinceUnix(item.EditedUnix, ctx.Locale)) timeSinceHTML := string(templates.TimeSince(item.EditedUnix))
results = append(results, map[string]any{ results = append(results, map[string]any{
"name": avatarHTML + "<strong>" + name + "</strong> " + actionText + " " + timeSinceText, "name": avatarHTML + "<strong>" + name + "</strong> " + actionText + " " + timeSinceHTML,
"value": item.HistoryID, "value": item.HistoryID,
}) })
} }

View File

@ -102,7 +102,6 @@ func NewTemplateContextForWeb(ctx *Context) TemplateContext {
tmplCtx := NewTemplateContext(ctx) tmplCtx := NewTemplateContext(ctx)
tmplCtx["Locale"] = ctx.Base.Locale tmplCtx["Locale"] = ctx.Base.Locale
tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx) tmplCtx["AvatarUtils"] = templates.NewAvatarUtils(ctx)
tmplCtx["DateUtils"] = templates.NewDateUtils(ctx)
return tmplCtx return tmplCtx
} }

View File

@ -51,14 +51,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, issue *issues_model.Issue,
return nil, err return nil, err
} }
if pr.HeadRepo.IsFork {
return nil, nil
}
if err := pr.LoadBaseRepo(ctx); err != nil { if err := pr.LoadBaseRepo(ctx); err != nil {
return nil, err return nil, err
} }
if pr.BaseRepo.IsFork {
return nil, nil
}
repo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo) repo, err := gitrepo.OpenRepository(ctx, pr.BaseRepo)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@ -43,7 +43,7 @@ func TestRepository_ContributorsGraph(t *testing.T) {
dataString, isData := mockCache.Get("key2").(string) dataString, isData := mockCache.Get("key2").(string)
assert.True(t, isData) assert.True(t, isData)
// Verify that JSON is actually stored in the cache. // Verify that JSON is actually stored in the cache.
assert.EqualValues(t, `{"ethantkoenig@gmail.com":{"name":"Ethan Koenig","login":"","avatar_link":"https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon","home_link":"","total_commits":1,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1}}},"jimmy.praet@telenet.be":{"name":"Jimmy Praet","login":"","avatar_link":"https://secure.gravatar.com/avatar/93c49b7c89eb156971d11161c9b52795?d=identicon","home_link":"","total_commits":1,"weeks":{"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}},"jon@allspice.io":{"name":"Jon","login":"","avatar_link":"https://secure.gravatar.com/avatar/00388ce725e6886f3e07c3733007289b?d=identicon","home_link":"","total_commits":1,"weeks":{"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1}}},"total":{"name":"Total","login":"","avatar_link":"","home_link":"","total_commits":3,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1},"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1},"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}}}`, dataString) assert.JSONEq(t, `{"ethantkoenig@gmail.com":{"name":"Ethan Koenig","login":"","avatar_link":"https://secure.gravatar.com/avatar/b42fb195faa8c61b8d88abfefe30e9e3?d=identicon","home_link":"","total_commits":1,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1}}},"jimmy.praet@telenet.be":{"name":"Jimmy Praet","login":"","avatar_link":"https://secure.gravatar.com/avatar/93c49b7c89eb156971d11161c9b52795?d=identicon","home_link":"","total_commits":1,"weeks":{"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}},"jon@allspice.io":{"name":"Jon","login":"","avatar_link":"https://secure.gravatar.com/avatar/00388ce725e6886f3e07c3733007289b?d=identicon","home_link":"","total_commits":1,"weeks":{"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1}}},"total":{"name":"Total","login":"","avatar_link":"","home_link":"","total_commits":3,"weeks":{"1511654400000":{"week":1511654400000,"additions":3,"deletions":0,"commits":1},"1607817600000":{"week":1607817600000,"additions":10,"deletions":0,"commits":1},"1624752000000":{"week":1624752000000,"additions":2,"deletions":0,"commits":1}}}}`, dataString)
var data map[string]*ContributorData var data map[string]*ContributorData
require.NoError(t, json.Unmarshal([]byte(dataString), &data)) require.NoError(t, json.Unmarshal([]byte(dataString), &data))

View File

@ -26,8 +26,8 @@
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td> <td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{.Name}}</a></td>
<td>{{.TypeName}}</td> <td>{{.TypeName}}</td>
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .UpdatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .UpdatedUnix}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td> <td><a href="{{AppSubUrl}}/admin/auths/{{.ID}}">{{svg "octicon-pencil"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View File

@ -23,8 +23,8 @@
<td><button type="submit" class="ui primary button" name="op" value="{{.Name}}" title="{{ctx.Locale.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td> <td><button type="submit" class="ui primary button" name="op" value="{{.Name}}" title="{{ctx.Locale.Tr "admin.dashboard.operation_run"}}">{{svg "octicon-triangle-right"}}</button></td>
<td>{{ctx.Locale.Tr (printf "admin.dashboard.%s" .Name)}}</td> <td>{{ctx.Locale.Tr (printf "admin.dashboard.%s" .Name)}}</td>
<td>{{.Spec}}</td> <td>{{.Spec}}</td>
<td>{{ctx.DateUtils.FullTime .Next}}</td> <td>{{DateUtils.FullTime .Next}}</td>
<td>{{if gt .Prev.Year 1}}{{ctx.DateUtils.FullTime .Prev}}{{else}}-{{end}}</td> <td>{{if gt .Prev.Year 1}}{{DateUtils.FullTime .Prev}}{{else}}-{{end}}</td>
<td>{{.ExecTimes}}</td> <td>{{.ExecTimes}}</td>
<td {{if ne .Status ""}}data-tooltip-content="{{.FormatLastMessage ctx.Locale}}"{{end}} >{{if eq .Status ""}}{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}}</td> <td {{if ne .Status ""}}data-tooltip-content="{{.FormatLastMessage ctx.Locale}}"{{end}} >{{if eq .Status ""}}{{else if eq .Status "finished"}}{{svg "octicon-check" 16}}{{else}}{{svg "octicon-x" 16}}{{end}}</td>
</tr> </tr>

View File

@ -21,7 +21,7 @@
<td>{{.ID}}</td> <td>{{.ID}}</td>
<td>{{ctx.Locale.Tr .TrStr}}</td> <td>{{ctx.Locale.Tr .TrStr}}</td>
<td class="view-detail auto-ellipsis tw-w-4/5"><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>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td> <td nowrap>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td> <td class="view-detail"><a href="#">{{svg "octicon-note" 16}}</a></td>
</tr> </tr>
{{end}} {{end}}

View File

@ -63,7 +63,7 @@
<td>{{.NumTeams}}</td> <td>{{.NumTeams}}</td>
<td>{{.NumMembers}}</td> <td>{{.NumMembers}}</td>
<td>{{.NumRepos}}</td> <td>{{.NumRepos}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td><a href="{{.OrganisationLink}}/settings" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a></td> <td><a href="{{.OrganisationLink}}/settings" data-tooltip-content="{{ctx.Locale.Tr "edit"}}">{{svg "octicon-pencil"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View File

@ -71,7 +71,7 @@
{{end}} {{end}}
</td> </td>
<td>{{ctx.Locale.TrSize .CalculateBlobSize}}</td> <td>{{ctx.Locale.TrSize .CalculateBlobSize}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .Version.CreatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .Version.CreatedUnix}}</td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td> <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.Version.ID}}" data-name="{{.Package.Name}}" data-data-version="{{.Version.Version}}">{{svg "octicon-trash"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View File

@ -82,8 +82,8 @@
<td>{{.NumIssues}}</td> <td>{{.NumIssues}}</td>
<td>{{ctx.Locale.TrSize .GitSize}}</td> <td>{{ctx.Locale.TrSize .GitSize}}</td>
<td>{{ctx.Locale.TrSize .LFSSize}}</td> <td>{{ctx.Locale.TrSize .LFSSize}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .UpdatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .UpdatedUnix}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
<td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td> <td><a class="delete-button" href="" data-url="{{$.Link}}/delete?page={{$.Page.Paginater.Current}}&sort={{$.SortType}}" data-id="{{.ID}}" data-name="{{.Name}}">{{svg "octicon-trash"}}</a></td>
</tr> </tr>
{{end}} {{end}}

View File

@ -13,7 +13,7 @@
</div> </div>
<div class="content tw-flex-1"> <div class="content tw-flex-1">
<div class="header">{{.Process.Description}}</div> <div class="header">{{.Process.Description}}</div>
<div class="description">{{if ne .Process.Type "none"}}{{TimeSince .Process.Start ctx.Locale}}{{end}}</div> <div class="description">{{if ne .Process.Type "none"}}{{DateUtils.TimeSince .Process.Start}}{{end}}</div>
</div> </div>
<div> <div>
{{if or (eq .Process.Type "request") (eq .Process.Type "normal")}} {{if or (eq .Process.Type "request") (eq .Process.Type "normal")}}

View File

@ -96,9 +96,9 @@
<td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if .IsActive}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if .IsRestricted}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td> <td>{{if index $.UsersTwoFaStatus .ID}}{{svg "octicon-check"}}{{else}}{{svg "octicon-x"}}{{end}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .CreatedUnix}}</td>
{{if .LastLoginUnix}} {{if .LastLoginUnix}}
<td>{{ctx.DateUtils.AbsoluteShort .LastLoginUnix}}</td> <td>{{DateUtils.AbsoluteShort .LastLoginUnix}}</td>
{{else}} {{else}}
<td><span>{{ctx.Locale.Tr "admin.users.never_login"}}</span></td> <td><span>{{ctx.Locale.Tr "admin.users.never_login"}}</span></td>
{{end}} {{end}}

View File

@ -167,13 +167,13 @@
<div> <div>
<h1>TimeSince</h1> <h1>TimeSince</h1>
<div>Now: {{TimeSince .TimeNow ctx.Locale}}</div> <div>Now: {{DateUtils.TimeSince .TimeNow}}</div>
<div>5s past: {{TimeSince .TimePast5s ctx.Locale}}</div> <div>5s past: {{DateUtils.TimeSince .TimePast5s}}</div>
<div>5s future: {{TimeSince .TimeFuture5s ctx.Locale}}</div> <div>5s future: {{DateUtils.TimeSince .TimeFuture5s}}</div>
<div>2m past: {{TimeSince .TimePast2m ctx.Locale}}</div> <div>2m past: {{DateUtils.TimeSince .TimePast2m}}</div>
<div>2m future: {{TimeSince .TimeFuture2m ctx.Locale}}</div> <div>2m future: {{DateUtils.TimeSince .TimeFuture2m}}</div>
<div>1y past: {{TimeSince .TimePast1y ctx.Locale}}</div> <div>1y past: {{DateUtils.TimeSince .TimePast1y}}</div>
<div>1y future: {{TimeSince .TimeFuture1y ctx.Locale}}</div> <div>1y future: {{DateUtils.TimeSince .TimeFuture1y}}</div>
</div> </div>
<div> <div>

View File

@ -62,7 +62,7 @@
{{end}} {{end}}
</div> </div>
{{end}} {{end}}
<div class="flex-item-body">{{ctx.Locale.Tr "org.repo_updated" (TimeSinceUnix .UpdatedUnix ctx.Locale)}}</div> <div class="flex-item-body">{{ctx.Locale.Tr "org.repo_updated" (DateUtils.TimeSince .UpdatedUnix)}}</div>
</div> </div>
</div> </div>
{{else}} {{else}}

View File

@ -21,7 +21,7 @@
<a href="mailto:{{.Email}}">{{.Email}}</a> <a href="mailto:{{.Email}}">{{.Email}}</a>
</span> </span>
{{end}} {{end}}
<span class="flex-text-inline">{{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (ctx.DateUtils.AbsoluteShort .CreatedUnix)}}</span> <span class="flex-text-inline">{{svg "octicon-calendar"}}{{ctx.Locale.Tr "user.joined_on" (DateUtils.AbsoluteShort .CreatedUnix)}}</span>
</div> </div>
</div> </div>
</div> </div>

View File

@ -22,7 +22,7 @@
<td><a href="{{.VersionWebLink}}">{{.Version.Version}}</a></td> <td><a href="{{.VersionWebLink}}">{{.Version.Version}}</a></td>
<td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td> <td><a href="{{.Creator.HomeLink}}">{{.Creator.Name}}</a></td>
<td>{{ctx.Locale.TrSize .CalculateBlobSize}}</td> <td>{{ctx.Locale.TrSize .CalculateBlobSize}}</td>
<td>{{ctx.DateUtils.AbsoluteShort .Version.CreatedUnix}}</td> <td>{{DateUtils.AbsoluteShort .Version.CreatedUnix}}</td>
</tr> </tr>
{{else}} {{else}}
<tr> <tr>

View File

@ -24,7 +24,7 @@
<span class="ui label">{{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}}</span> <span class="ui label">{{svg .Package.Type.SVGName 16}} {{.Package.Type.Name}}</span>
</div> </div>
<div class="flex-item-body"> <div class="flex-item-body">
{{$timeStr := TimeSinceUnix .Version.CreatedUnix ctx.Locale}} {{$timeStr := DateUtils.TimeSince .Version.CreatedUnix}}
{{$hasRepositoryAccess := false}} {{$hasRepositoryAccess := false}}
{{if .Repository}} {{if .Repository}}
{{$hasRepositoryAccess = index $.RepositoryAccessMap .Repository.ID}} {{$hasRepositoryAccess = index $.RepositoryAccessMap .Repository.ID}}

View File

@ -25,7 +25,7 @@
<div class="flex-item-main"> <div class="flex-item-main">
<a class="flex-item-title" href="{{.VersionWebLink}}">{{.Version.LowerVersion}}</a> <a class="flex-item-title" href="{{.VersionWebLink}}">{{.Version.LowerVersion}}</a>
<div class="flex-item-body"> <div class="flex-item-body">
{{ctx.Locale.Tr "packages.published_by" (TimeSinceUnix .Version.CreatedUnix ctx.Locale) .Creator.HomeLink .Creator.GetDisplayName}} {{ctx.Locale.Tr "packages.published_by" (DateUtils.TimeSince .Version.CreatedUnix) .Creator.HomeLink .Creator.GetDisplayName}}
</div> </div>
</div> </div>
</div> </div>

View File

@ -8,7 +8,7 @@
<h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1> <h1>{{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}})</h1>
</div> </div>
<div> <div>
{{$timeStr := TimeSinceUnix .PackageDescriptor.Version.CreatedUnix ctx.Locale}} {{$timeStr := DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}
{{if .HasRepositoryAccess}} {{if .HasRepositoryAccess}}
{{ctx.Locale.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName .PackageDescriptor.Repository.Link .PackageDescriptor.Repository.FullName}} {{ctx.Locale.Tr "packages.published_by_in" $timeStr .PackageDescriptor.Creator.HomeLink .PackageDescriptor.Creator.GetDisplayName .PackageDescriptor.Repository.Link .PackageDescriptor.Repository.FullName}}
{{else}} {{else}}
@ -48,7 +48,7 @@
{{if .HasRepositoryAccess}} {{if .HasRepositoryAccess}}
<div class="item">{{svg "octicon-repo" 16 "tw-mr-2"}} <a href="{{.PackageDescriptor.Repository.Link}}">{{.PackageDescriptor.Repository.FullName}}</a></div> <div class="item">{{svg "octicon-repo" 16 "tw-mr-2"}} <a href="{{.PackageDescriptor.Repository.Link}}">{{.PackageDescriptor.Repository.FullName}}</a></div>
{{end}} {{end}}
<div class="item">{{svg "octicon-calendar" 16 "tw-mr-2"}} {{TimeSinceUnix .PackageDescriptor.Version.CreatedUnix ctx.Locale}}</div> <div class="item">{{svg "octicon-calendar" 16 "tw-mr-2"}} {{DateUtils.TimeSince .PackageDescriptor.Version.CreatedUnix}}</div>
<div class="item">{{svg "octicon-download" 16 "tw-mr-2"}} {{.PackageDescriptor.Version.DownloadCount}}</div> <div class="item">{{svg "octicon-download" 16 "tw-mr-2"}} {{.PackageDescriptor.Version.DownloadCount}}</div>
{{template "package/metadata/alpine" .}} {{template "package/metadata/alpine" .}}
{{template "package/metadata/arch" .}} {{template "package/metadata/arch" .}}
@ -94,7 +94,7 @@
{{range .LatestVersions}} {{range .LatestVersions}}
<div class="item tw-flex"> <div class="item tw-flex">
<a class="tw-flex-1 gt-ellipsis" title="{{.Version}}" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a> <a class="tw-flex-1 gt-ellipsis" title="{{.Version}}" href="{{$.PackageDescriptor.PackageWebLink}}/{{PathEscape .LowerVersion}}">{{.Version}}</a>
<span class="text small">{{ctx.DateUtils.AbsoluteShort .CreatedUnix}}</span> <span class="text small">{{DateUtils.AbsoluteShort .CreatedUnix}}</span>
</div> </div>
{{end}} {{end}}
</div> </div>

View File

@ -33,7 +33,7 @@
<span class="ui label run-list-ref gt-ellipsis">{{.PrettyRef}}</span> <span class="ui label run-list-ref gt-ellipsis">{{.PrettyRef}}</span>
{{end}} {{end}}
<div class="run-list-item-right"> <div class="run-list-item-right">
<div class="run-list-meta">{{svg "octicon-calendar" 16}}{{TimeSinceUnix .Updated ctx.Locale}}</div> <div class="run-list-meta">{{svg "octicon-calendar" 16}}{{DateUtils.TimeSince .Updated}}</div>
<div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{.Duration}}</div> <div class="run-list-meta">{{svg "octicon-stopwatch" 16}}{{.Duration}}</div>
</div> </div>
</div> </div>

View File

@ -27,7 +27,7 @@
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button> <button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DefaultBranchBranch.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}} {{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DefaultBranchBranch.DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DefaultBranchBranch.DBBranch.CommitID)}}
</div> </div>
<p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated" (TimeSince .DefaultBranchBranch.DBBranch.CommitTime.AsTime ctx.Locale)}} {{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p> <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{.RepoLink}}/commit/{{PathEscape .DefaultBranchBranch.DBBranch.CommitID}}">{{ShortSha .DefaultBranchBranch.DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DefaultBranchBranch.DBBranch.CommitMessage (.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated" (DateUtils.TimeSince .DefaultBranchBranch.DBBranch.CommitTime)}}{{if .DefaultBranchBranch.DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DefaultBranchBranch.DBBranch.Pusher}}{{template "shared/user/namelink" .DefaultBranchBranch.DBBranch.Pusher}}{{end}}</p>
</td> </td>
<td class="right aligned middle aligned overflow-visible"> <td class="right aligned middle aligned overflow-visible">
{{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}} {{if and $.IsWriter (not $.Repository.IsArchived) (not .IsDeleted)}}
@ -92,7 +92,7 @@
<span class="gt-ellipsis">{{.DBBranch.Name}}</span> <span class="gt-ellipsis">{{.DBBranch.Name}}</span>
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button> <button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
</div> </div>
<p class="info">{{ctx.Locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{TimeSinceUnix .DBBranch.DeletedUnix ctx.Locale}}</p> <p class="info">{{ctx.Locale.Tr "repo.branch.deleted_by" .DBBranch.DeletedBy.Name}} {{DateUtils.TimeSince .DBBranch.DeletedUnix}}</p>
{{else}} {{else}}
<div class="flex-text-block"> <div class="flex-text-block">
<a class="gt-ellipsis" href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a> <a class="gt-ellipsis" href="{{$.RepoLink}}/src/branch/{{PathEscapeSegments .DBBranch.Name}}">{{.DBBranch.Name}}</a>
@ -102,7 +102,7 @@
<button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button> <button class="btn interact-fg tw-px-1" data-clipboard-text="{{.DBBranch.Name}}" data-tooltip-content="{{ctx.Locale.Tr "copy_branch"}}">{{svg "octicon-copy" 14}}</button>
{{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}} {{template "repo/commit_statuses" dict "Status" (index $.CommitStatus .DBBranch.CommitID) "Statuses" (index $.CommitStatuses .DBBranch.CommitID)}}
</div> </div>
<p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated" (TimeSince .DBBranch.CommitTime.AsTime ctx.Locale)}} {{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p> <p class="info tw-flex tw-items-center tw-my-1">{{svg "octicon-git-commit" 16 "tw-mr-1"}}<a href="{{$.RepoLink}}/commit/{{PathEscape .DBBranch.CommitID}}">{{ShortSha .DBBranch.CommitID}}</a> · <span class="commit-message">{{RenderCommitMessage $.Context .DBBranch.CommitMessage ($.Repository.ComposeMetas ctx)}}</span> · {{ctx.Locale.Tr "org.repo_updated" (DateUtils.TimeSince .DBBranch.CommitTime)}}{{if .DBBranch.Pusher}} &nbsp;{{template "shared/user/avatarlink" dict "user" .DBBranch.Pusher}} &nbsp;{{template "shared/user/namelink" .DBBranch.Pusher}}{{end}}</p>
{{end}} {{end}}
</td> </td>
<td class="two wide ui"> <td class="two wide ui">

View File

@ -1,7 +1,7 @@
{{range .RecentlyPushedNewBranches}} {{range .RecentlyPushedNewBranches}}
<div class="ui positive message tw-flex tw-items-center tw-gap-2"> <div class="ui positive message tw-flex tw-items-center tw-gap-2">
<div class="tw-flex-1 tw-break-anywhere"> <div class="tw-flex-1 tw-break-anywhere">
{{$timeSince := TimeSince .CommitTime.AsTime ctx.Locale}} {{$timeSince := DateUtils.TimeSince .CommitTime}}
{{$repo := .GetRepo $.Context}} {{$repo := .GetRepo $.Context}}
{{$name := .Name}} {{$name := .Name}}
{{if ne $repo.ID $.Repository.ID}} {{if ne $repo.ID $.Repository.ID}}

View File

@ -152,7 +152,7 @@
{{ctx.AvatarUtils.AvatarByEmail .Commit.Author.Email .Commit.Author.Email 28 "tw-mr-2"}} {{ctx.AvatarUtils.AvatarByEmail .Commit.Author.Email .Commit.Author.Email 28 "tw-mr-2"}}
<strong>{{.Commit.Author.Name}}</strong> <strong>{{.Commit.Author.Name}}</strong>
{{end}} {{end}}
<span class="text grey tw-ml-2" id="authored-time">{{TimeSince .Commit.Author.When ctx.Locale}}</span> <span class="text grey tw-ml-2" id="authored-time">{{DateUtils.TimeSince .Commit.Author.When}}</span>
{{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}} {{if or (ne .Commit.Committer.Name .Commit.Author.Name) (ne .Commit.Committer.Email .Commit.Author.Email)}}
<span class="tw-ml-2"></span> <span class="tw-ml-2"></span>
<span class="text grey tw-mx-2">{{ctx.Locale.Tr "repo.diff.committed_by"}}</span> <span class="text grey tw-mx-2">{{ctx.Locale.Tr "repo.diff.committed_by"}}</span>
@ -274,7 +274,7 @@
{{else}} {{else}}
<strong>{{.NoteCommit.Author.Name}}</strong> <strong>{{.NoteCommit.Author.Name}}</strong>
{{end}} {{end}}
<span class="text grey" id="note-authored-time">{{TimeSince .NoteCommit.Author.When ctx.Locale}}</span> <span class="text grey" id="note-authored-time">{{DateUtils.TimeSince .NoteCommit.Author.When}}</span>
</div> </div>
<div class="ui bottom attached info segment git-notes"> <div class="ui bottom attached info segment git-notes">
<pre class="commit-body">{{.NoteRendered | SanitizeHTML}}</pre> <pre class="commit-body">{{.NoteRendered | SanitizeHTML}}</pre>

View File

@ -74,9 +74,9 @@
{{end}} {{end}}
</td> </td>
{{if .Committer}} {{if .Committer}}
<td class="text right aligned">{{TimeSince .Committer.When ctx.Locale}}</td> <td class="text right aligned">{{DateUtils.TimeSince .Committer.When}}</td>
{{else}} {{else}}
<td class="text right aligned">{{TimeSince .Author.When ctx.Locale}}</td> <td class="text right aligned">{{DateUtils.TimeSince .Author.When}}</td>
{{end}} {{end}}
<td class="text right aligned tw-py-0"> <td class="text right aligned tw-py-0">
<button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button> <button class="btn interact-bg tw-p-2" data-tooltip-content="{{ctx.Locale.Tr "copy_hash"}}" data-clipboard-text="{{.ID}}">{{svg "octicon-copy"}}</button>

View File

@ -1,6 +1,6 @@
{{range .comments}} {{range .comments}}
{{$createdStr:= TimeSinceUnix .CreatedUnix ctx.Locale}} {{$createdStr:= DateUtils.TimeSince .CreatedUnix}}
<div class="comment" id="{{.HashTag}}"> <div class="comment" id="{{.HashTag}}">
{{if .OriginalAuthor}} {{if .OriginalAuthor}}
<span class="avatar">{{ctx.AvatarUtils.Avatar nil}}</span> <span class="avatar">{{ctx.AvatarUtils.Avatar nil}}</span>

View File

@ -212,7 +212,7 @@
{{if .Repository.ArchivedUnix.IsZero}} {{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}} {{ctx.Locale.Tr "repo.archive.title"}}
{{else}} {{else}}
{{ctx.Locale.Tr "repo.archive.title_date" (ctx.DateUtils.AbsoluteLong .Repository.ArchivedUnix)}} {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}

View File

@ -10,7 +10,7 @@
{{if .Repository.ArchivedUnix.IsZero}} {{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}} {{ctx.Locale.Tr "repo.archive.title"}}
{{else}} {{else}}
{{ctx.Locale.Tr "repo.archive.title_date" (ctx.DateUtils.AbsoluteLong .Repository.ArchivedUnix)}} {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}

View File

@ -71,7 +71,7 @@
{{$userName}} {{$userName}}
{{end}} {{end}}
</span> </span>
<span class="time tw-flex tw-items-center">{{ctx.DateUtils.FullTime $commit.Date}}</span> <span class="time tw-flex tw-items-center">{{DateUtils.FullTime $commit.Date}}</span>
{{end}} {{end}}
</li> </li>
{{end}} {{end}}

View File

@ -74,7 +74,7 @@
<div class="fork-flag"> <div class="fork-flag">
{{ctx.Locale.Tr "repo.mirror_from"}} {{ctx.Locale.Tr "repo.mirror_from"}}
<a target="_blank" rel="noopener noreferrer" href="{{$.PullMirror.RemoteAddress}}">{{$.PullMirror.RemoteAddress}}</a> <a target="_blank" rel="noopener noreferrer" href="{{$.PullMirror.RemoteAddress}}">{{$.PullMirror.RemoteAddress}}</a>
{{if $.PullMirror.UpdatedUnix}}{{ctx.Locale.Tr "repo.mirror_sync"}} {{TimeSinceUnix $.PullMirror.UpdatedUnix ctx.Locale}}{{end}} {{if $.PullMirror.UpdatedUnix}}{{ctx.Locale.Tr "repo.mirror_sync"}} {{DateUtils.TimeSince $.PullMirror.UpdatedUnix}}{{end}}
</div> </div>
{{end}} {{end}}
{{if .IsFork}}<div class="fork-flag">{{ctx.Locale.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullName}}</a></div>{{end}} {{if .IsFork}}<div class="fork-flag">{{ctx.Locale.Tr "repo.forked_from"}} <a href="{{.BaseRepo.Link}}">{{.BaseRepo.FullName}}</a></div>{{end}}

View File

@ -53,7 +53,7 @@
{{if .Repository.ArchivedUnix.IsZero}} {{if .Repository.ArchivedUnix.IsZero}}
{{ctx.Locale.Tr "repo.archive.title"}} {{ctx.Locale.Tr "repo.archive.title"}}
{{else}} {{else}}
{{ctx.Locale.Tr "repo.archive.title_date" (ctx.DateUtils.AbsoluteLong .Repository.ArchivedUnix)}} {{ctx.Locale.Tr "repo.archive.title_date" (DateUtils.AbsoluteLong .Repository.ArchivedUnix)}}
{{end}} {{end}}
</div> </div>
{{end}} {{end}}

View File

@ -24,7 +24,7 @@
<div class="meta"> <div class="meta">
<span class="text light grey muted-links"> <span class="text light grey muted-links">
{{if not $.Page.Repository}}{{.Repo.FullName}}{{end}}#{{.Index}} {{if not $.Page.Repository}}{{.Repo.FullName}}{{end}}#{{.Index}}
{{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}} {{$timeStr := DateUtils.TimeSince .GetLastEventTimestamp}}
{{if .OriginalAuthor}} {{if .OriginalAuthor}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}} {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}}
{{else if gt .Poster.ID 0}} {{else if gt .Poster.ID 0}}

View File

@ -146,13 +146,11 @@
</span> </span>
{{svg "octicon-triangle-down" 14 "dropdown icon"}} {{svg "octicon-triangle-down" 14 "dropdown icon"}}
<div class="menu"> <div class="menu">
<a rel="nofollow" class="{{if or (eq .SortType "latest") (not .SortType)}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=latest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.latest"}}</a> <a rel="nofollow" class="{{if or (eq .SortType "relevance") (not .SortType)}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=relevency&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.relevance"}}</a>
<a rel="nofollow" class="{{if eq .SortType "oldest"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=oldest&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.oldest"}}</a> {{$o := .}}
<a rel="nofollow" class="{{if eq .SortType "recentupdate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=recentupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.recentupdate"}}</a> {{range $opt := StringUtils.Make "latest" "oldest" "recentupdate" "leastupdate" "mostcomment" "leastcomment" "nearduedate" "farduedate"}}
<a rel="nofollow" class="{{if eq .SortType "leastupdate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastupdate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastupdate"}}</a> {{$text := ctx.Locale.Tr (printf "repo.issues.filter_sort.%s" $opt)}}
<a rel="nofollow" class="{{if eq .SortType "mostcomment"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=mostcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.mostcomment"}}</a> <a rel="nofollow" class="{{if eq $o.SortType $opt}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort={{$opt}}&state={{$.State}}&labels={{$o.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{$text}}</a>
<a rel="nofollow" class="{{if eq .SortType "leastcomment"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=leastcomment&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.leastcomment"}}</a> {{end}}
<a rel="nofollow" class="{{if eq .SortType "nearduedate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=nearduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.nearduedate"}}</a>
<a rel="nofollow" class="{{if eq .SortType "farduedate"}}active {{end}}item" href="?q={{$.Keyword}}&type={{$.ViewType}}&sort=farduedate&state={{$.State}}&labels={{.SelectLabels}}&milestone={{$.MilestoneID}}&project={{$.ProjectID}}&assignee={{$.AssigneeID}}&poster={{$.PosterID}}&fuzzy={{$.IsFuzzy}}{{if $.ShowArchivedLabels}}&archived=true{{end}}">{{ctx.Locale.Tr "repo.issues.filter_sort.farduedate"}}</a>
</div> </div>
</div> </div>

View File

@ -30,7 +30,7 @@
<progress class="milestone-progress-big" value="{{.Milestone.Completeness}}" max="100"></progress> <progress class="milestone-progress-big" value="{{.Milestone.Completeness}}" max="100"></progress>
<div class="tw-flex tw-gap-4"> <div class="tw-flex tw-gap-4">
<div class="tw-flex tw-items-center"> <div class="tw-flex tw-items-center">
{{$closedDate:= TimeSinceUnix .Milestone.ClosedDateUnix ctx.Locale}} {{$closedDate:= DateUtils.TimeSince .Milestone.ClosedDateUnix}}
{{if .IsClosed}} {{if .IsClosed}}
{{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}} {{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}}
{{else}} {{else}}
@ -38,7 +38,7 @@
{{if .Milestone.DeadlineString}} {{if .Milestone.DeadlineString}}
<span{{if .IsOverdue}} class="text red"{{end}}> <span{{if .IsOverdue}} class="text red"{{end}}>
{{svg "octicon-calendar"}} {{svg "octicon-calendar"}}
{{ctx.DateUtils.AbsoluteShort (.Milestone.DeadlineString|ctx.DateUtils.ParseLegacy)}} {{DateUtils.AbsoluteShort (.Milestone.DeadlineString|DateUtils.ParseLegacy)}}
</span> </span>
{{else}} {{else}}
{{svg "octicon-calendar"}} {{svg "octicon-calendar"}}

View File

@ -49,19 +49,19 @@
{{if .UpdatedUnix}} {{if .UpdatedUnix}}
<div class="flex-text-block"> <div class="flex-text-block">
{{svg "octicon-clock"}} {{svg "octicon-clock"}}
{{ctx.Locale.Tr "repo.milestones.update_ago" (TimeSinceUnix .UpdatedUnix ctx.Locale)}} {{ctx.Locale.Tr "repo.milestones.update_ago" (DateUtils.TimeSince .UpdatedUnix)}}
</div> </div>
{{end}} {{end}}
<div class="flex-text-block"> <div class="flex-text-block">
{{if .IsClosed}} {{if .IsClosed}}
{{$closedDate:= TimeSinceUnix .ClosedDateUnix ctx.Locale}} {{$closedDate:= DateUtils.TimeSince .ClosedDateUnix}}
{{svg "octicon-clock" 14}} {{svg "octicon-clock" 14}}
{{ctx.Locale.Tr "repo.milestones.closed" $closedDate}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}}
{{else}} {{else}}
{{if .DeadlineString}} {{if .DeadlineString}}
<span class="flex-text-inline {{if .IsOverdue}}text red{{end}}"> <span class="flex-text-inline {{if .IsOverdue}}text red{{end}}">
{{svg "octicon-calendar" 14}} {{svg "octicon-calendar" 14}}
{{ctx.DateUtils.AbsoluteShort (.DeadlineString|ctx.DateUtils.ParseLegacy)}} {{DateUtils.AbsoluteShort (.DeadlineString|DateUtils.ParseLegacy)}}
</span> </span>
{{else}} {{else}}
{{svg "octicon-calendar" 14}} {{svg "octicon-calendar" 14}}

View File

@ -6,7 +6,7 @@
<input type="hidden" id="issueIndex" value="{{.Issue.Index}}"> <input type="hidden" id="issueIndex" value="{{.Issue.Index}}">
<input type="hidden" id="type" value="{{.IssueType}}"> <input type="hidden" id="type" value="{{.IssueType}}">
{{$createdStr:= TimeSinceUnix .Issue.CreatedUnix ctx.Locale}} {{$createdStr:= DateUtils.TimeSince .Issue.CreatedUnix}}
<div class="issue-content-left comment-list prevent-before-timeline"> <div class="issue-content-left comment-list prevent-before-timeline">
<div class="ui timeline"> <div class="ui timeline">
<div id="{{.Issue.HashTag}}" class="timeline-item comment first"> <div id="{{.Issue.HashTag}}" class="timeline-item comment first">

View File

@ -1,7 +1,7 @@
{{template "base/alert"}} {{template "base/alert"}}
{{range .Issue.Comments}} {{range .Issue.Comments}}
{{if call $.ShouldShowCommentType .Type}} {{if call $.ShouldShowCommentType .Type}}
{{$createdStr:= TimeSinceUnix .CreatedUnix ctx.Locale}} {{$createdStr:= DateUtils.TimeSince .CreatedUnix}}
<!-- 0 = COMMENT, 1 = REOPEN, 2 = CLOSE, 3 = ISSUE_REF, 4 = COMMIT_REF, <!-- 0 = COMMENT, 1 = REOPEN, 2 = CLOSE, 3 = ISSUE_REF, 4 = COMMIT_REF,
5 = COMMENT_REF, 6 = PULL_REF, 7 = COMMENT_LABEL, 8 = MILESTONE_CHANGE, 5 = COMMENT_REF, 6 = PULL_REF, 7 = COMMENT_LABEL, 8 = MILESTONE_CHANGE,
@ -137,7 +137,7 @@
{{else if eq .RefAction 2}} {{else if eq .RefAction 2}}
{{$refTr = "repo.issues.ref_reopening_from"}} {{$refTr = "repo.issues.ref_reopening_from"}}
{{end}} {{end}}
{{$createdStr:= TimeSinceUnix .CreatedUnix ctx.Locale}} {{$createdStr:= DateUtils.TimeSince .CreatedUnix}}
<div class="timeline-item event" id="{{.HashTag}}"> <div class="timeline-item event" id="{{.HashTag}}">
<span class="badge">{{svg "octicon-bookmark"}}</span> <span class="badge">{{svg "octicon-bookmark"}}</span>
{{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/avatarlink" dict "user" .Poster}}
@ -300,7 +300,7 @@
{{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/avatarlink" dict "user" .Poster}}
<span class="text grey muted-links"> <span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}} {{template "shared/user/authorlink" .Poster}}
{{$dueDate := ctx.DateUtils.AbsoluteLong (.Content|ctx.DateUtils.ParseLegacy)}} {{$dueDate := DateUtils.AbsoluteLong (.Content|DateUtils.ParseLegacy)}}
{{ctx.Locale.Tr "repo.issues.due_date_added" $dueDate $createdStr}} {{ctx.Locale.Tr "repo.issues.due_date_added" $dueDate $createdStr}}
</span> </span>
</div> </div>
@ -312,8 +312,8 @@
{{template "shared/user/authorlink" .Poster}} {{template "shared/user/authorlink" .Poster}}
{{$parsedDeadline := StringUtils.Split .Content "|"}} {{$parsedDeadline := StringUtils.Split .Content "|"}}
{{if eq (len $parsedDeadline) 2}} {{if eq (len $parsedDeadline) 2}}
{{$to := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 0)|ctx.DateUtils.ParseLegacy)}} {{$to := DateUtils.AbsoluteLong ((index $parsedDeadline 0)|DateUtils.ParseLegacy)}}
{{$from := ctx.DateUtils.AbsoluteLong ((index $parsedDeadline 1)|ctx.DateUtils.ParseLegacy)}} {{$from := DateUtils.AbsoluteLong ((index $parsedDeadline 1)|DateUtils.ParseLegacy)}}
{{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}} {{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}}
{{end}} {{end}}
</span> </span>
@ -324,7 +324,7 @@
{{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/avatarlink" dict "user" .Poster}}
<span class="text grey muted-links"> <span class="text grey muted-links">
{{template "shared/user/authorlink" .Poster}} {{template "shared/user/authorlink" .Poster}}
{{$dueDate := ctx.DateUtils.AbsoluteLong (.Content|ctx.DateUtils.ParseLegacy)}} {{$dueDate := DateUtils.AbsoluteLong (.Content|DateUtils.ParseLegacy)}}
{{ctx.Locale.Tr "repo.issues.due_date_remove" $dueDate $createdStr}} {{ctx.Locale.Tr "repo.issues.due_date_remove" $dueDate $createdStr}}
</span> </span>
</div> </div>

View File

@ -51,7 +51,7 @@
<div id="code-comments-{{(index .comments 0).ID}}" class="comment-code-cloud ui segment{{if $resolved}} tw-hidden{{end}}"> <div id="code-comments-{{(index .comments 0).ID}}" class="comment-code-cloud ui segment{{if $resolved}} tw-hidden{{end}}">
<div class="ui comments tw-mb-0"> <div class="ui comments tw-mb-0">
{{range .comments}} {{range .comments}}
{{$createdSubStr:= TimeSinceUnix .CreatedUnix ctx.Locale}} {{$createdSubStr:= DateUtils.TimeSince .CreatedUnix}}
<div class="comment code-comment tw-pb-4" id="{{.HashTag}}"> <div class="comment code-comment tw-pb-4" id="{{.HashTag}}">
<div class="content"> <div class="content">
<div class="header comment-header"> <div class="header comment-header">

View File

@ -202,7 +202,7 @@
{{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}}
{{$hasPendingPullRequestMergeTip := ""}} {{$hasPendingPullRequestMergeTip := ""}}
{{if .HasPendingPullRequestMerge}} {{if .HasPendingPullRequestMerge}}
{{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix ctx.Locale}} {{$createdPRMergeStr := DateUtils.TimeSince .PendingPullRequestMerge.CreatedUnix}}
{{$hasPendingPullRequestMergeTip = ctx.Locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}} {{$hasPendingPullRequestMergeTip = ctx.Locale.Tr "repo.pulls.auto_merge_has_pending_schedule" .PendingPullRequestMerge.Doer.Name $createdPRMergeStr}}
{{end}} {{end}}
<div class="divider"></div> <div class="divider"></div>

View File

@ -9,7 +9,7 @@
<div class="tw-flex tw-justify-between tw-items-center"> <div class="tw-flex tw-justify-between tw-items-center">
<div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}> <div class="due-date {{if .Issue.IsOverdue}}text red{{end}}" {{if .Issue.IsOverdue}}data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date_overdue"}}"{{end}}>
{{svg "octicon-calendar" 16 "tw-mr-2"}} {{svg "octicon-calendar" 16 "tw-mr-2"}}
{{ctx.DateUtils.AbsoluteLong .Issue.DeadlineUnix}} {{DateUtils.AbsoluteLong .Issue.DeadlineUnix}}
</div> </div>
<div> <div>
{{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}} {{if and .HasIssuesOrPullsWritePermission (not .Repository.IsArchived)}}

View File

@ -60,7 +60,7 @@
{{$baseHref = HTMLFormat `<a href="%s">%s</a>` .BaseBranchLink $baseHref}} {{$baseHref = HTMLFormat `<a href="%s">%s</a>` .BaseBranchLink $baseHref}}
{{end}} {{end}}
{{if .Issue.PullRequest.HasMerged}} {{if .Issue.PullRequest.HasMerged}}
{{$mergedStr:= TimeSinceUnix .Issue.PullRequest.MergedUnix ctx.Locale}} {{$mergedStr:= DateUtils.TimeSince .Issue.PullRequest.MergedUnix}}
{{if .Issue.OriginalAuthor}} {{if .Issue.OriginalAuthor}}
{{.Issue.OriginalAuthor}} {{.Issue.OriginalAuthor}}
<span class="pull-desc">{{ctx.Locale.TrN .NumCommits "repo.pulls.merged_title_desc_one" "repo.pulls.merged_title_desc_few" .NumCommits $headHref $baseHref $mergedStr}}</span> <span class="pull-desc">{{ctx.Locale.TrN .NumCommits "repo.pulls.merged_title_desc_one" "repo.pulls.merged_title_desc_few" .NumCommits $headHref $baseHref $mergedStr}}</span>
@ -126,7 +126,7 @@
</span> </span>
{{end}} {{end}}
{{else}} {{else}}
{{$createdStr:= TimeSinceUnix .Issue.CreatedUnix ctx.Locale}} {{$createdStr:= DateUtils.TimeSince .Issue.CreatedUnix}}
<span class="time-desc"> <span class="time-desc">
{{if .Issue.OriginalAuthor}} {{if .Issue.OriginalAuthor}}
{{ctx.Locale.Tr "repo.issues.opened_by_fake" $createdStr .Issue.OriginalAuthor}} {{ctx.Locale.Tr "repo.issues.opened_by_fake" $createdStr .Issue.OriginalAuthor}}

View File

@ -1,5 +1,5 @@
<h2 class="ui header activity-header"> <h2 class="ui header activity-header">
<span>{{ctx.DateUtils.AbsoluteLong .DateFrom}} - {{ctx.DateUtils.AbsoluteLong .DateUntil}}</span> <span>{{DateUtils.AbsoluteLong .DateFrom}} - {{DateUtils.AbsoluteLong .DateUntil}}</span>
<!-- Period --> <!-- Period -->
<div class="ui floating dropdown jump filter"> <div class="ui floating dropdown jump filter">
<div class="ui basic compact button"> <div class="ui basic compact button">
@ -135,7 +135,7 @@
{{.TagName}} {{.TagName}}
<a class="title" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a> <a class="title" href="{{$.RepoLink}}/releases/tag/{{.TagName | PathEscapeSegments}}">{{.Title | RenderEmoji $.Context | RenderCodeBlock}}</a>
{{end}} {{end}}
{{TimeSinceUnix .CreatedUnix ctx.Locale}} {{DateUtils.TimeSince .CreatedUnix}}
</p> </p>
{{end}} {{end}}
</div> </div>
@ -154,7 +154,7 @@
<p class="desc"> <p class="desc">
<span class="ui purple label">{{ctx.Locale.Tr "repo.activity.merged_prs_label"}}</span> <span class="ui purple label">{{ctx.Locale.Tr "repo.activity.merged_prs_label"}}</span>
#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{RenderRefIssueTitle $.Context .Issue.Title}}</a> #{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{RenderRefIssueTitle $.Context .Issue.Title}}</a>
{{TimeSinceUnix .MergedUnix ctx.Locale}} {{DateUtils.TimeSince .MergedUnix}}
</p> </p>
{{end}} {{end}}
</div> </div>
@ -173,7 +173,7 @@
<p class="desc"> <p class="desc">
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.opened_prs_label"}}</span> <span class="ui green label">{{ctx.Locale.Tr "repo.activity.opened_prs_label"}}</span>
#{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{RenderRefIssueTitle $.Context .Issue.Title}}</a> #{{.Index}} <a class="title" href="{{$.RepoLink}}/pulls/{{.Index}}">{{RenderRefIssueTitle $.Context .Issue.Title}}</a>
{{TimeSinceUnix .Issue.CreatedUnix ctx.Locale}} {{DateUtils.TimeSince .Issue.CreatedUnix}}
</p> </p>
{{end}} {{end}}
</div> </div>
@ -192,7 +192,7 @@
<p class="desc"> <p class="desc">
<span class="ui red label">{{ctx.Locale.Tr "repo.activity.closed_issue_label"}}</span> <span class="ui red label">{{ctx.Locale.Tr "repo.activity.closed_issue_label"}}</span>
#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{RenderRefIssueTitle $.Context .Title}}</a> #{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{RenderRefIssueTitle $.Context .Title}}</a>
{{TimeSinceUnix .ClosedUnix ctx.Locale}} {{DateUtils.TimeSince .ClosedUnix}}
</p> </p>
{{end}} {{end}}
</div> </div>
@ -211,7 +211,7 @@
<p class="desc"> <p class="desc">
<span class="ui green label">{{ctx.Locale.Tr "repo.activity.new_issue_label"}}</span> <span class="ui green label">{{ctx.Locale.Tr "repo.activity.new_issue_label"}}</span>
#{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{RenderRefIssueTitle $.Context .Title}}</a> #{{.Index}} <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{RenderRefIssueTitle $.Context .Title}}</a>
{{TimeSinceUnix .CreatedUnix ctx.Locale}} {{DateUtils.TimeSince .CreatedUnix}}
</p> </p>
{{end}} {{end}}
</div> </div>
@ -232,7 +232,7 @@
{{else}} {{else}}
<a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{RenderRefIssueTitle $.Context .Title}}</a> <a class="title" href="{{$.RepoLink}}/issues/{{.Index}}">{{RenderRefIssueTitle $.Context .Title}}</a>
{{end}} {{end}}
{{TimeSinceUnix .UpdatedUnix ctx.Locale}} {{DateUtils.TimeSince .UpdatedUnix}}
</p> </p>
{{end}} {{end}}
</div> </div>

View File

@ -51,7 +51,7 @@
{{ctx.Locale.Tr "repo.released_this"}} {{ctx.Locale.Tr "repo.released_this"}}
</span> </span>
{{if $release.CreatedUnix}} {{if $release.CreatedUnix}}
<span class="time">{{TimeSinceUnix $release.CreatedUnix ctx.Locale}}</span> <span class="time">{{DateUtils.TimeSince $release.CreatedUnix}}</span>
{{end}} {{end}}
{{if and (not $release.IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} {{if and (not $release.IsDraft) ($.Permission.CanRead $.UnitTypeCode)}}
| <span class="ahead"><a href="{{$.RepoLink}}/compare/{{$release.TagName | PathEscapeSegments}}...{{$release.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" $release.NumCommitsBehind}}</a> {{ctx.Locale.Tr "repo.release.ahead.target" $release.TargetBehind}}</span> | <span class="ahead"><a href="{{$.RepoLink}}/compare/{{$release.TagName | PathEscapeSegments}}...{{$release.TargetBehind | PathEscapeSegments}}">{{ctx.Locale.Tr "repo.release.ahead.commits" $release.NumCommitsBehind}}</a> {{ctx.Locale.Tr "repo.release.ahead.target" $release.TargetBehind}}</span>

View File

@ -55,7 +55,7 @@
{{.Fingerprint}} {{.Fingerprint}}
</div> </div>
<div class="flex-item-body"> <div class="flex-item-body">
<p>{{ctx.Locale.Tr "settings.added_on" (ctx.DateUtils.AbsoluteShort .CreatedUnix)}}{{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="text green"{{end}}>{{ctx.DateUtils.AbsoluteShort .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} - <span>{{ctx.Locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{ctx.Locale.Tr "settings.can_write_info"}} {{end}}</span></p> <p>{{ctx.Locale.Tr "settings.added_on" (DateUtils.AbsoluteShort .CreatedUnix)}}{{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} <span {{if .HasRecentActivity}}class="text green"{{end}}>{{DateUtils.AbsoluteShort .UpdatedUnix}}</span>{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} - <span>{{ctx.Locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{ctx.Locale.Tr "settings.can_write_info"}} {{end}}</span></p>
</div> </div>
</div> </div>
<div class="flex-item-trailing"> <div class="flex-item-trailing">

View File

@ -17,7 +17,7 @@
</a> </a>
</td> </td>
<td>{{ctx.Locale.TrSize .Size}}</td> <td>{{ctx.Locale.TrSize .Size}}</td>
<td>{{TimeSince .CreatedUnix.AsTime ctx.Locale}}</td> <td>{{DateUtils.TimeSince .CreatedUnix}}</td>
<td class="right aligned"> <td class="right aligned">
<a class="ui primary button" href="{{$.Link}}/find?oid={{.Oid}}&size={{.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a> <a class="ui primary button" href="{{$.Link}}/find?oid={{.Oid}}&size={{.Size}}">{{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}</a>
<button class="ui basic show-modal icon button red" data-modal="#delete-{{.Oid}}"> <button class="ui basic show-modal icon button red" data-modal="#delete-{{.Oid}}">

View File

@ -32,7 +32,7 @@
{{ctx.Locale.Tr "repo.diff.commit"}} {{ctx.Locale.Tr "repo.diff.commit"}}
<a class="ui primary sha label" href="{{$.RepoLink}}/commit/{{.SHA}}">{{ShortSha .SHA}}</a> <a class="ui primary sha label" href="{{$.RepoLink}}/commit/{{.SHA}}">{{ShortSha .SHA}}</a>
</td> </td>
<td>{{TimeSince .When ctx.Locale}}</td> <td>{{DateUtils.TimeSince .When}}</td>
</tr> </tr>
{{else}} {{else}}
<tr> <tr>

View File

@ -35,7 +35,7 @@
{{$lock.Owner.DisplayName}} {{$lock.Owner.DisplayName}}
</a> </a>
</td> </td>
<td>{{TimeSince .Created ctx.Locale}}</td> <td>{{DateUtils.TimeSince .Created}}</td>
<td class="right aligned"> <td class="right aligned">
<form action="{{$.LFSFilesLink}}/locks/{{$lock.ID}}/unlock" method="post"> <form action="{{$.LFSFilesLink}}/locks/{{$lock.ID}}/unlock" method="post">
{{$.CsrfTokenHtml}} {{$.CsrfTokenHtml}}

View File

@ -5,7 +5,7 @@
{{ctx.Locale.Tr "repo.settings.options"}} {{ctx.Locale.Tr "repo.settings.options"}}
</a> </a>
<details class="item toggleable-item" {{if .PageIsRepoSettingsUnits}}open{{end}}> <details class="item toggleable-item" {{if .PageIsRepoSettingsUnits}}open{{end}}>
<summary class="item{{if .PageIsRepoSettingsUnits}} active{{end}}">{{ctx.Locale.Tr "repo.settings.units.units"}}</summary> <summary {{if .PageIsRepoSettingsUnits}}class="active"{{end}}>{{ctx.Locale.Tr "repo.settings.units.units"}}</summary>
<div class="menu"> <div class="menu">
<a class="item" href="{{.RepoLink}}/settings/units#overview"> <a class="item" href="{{.RepoLink}}/settings/units#overview">
{{ctx.Locale.Tr "repo.settings.units.overview"}} {{ctx.Locale.Tr "repo.settings.units.overview"}}

View File

@ -154,7 +154,7 @@
<tr> <tr>
<td>{{.PullMirror.RemoteAddress}}</td> <td>{{.PullMirror.RemoteAddress}}</td>
<td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td> <td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.pull"}}</td>
<td>{{ctx.DateUtils.FullTime .PullMirror.UpdatedUnix}}</td> <td>{{DateUtils.FullTime .PullMirror.UpdatedUnix}}</td>
<td class="right aligned"> <td class="right aligned">
<form method="post" class="tw-inline-block"> <form method="post" class="tw-inline-block">
{{.CsrfTokenHtml}} {{.CsrfTokenHtml}}
@ -243,7 +243,7 @@
<tr> <tr>
<td class="tw-break-anywhere">{{.RemoteAddress}}</td> <td class="tw-break-anywhere">{{.RemoteAddress}}</td>
<td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.push"}}</td> <td>{{ctx.Locale.Tr "repo.settings.mirror_settings.direction.push"}}</td>
<td>{{if .LastUpdateUnix}}{{ctx.DateUtils.FullTime .LastUpdateUnix}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{ctx.Locale.Tr "error"}}</div>{{end}}</td> <td>{{if .LastUpdateUnix}}{{DateUtils.FullTime .LastUpdateUnix}}{{else}}{{ctx.Locale.Tr "never"}}{{end}} {{if .LastError}}<div class="ui red label" data-tooltip-content="{{.LastError}}">{{ctx.Locale.Tr "error"}}</div>{{end}}</td>
<td>{{if not (eq (len .GetPublicKey) 0)}}<a data-clipboard-text="{{.GetPublicKey}}">{{ctx.Locale.Tr "repo.settings.mirror_settings.push_mirror.copy_public_key"}}</a>{{else}}{{ctx.Locale.Tr "repo.settings.mirror_settings.push_mirror.none_ssh"}}{{end}}</td> <td>{{if not (eq (len .GetPublicKey) 0)}}<a data-clipboard-text="{{.GetPublicKey}}">{{ctx.Locale.Tr "repo.settings.mirror_settings.push_mirror.copy_public_key"}}</a>{{else}}{{ctx.Locale.Tr "repo.settings.mirror_settings.push_mirror.none_ssh"}}{{end}}</td>
<td class="right aligned"> <td class="right aligned">
<button <button

View File

@ -29,7 +29,7 @@
<a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a> <a class="ui primary sha label toggle button show-panel" data-panel="#info-{{.ID}}">{{.UUID}}</a>
</div> </div>
<span class="text grey"> <span class="text grey">
{{TimeSince .Delivered.AsTime ctx.Locale}} {{DateUtils.TimeSince .Delivered}}
</span> </span>
</div> </div>
<div class="info tw-hidden" id="info-{{.ID}}"> <div class="info tw-hidden" id="info-{{.ID}}">

View File

@ -27,7 +27,7 @@
<div class="download tw-flex tw-items-center"> <div class="download tw-flex tw-items-center">
{{if $.Permission.CanRead $.UnitTypeCode}} {{if $.Permission.CanRead $.UnitTypeCode}}
{{if .CreatedUnix}} {{if .CreatedUnix}}
<span class="tw-mr-2">{{svg "octicon-clock" 16 "tw-mr-1"}}{{TimeSinceUnix .CreatedUnix ctx.Locale}}</span> <span class="tw-mr-2">{{svg "octicon-clock" 16 "tw-mr-1"}}{{DateUtils.TimeSince .CreatedUnix}}</span>
{{end}} {{end}}
<a class="tw-mr-2 tw-font-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha .Sha1}}</a> <a class="tw-mr-2 tw-font-mono muted" href="{{$.RepoLink}}/src/commit/{{.Sha1}}" rel="nofollow">{{svg "octicon-git-commit" 16 "tw-mr-1"}}{{ShortSha .Sha1}}</a>

View File

@ -20,7 +20,7 @@
{{else if .Location}} {{else if .Location}}
{{svg "octicon-location"}} {{.Location}} {{svg "octicon-location"}} {{.Location}}
{{else}} {{else}}
{{svg "octicon-calendar"}} {{ctx.Locale.Tr "user.joined_on" (ctx.DateUtils.AbsoluteShort .CreatedUnix)}} {{svg "octicon-calendar"}} {{ctx.Locale.Tr "user.joined_on" (DateUtils.AbsoluteShort .CreatedUnix)}}
{{end}} {{end}}
</div> </div>
</div> </div>

View File

@ -18,7 +18,7 @@
{{if .LatestCommit}} {{if .LatestCommit}}
{{if .LatestCommit.Committer}} {{if .LatestCommit.Committer}}
<div class="text grey age"> <div class="text grey age">
{{TimeSince .LatestCommit.Committer.When ctx.Locale}} {{DateUtils.TimeSince .LatestCommit.Committer.When}}
</div> </div>
{{end}} {{end}}
{{end}} {{end}}

View File

@ -8,7 +8,7 @@
</div> </div>
</div> </div>
</th> </th>
<th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When ctx.Locale}}{{end}}{{end}}</th> <th class="text grey right age">{{if .LatestCommit}}{{if .LatestCommit.Committer}}{{DateUtils.TimeSince .LatestCommit.Committer.When}}{{end}}{{end}}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -62,7 +62,7 @@
{{end}} {{end}}
</span> </span>
</td> </td>
<td class="text right age three wide">{{if $commit}}{{TimeSince $commit.Committer.When ctx.Locale}}{{end}}</td> <td class="text right age three wide">{{if $commit}}{{DateUtils.TimeSince $commit.Committer.When}}{{end}}</td>
</tr> </tr>
{{end}} {{end}}
</tbody> </tbody>

View File

@ -19,7 +19,7 @@
<a href="{{$.RepoLink}}/wiki/{{.SubURL}}">{{.Name}}</a> <a href="{{$.RepoLink}}/wiki/{{.SubURL}}">{{.Name}}</a>
<a class="wiki-git-entry" href="{{$.RepoLink}}/wiki/{{.GitEntryName | PathEscape}}" data-tooltip-content="{{ctx.Locale.Tr "repo.wiki.original_git_entry_tooltip"}}">{{svg "octicon-chevron-right"}}</a> <a class="wiki-git-entry" href="{{$.RepoLink}}/wiki/{{.GitEntryName | PathEscape}}" data-tooltip-content="{{ctx.Locale.Tr "repo.wiki.original_git_entry_tooltip"}}">{{svg "octicon-chevron-right"}}</a>
</td> </td>
{{$timeSince := TimeSinceUnix .UpdatedUnix ctx.Locale}} {{$timeSince := DateUtils.TimeSince .UpdatedUnix}}
<td class="text right">{{ctx.Locale.Tr "repo.wiki.last_updated" $timeSince}}</td> <td class="text right">{{ctx.Locale.Tr "repo.wiki.last_updated" $timeSince}}</td>
</tr> </tr>
{{end}} {{end}}

View File

@ -9,7 +9,7 @@
<a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}"><span>{{.revision}}</span> {{svg "octicon-home"}}</a> <a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.back_to_wiki"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}"><span>{{.revision}}</span> {{svg "octicon-home"}}</a>
{{$title}} {{$title}}
<div class="ui sub header tw-break-anywhere"> <div class="ui sub header tw-break-anywhere">
{{$timeSince := TimeSince .Author.When ctx.Locale}} {{$timeSince := DateUtils.TimeSince .Author.When}}
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}} {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
</div> </div>
</div> </div>

View File

@ -48,7 +48,7 @@
<a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" ><span>{{.CommitCount}}</span> {{svg "octicon-history"}}</a> <a class="file-revisions-btn ui basic button" title="{{ctx.Locale.Tr "repo.wiki.file_revision"}}" href="{{.RepoLink}}/wiki/{{.PageURL}}?action=_revision" ><span>{{.CommitCount}}</span> {{svg "octicon-history"}}</a>
{{$title}} {{$title}}
<div class="ui sub header"> <div class="ui sub header">
{{$timeSince := TimeSince .Author.When ctx.Locale}} {{$timeSince := DateUtils.TimeSince .Author.When}}
{{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}} {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
</div> </div>
</div> </div>

View File

@ -13,7 +13,7 @@
</div> </div>
<div class="field tw-inline-block tw-mr-4"> <div class="field tw-inline-block tw-mr-4">
<label>{{ctx.Locale.Tr "actions.runners.last_online"}}</label> <label>{{ctx.Locale.Tr "actions.runners.last_online"}}</label>
<span>{{if .Runner.LastOnline}}{{TimeSinceUnix .Runner.LastOnline ctx.Locale}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</span> <span>{{if .Runner.LastOnline}}{{DateUtils.TimeSince .Runner.LastOnline}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</span>
</div> </div>
<div class="field tw-inline-block tw-mr-4"> <div class="field tw-inline-block tw-mr-4">
<label>{{ctx.Locale.Tr "actions.runners.labels"}}</label> <label>{{ctx.Locale.Tr "actions.runners.labels"}}</label>
@ -70,7 +70,7 @@
<strong><a href="{{.GetCommitLink}}" target="_blank">{{ShortSha .CommitSHA}}</a></strong> <strong><a href="{{.GetCommitLink}}" target="_blank">{{ShortSha .CommitSHA}}</a></strong>
</td> </td>
<td>{{if .IsStopped}} <td>{{if .IsStopped}}
<span>{{TimeSinceUnix .Stopped ctx.Locale}}</span> <span>{{DateUtils.TimeSince .Stopped}}</span>
{{else}}-{{end}}</td> {{else}}-{{end}}</td>
</tr> </tr>
{{end}} {{end}}

View File

@ -73,7 +73,7 @@
<td class="tw-flex tw-flex-wrap tw-gap-2 runner-tags"> <td class="tw-flex tw-flex-wrap tw-gap-2 runner-tags">
{{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}} {{range .AgentLabels}}<span class="ui label">{{.}}</span>{{end}}
</td> </td>
<td>{{if .LastOnline}}{{TimeSinceUnix .LastOnline ctx.Locale}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</td> <td>{{if .LastOnline}}{{DateUtils.TimeSince .LastOnline}}{{else}}{{ctx.Locale.Tr "never"}}{{end}}</td>
<td class="runner-ops"> <td class="runner-ops">
{{if .Editable $.RunnerOwnerID $.RunnerRepoID}} {{if .Editable $.RunnerOwnerID $.RunnerRepoID}}
<a href="{{$.Link}}/{{.ID}}">{{svg "octicon-pencil"}}</a> <a href="{{$.Link}}/{{.ID}}">{{svg "octicon-pencil"}}</a>

View File

@ -60,7 +60,7 @@
#{{.Index}} #{{.Index}}
{{end}} {{end}}
</a> </a>
{{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}} {{$timeStr := DateUtils.TimeSince .GetLastEventTimestamp}}
{{if .OriginalAuthor}} {{if .OriginalAuthor}}
{{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}} {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr .OriginalAuthor}}
{{else if gt .Poster.ID 0}} {{else if gt .Poster.ID 0}}
@ -117,7 +117,7 @@
<span class="due-date flex-text-inline" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date"}}"> <span class="due-date flex-text-inline" data-tooltip-content="{{ctx.Locale.Tr "repo.issues.due_date"}}">
<span{{if .IsOverdue}} class="text red"{{end}}> <span{{if .IsOverdue}} class="text red"{{end}}>
{{svg "octicon-calendar" 14}} {{svg "octicon-calendar" 14}}
{{ctx.DateUtils.AbsoluteShort .DeadlineUnix}} {{DateUtils.AbsoluteShort .DeadlineUnix}}
</span> </span>
</span> </span>
{{end}} {{end}}

Some files were not shown because too many files have changed in this diff Show More