diff --git a/.air.toml b/.air.toml index de97bd8b29..d506c19426 100644 --- a/.air.toml +++ b/.air.toml @@ -2,9 +2,10 @@ root = "." tmp_dir = ".air" [build] +pre_cmd = ["killall -9 gitea 2>/dev/null || true"] # kill off potential zombie processes from previous runs cmd = "make --no-print-directory backend" bin = "gitea" -delay = 1000 +delay = 2000 include_ext = ["go", "tmpl"] include_file = ["main.go"] include_dir = ["cmd", "models", "modules", "options", "routers", "services"] @@ -15,8 +16,11 @@ exclude_dir = [ "modules/avatar/testdata", "modules/git/tests", "modules/migration/file_format_testdata", + "modules/markup/tests/repo/repo1_filepreview", "routers/private/tests", "services/gitdiff/testdata", + "services/migrations/testdata", + "services/webhook/sourcehut/testdata", ] exclude_regex = ["_test.go$", "_gen.go$"] stop_on_error = true diff --git a/.deadcode-out b/.deadcode-out index 0cdbd1ae6f..98b06bc0cd 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -73,6 +73,7 @@ package "code.gitea.io/gitea/models/migrations/base" func MainTest package "code.gitea.io/gitea/models/organization" + func GetTeamNamesByID func UpdateTeamUnits func (SearchMembersOptions).ToConds func UsersInTeamsCount @@ -139,6 +140,7 @@ package "code.gitea.io/gitea/models/user" func GetUserAllSettings func DeleteUserSetting func GetUserEmailsByNames + func GetUserNamesByIDs package "code.gitea.io/gitea/modules/assetfs" func Bindata diff --git a/.dockerignore b/.dockerignore index 86cc8f6087..a1611a1ca5 100644 --- a/.dockerignore +++ b/.dockerignore @@ -94,6 +94,9 @@ cpu.out /.air /.go-licenses +# Files and folders that were previously generated +/public/assets/img/webpack + # Snapcraft snap/.snapcraft/ parts/ diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 5fd0a245f2..f28d2ac247 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -4,6 +4,7 @@ reportUnusedDisableDirectives: true ignorePatterns: - /web_src/js/vendor - /web_src/fomantic + - /public/assets/js parserOptions: sourceType: module diff --git a/.forgejo/testdata/build-release/Dockerfile b/.forgejo/testdata/build-release/Dockerfile index 8dccad281c..f798448261 100644 --- a/.forgejo/testdata/build-release/Dockerfile +++ b/.forgejo/testdata/build-release/Dockerfile @@ -1,4 +1,6 @@ FROM code.forgejo.org/oci/alpine:3.19 ARG RELEASE_VERSION=unkown +LABEL maintainer="contact@forgejo.org" \ + org.opencontainers.image.version="${RELEASE_VERSION}" RUN mkdir -p /app/gitea RUN ( echo '#!/bin/sh' ; echo "echo forgejo v$RELEASE_VERSION" ) > /app/gitea/gitea ; chmod +x /app/gitea/gitea diff --git a/.forgejo/workflows/backport.yml b/.forgejo/workflows/backport.yml index da064feff3..6181dcf352 100644 --- a/.forgejo/workflows/backport.yml +++ b/.forgejo/workflows/backport.yml @@ -45,39 +45,13 @@ jobs: cat <<'EOF' ${{ toJSON(github) }} EOF - - name: Fetch labels - id: fetch-labels - shell: bash - run: | - set -x - echo "Labels retrieved below" - export DEBIAN_FRONTEND=noninteractive - apt-get update -qq - apt-get -q install -qq -y jq - filtered_labels=$(echo "$LABELS" | jq -c 'map(select(.name | startswith("backport/v")))') - echo "FILTERED_LABELS=${filtered_labels}" >> $GITHUB_ENV - env: - LABELS: ${{ toJSON(github.event.pull_request.labels) }} - - name: Extract targets - id: extract-targets - shell: bash - run: | - set -x - targets="$(echo $FILTERED_LABELS | jq -c '[.[] | .name | sub("backport/"; "")]')" - echo "targets=$(echo $targets)" >> $GITHUB_OUTPUT - - - name: Printing info - shell: bash - run: | - echo "targets: ${{ steps.extract-targets.outputs.targets }}" - echo "target-branch: ${{ fromJSON(steps.extract-targets.outputs.targets)[0] }}" - echo "pull-request: ${{ github.event.pull_request.url }}" - - - uses: https://code.forgejo.org/forgejo/git-backporting@b2554a678d5ea2814f0df3abad2ac4fcdee2d81f + - uses: https://code.forgejo.org/actions/git-backporting@v4.8.0 with: - target-branch: ${{ fromJSON(steps.extract-targets.outputs.targets)[0] }}/forgejo + target-branch-pattern: "^backport/(?(v.*))$" strategy: ort strategy-option: find-renames cherry-pick-options: -x auth: ${{ secrets.BACKPORT_TOKEN }} pull-request: ${{ github.event.pull_request.url }} + auto-no-squash: true + enable-err-notification: true diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index f8cd69850c..eb4297c7ef 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -159,7 +159,7 @@ jobs: - name: build container & release if: ${{ secrets.TOKEN != '' }} - uses: https://code.forgejo.org/forgejo/forgejo-build-publish/build@v3 + uses: https://code.forgejo.org/forgejo/forgejo-build-publish/build@v5.1.1 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -173,11 +173,12 @@ jobs: binary-name: forgejo binary-path: /app/gitea/gitea override: "${{ steps.release-info.outputs.override }}" + verify-labels: "maintainer=contact@forgejo.org,org.opencontainers.image.version=${{ steps.release-info.outputs.version }}" verbose: ${{ vars.VERBOSE || secrets.VERBOSE || 'false' }} - name: build rootless container if: ${{ secrets.TOKEN != '' }} - uses: https://code.forgejo.org/forgejo/forgejo-build-publish/build@v3 + uses: https://code.forgejo.org/forgejo/forgejo-build-publish/build@v5.1.1 with: forgejo: "${{ env.GITHUB_SERVER_URL }}" owner: "${{ env.GITHUB_REPOSITORY_OWNER }}" @@ -190,6 +191,7 @@ jobs: suffix: -rootless dockerfile: Dockerfile.rootless override: "${{ steps.release-info.outputs.override }}" + verify-labels: "maintainer=contact@forgejo.org,org.opencontainers.image.version=${{ steps.release-info.outputs.version }}" verbose: ${{ vars.VERBOSE || secrets.VERBOSE || 'false' }} - name: end-to-end tests diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 674c1c2704..b89e8d1d7b 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -42,7 +42,7 @@ jobs: - uses: actions/checkout@v3 - name: copy & sign - uses: https://code.forgejo.org/forgejo/forgejo-build-publish/publish@v4 + uses: https://code.forgejo.org/forgejo/forgejo-build-publish/publish@v5 with: from-forgejo: ${{ vars.FORGEJO }} to-forgejo: ${{ vars.FORGEJO }} diff --git a/.forgejo/workflows/renovate.yml b/.forgejo/workflows/renovate.yml index 457d5b5d56..6212b8ef0b 100644 --- a/.forgejo/workflows/renovate.yml +++ b/.forgejo/workflows/renovate.yml @@ -22,10 +22,11 @@ jobs: runs-on: docker container: - image: ghcr.io/visualon/renovate:37.280.0 + image: ghcr.io/visualon/renovate:37.323.3 steps: - - uses: https://code.forgejo.org/actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 + - name: Load renovate repo cache + uses: https://code.forgejo.org/actions/cache/restore@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2 with: path: | .tmp/cache/renovate/repository @@ -33,7 +34,8 @@ jobs: restore-keys: | repo-cache- - - run: renovate + - name: Run renovate + run: renovate env: GITHUB_COM_TOKEN: ${{ secrets.RENOVATE_GITHUB_COM_TOKEN }} LOG_LEVEL: debug diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index be0689006c..cd955c01af 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -140,6 +140,8 @@ jobs: env: MINIO_ROOT_USER: 123456 MINIO_ROOT_PASSWORD: 12345678 + ldap: + image: docker.io/gitea/test-openldap:latest pgsql: image: 'docker.io/postgres:15' env: @@ -176,6 +178,7 @@ jobs: TAGS: bindata RACE_ENABLED: true USE_REPO_TEST_DIR: 1 + TEST_LDAP: 1 test-sqlite: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker diff --git a/.gitattributes b/.gitattributes index 51131c7d83..4e748c071a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,5 +1,6 @@ * text=auto eol=lf *.tmpl linguist-language=go-html-template +*.pb.go linguist-generated /assets/*.json linguist-generated /public/assets/img/svg/*.svg linguist-generated /templates/swagger/v1_json.tmpl linguist-generated diff --git a/.gitignore b/.gitignore index be8db3b51d..ebbed981e1 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,9 @@ cpu.out /.go-licenses /.cur-deadcode-out +# Files and folders that were previously generated +/public/assets/img/webpack + # Snapcraft /gitea_a*.txt snap/.snapcraft/ diff --git a/.gitmodules b/.gitmodules index f5796e6e31..e69de29bb2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "manual-testing"] - path = manual-testing - url = https://codeberg.org/forgejo/forgejo-manual-testing diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2344d371cc..77c6463fbf 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,21 +4,4 @@ The Forgejo project is run by a community of people who are expected to follow t Sensitive security-related issues should be reported to [security@forgejo.org](mailto:security@forgejo.org) using [encryption](https://keyoxide.org/security@forgejo.org). -## For everyone involved - -- [Documentation](https://forgejo.org/docs/next/) -- [Code of Conduct](https://forgejo.org/docs/latest/developer/coc/) -- [Bugs, features, security and others discussions](https://forgejo.org/docs/latest/developer/discussions/) -- [Governance](https://forgejo.org/docs/latest/developer/governance/) -- [Sustainability and funding](https://codeberg.org/forgejo/sustainability/src/branch/main/README.md) - -## For contributors - -- [Developer Certificate of Origin (DCO)](https://forgejo.org/docs/latest/developer/dco/) -- [Development workflow](https://forgejo.org/docs/latest/developer/workflow/) -- [Compiling from source](https://forgejo.org/docs/latest/developer/from-source/) - -## For maintainers - -- [Release management](https://forgejo.org/docs/latest/developer/release/) -- [Secrets](https://forgejo.org/docs/latest/developer/secrets/) +You can find links to the different aspects of Developer documentation on this page: [Forgejo developer guide](https://forgejo.org/docs/next/developer/). diff --git a/Dockerfile b/Dockerfile index dcd6b0eba6..3c455a5411 100644 --- a/Dockerfile +++ b/Dockerfile @@ -52,7 +52,17 @@ RUN chmod 755 /tmp/local/usr/bin/entrypoint \ RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete FROM docker.io/library/alpine:3.19 -LABEL maintainer="contact@forgejo.org" +ARG RELEASE_VERSION +LABEL maintainer="contact@forgejo.org" \ + org.opencontainers.image.authors="Forgejo" \ + org.opencontainers.image.url="https://forgejo.org" \ + org.opencontainers.image.documentation="https://forgejo.org/download/#container-image" \ + org.opencontainers.image.source="https://codeberg.org/forgejo/forgejo" \ + org.opencontainers.image.version="${RELEASE_VERSION}" \ + org.opencontainers.image.vendor="Forgejo" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.title="Forgejo. Beyond coding. We forge." \ + org.opencontainers.image.description="Forgejo is a self-hosted lightweight software forge. Easy to install and low maintenance, it just does the job." EXPOSE 22 3000 diff --git a/Dockerfile.rootless b/Dockerfile.rootless index 33b7c3c3e0..3f4cba955a 100644 --- a/Dockerfile.rootless +++ b/Dockerfile.rootless @@ -50,7 +50,16 @@ RUN chmod 755 /tmp/local/usr/local/bin/docker-entrypoint.sh \ RUN chmod 644 /go/src/code.gitea.io/gitea/contrib/autocompletion/bash_autocomplete FROM docker.io/library/alpine:3.19 -LABEL maintainer="contact@forgejo.org" +LABEL maintainer="contact@forgejo.org" \ + org.opencontainers.image.authors="Forgejo" \ + org.opencontainers.image.url="https://forgejo.org" \ + org.opencontainers.image.documentation="https://forgejo.org/download/#container-image" \ + org.opencontainers.image.source="https://codeberg.org/forgejo/forgejo" \ + org.opencontainers.image.version="${RELEASE_VERSION}" \ + org.opencontainers.image.vendor="Forgejo" \ + org.opencontainers.image.licenses="MIT" \ + org.opencontainers.image.title="Forgejo. Beyond coding. We forge." \ + org.opencontainers.image.description="Forgejo is a self-hosted lightweight software forge. Easy to install and low maintenance, it just does the job." EXPOSE 2222 3000 diff --git a/Makefile b/Makefile index 5f3bc29a81..6e46283ac8 100644 --- a/Makefile +++ b/Makefile @@ -26,18 +26,18 @@ DIFF ?= diff --unified XGO_VERSION := go-1.21.x -AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 -EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0 -GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 -GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 -MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.6-0.20240201115257-bcc7c78b7786 -XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest -GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 -GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3 -ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.26 -DEADCODE_PACKAGE ?= golang.org/x/tools/internal/cmd/deadcode@v0.14.0 +AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 # renovate: datasource=go +EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/v2/cmd/editorconfig-checker@2.8.0 # renovate: datasource=go +GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 # renovate: datasource=go +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.57.2 # 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.4.1 # renovate: datasource=go +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.6-0.20240201115257-bcc7c78b7786 # renovate: datasource=go +XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest # renovate: datasource=go +GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 # renovate: datasource=go +GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1 # renovate: datasource=go +ACTIONLINT_PACKAGE ?= github.com/rhysd/actionlint/cmd/actionlint@v1.6.27 # renovate: datasource=go +DEADCODE_PACKAGE ?= golang.org/x/tools/internal/cmd/deadcode@v0.14.0 # renovate: datasource=go DOCKER_IMAGE ?= gitea/gitea DOCKER_TAG ?= latest @@ -121,7 +121,6 @@ LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeV LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64 ifeq ($(HAS_GO), yes) - GO_PACKAGES ?= $(filter-out code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/)) GO_TEST_PACKAGES ?= $(filter-out $(shell $(GO) list code.gitea.io/gitea/models/migrations/...) $(shell $(GO) list code.gitea.io/gitea/models/forgejo_migrations/...) code.gitea.io/gitea/tests/integration/migration-test code.gitea.io/gitea/tests code.gitea.io/gitea/tests/integration code.gitea.io/gitea/tests/e2e,$(shell $(GO) list ./... | grep -v /vendor/)) endif @@ -155,9 +154,9 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN GO_DIRS := build cmd models modules routers services tests WEB_DIRS := web_src/js web_src/css -ESLINT_FILES := web_src/js tools *.config.js tests/e2e +ESLINT_FILES := web_src/js tools *.js tests/e2e STYLELINT_FILES := web_src/css web_src/js/components/*.vue -SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github +SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github $(wildcard *.go *.js *.md *.yml *.yaml *.toml) GO_SOURCES := $(wildcard *.go) GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go) @@ -245,7 +244,7 @@ help: @echo " - show-version-major show major release number only" @echo " - test-frontend test frontend files" @echo " - test-backend test backend files" - @echo " - test-e2e[\#TestSpecificName] test end to end using playwright" + @echo " - test-e2e-sqlite[\#name.test.e2e] test end to end using playwright and sqlite" @echo " - update update js and py dependencies" @echo " - update-js update js dependencies" @echo " - update-py update py dependencies" @@ -312,7 +311,7 @@ clean: .PHONY: fmt fmt: - GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' + @GOFUMPT_PACKAGE=$(GOFUMPT_PACKAGE) $(GO) run build/code-batch-process.go gitea-fmt -w '{file-list}' $(eval TEMPLATES := $(shell find templates -type f -name '*.tmpl')) @# strip whitespace after '{{' or '(' and before '}}' or ')' unless there is only @# whitespace before it @@ -335,8 +334,8 @@ ifneq "$(TAGS)" "$(shell cat $(TAGS_EVIDENCE) 2>/dev/null)" TAGS_PREREQ := $(TAGS_EVIDENCE) endif -OAPI_CODEGEN_PACKAGE ?= github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 -KIN_OPENAPI_CODEGEN_PACKAGE ?= github.com/getkin/kin-openapi/cmd/validate@v0.114.0 +OAPI_CODEGEN_PACKAGE ?= github.com/deepmap/oapi-codegen/cmd/oapi-codegen@v1.12.4 # renovate: datasource=go +KIN_OPENAPI_CODEGEN_PACKAGE ?= github.com/getkin/kin-openapi/cmd/validate@v0.114.0 # renovate: datasource=go FORGEJO_API_SERVER = routers/api/forgejo/v1/generated.go .PHONY: generate-forgejo-api @@ -453,7 +452,7 @@ lint-go-windows: .PHONY: lint-go-vet lint-go-vet: @echo "Running go vet..." - @$(GO) vet $(GO_PACKAGES) + @$(GO) vet ./... .PHONY: lint-editorconfig lint-editorconfig: @@ -628,6 +627,10 @@ test-e2e-sqlite: playwright e2e.sqlite.test generate-ini-sqlite test-e2e-sqlite\#%: playwright e2e.sqlite.test generate-ini-sqlite GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini ./e2e.sqlite.test -test.run TestE2e/$* +.PHONY: test-e2e-sqlite-firefox\#% +test-e2e-sqlite-firefox\#%: playwright e2e.sqlite.test generate-ini-sqlite + GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/sqlite.ini PLAYWRIGHT_PROJECT=firefox ./e2e.sqlite.test -test.run TestE2e/$* + .PHONY: test-e2e-mysql test-e2e-mysql: playwright e2e.mysql.test generate-ini-mysql GITEA_ROOT="$(CURDIR)" GITEA_CONF=tests/mysql.ini ./e2e.mysql.test -test.run TestE2e @@ -764,7 +767,7 @@ generate-backend: $(TAGS_PREREQ) generate-go .PHONY: generate-go generate-go: $(TAGS_PREREQ) @echo "Running go generate..." - @CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' $(GO_PACKAGES) + @CC= GOOS= GOARCH= $(GO) generate -tags '$(TAGS)' ./... .PHONY: merge-locales merge-locales: diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 7bb32310b4..85c8f8bcc1 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -1,8 +1,515 @@ # Release Notes -A Forgejo release is published shortly after a Gitea release is published and they have [matching release numbers](https://codeberg.org/forgejo/forgejo/src/branch/forgejo/CONTRIBUTING/RELEASE.md#release-numbering). Additional Forgejo releases may be published to address urgent security issues or bug fixes. +A minor or major Forgejo release is published every [three months](https://forgejo.org/docs/latest/user/versions/), with more patch releases in between depending on the severity of the bug and security fixes it contains. -The Forgejo admin should carefully read the required manual actions before upgrading. A point release (e.g. v1.21.1-0 or v1.21.2-0) does not require manual actions but others might (e.g. v1.20, v1.21). +A [patch or minor release](https://semver.org/spec/v2.0.0.html) (e.g. upgrading from v7.0.0 to v7.0.1 or v7.1.0) does not require manual intervention. But [major releases](https://semver.org/spec/v2.0.0.html#spec-item-8) where the first version number changes (e.g. upgrading from v1.21 to v7.0) contain breaking changes and the release notes explain how to deal with them. + +## Upcoming releases (not available yet) + +- [8.0.0](/release-notes/8.0.0/) + +## 7.0.0 + +The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v7.0/forgejo) included in the `Forgejo v7.0.0` release can be reviewed from the command line with: + +```shell +$ git clone https://codeberg.org/forgejo/forgejo/ +$ git -C forgejo log --oneline --no-merges origin/v1.21/forgejo..origin/v7.0/forgejo +``` + +* **Regressions and workarounds:** + * Running the [`forgejo doctor check --fix`](https://forgejo.org/docs/v7.0/admin/command-line/#doctor-check) CLI command or setting [`[cron.gc_lfs].ENABLED=true`](https://forgejo.org/docs/v7.0/admin/config-cheat-sheet/#cron---garbage-collect-lfs-pointers-in-repositories-crongc_lfs) (the default is `false`) will corrupt the LFS storage. The workaround is to not run the doctor CLI command and disable the `cron.gc_lfs`. This regression will be [fixed in 7.0.1](https://codeberg.org/forgejo/forgejo/issues/3438). + * The [`fogejo admin user create`](https://forgejo.org/docs/v7.0/admin/command-line/#admin-user-create) CLI command [requires a password](https://codeberg.org/forgejo/forgejo/commit/b122c6ef8b9254120432aed373cbe075331132ac) change by default when creating the first user and the `--admin` flag is not specified. The `--must-change-password=false` argument must be given to not require a password change. This regression will be [fixed in 7.0.1](https://codeberg.org/forgejo/forgejo/issues/3399). +* **Breaking changes requiring manual intervention:** + * [MySQL 8.0 or PostgreSQL 12](https://codeberg.org/forgejo/forgejo/commit/e94f9fcafdcf284561e7fb33f60156a69c4ad6a5) are the minimum supported versions. The database must be migrated before upgrading. The requirements regarding SQLite did not change. + * The `per_page` parameter is [no longer a synonym for `limit`](https://codeberg.org/forgejo/forgejo/commit/0aab2d38a7d91bc8caff332e452364468ce52d9a) in the [/repos/{owner}/{repo}/releases](https://code.forgejo.org/api/swagger/#/repository/repoListReleases) API endpoint. + * The date format of the `created` and `last_update` fields of the [`/repos/{owner}/{repo}/push_mirrors`](https://code.forgejo.org/api/swagger/#/repository/repoListPushMirrors) and [/repos/{owner}/{repo}/push_mirrors](https://code.forgejo.org/api/swagger/#/repository/repoAddPushMirror) API endpoint changed [to be timestamps instead of numbers](https://codeberg.org/forgejo/forgejo/commit/0ee7cbf725f45650136be45f8e0f74d395f73b5c). + * Labels used [by pprof endpoint](https://forgejo.org/docs/v7.0/admin/config-cheat-sheet/#server-server) have been changed: + * `graceful-lifecycle` to `gracefulLifecycle` + * `process-type` to `processType` + * `process-description` to `processDescription` + This allows for those endpoints to be scraped by services requiring prometheus style labels such as [grafana-agent](https://grafana.com/docs/agent/latest/). + * The repository description [imposes additional restrictions on what it contains](https://codeberg.org/forgejo/forgejo/commit/1075ff74b5050f671c5f9824ae39390230b3c85d) to prevent abuse. You may use [the v7.0 test instance](https://v7.next.forgejo.org/) to check how it will be modified. + * The [Gitea themes were renamed](https://codeberg.org/forgejo/forgejo/commit/023e937141dd891bce3370c869d4db2c60f971ed) and the `[ui].THEMES` setting must be changed as follows: + * `gitea` is replaced by `gitea-light` + * `arc-green` is replaced by `gitea-dark` + * `auto` is replaced by `gitea-auto` +* **Breaking changes in the user interface:** + Note that the modifications related to CSS, templates or assets (images, fonts, etc.) are not documented here. + Although they can be extracted and modified, Forgejo does not provide any guarantee that such changes + will be portable from one version to another (even a patch version). See also + [the developer documentation about interface customization](https://forgejo.org/docs/v1.21/developer/customization/). + * [Update checker setting might change](https://codeberg.org/forgejo/forgejo/pulls/2925). The documentation was listing it as enabled by default, however, for a while it was disabled unless it was explicitly specified in the config or on the installation page. Instances migrated from Gitea also had it disabled due to different default value. Since then Forgejo got a privacy-friendly DNS-based update checking mechanism which is now being enabled by default unless explicitly specified [in the config](https://forgejo.org/docs/v7.0/admin/config-cheat-sheet/#cron---check-for-new-forgejo-versions-cronupdate_checker). + * Language statistics for repositories that use `linguist` attributes in `.gitattributes` *may* show different statistics than previously, because Forgejo recognizes more [linguist attributes](https://forgejo.org/docs/v7.0/user/language-detection/) now. + * It is [no longer possible to replace the default web editor](https://codeberg.org/forgejo/forgejo/pulls/2916) used to write comments or issues and pull requests with the EasyMDE editor. It is however still available as an alternative to edit releases and wiki pages. + * [The list of all repositories and the `New Issue` button are no longer available in the user dashboard](https://codeberg.org/forgejo/forgejo/commit/beb71f5ef6e8074dc744ac995c15f7b5947a3f2e) for issues and pull requests. +* **Migration warning** + * If the logs show a line like the following, [run doctor convert](https://forgejo.org/docs/v7.0/admin/command-line/#doctor-convert) to fix it. + ``` + [W] Current database is using a case-insensitive collation "utf8mb4_general_ci" + ``` + * Large instances may experience slow migrations when the database is upgraded to support SHA-256 git repositories. For instance, here are the logs from a test migration of the https://codeberg.org production database: + ``` + [I] Migration[286]: Add support for SHA256 git repositories + [W] [Slow SQL Query] ALTER TABLE `commit_status` MODIFY COLUMN `context_hash` VARCHAR(64) [] - 3m41.647738396s + [W] [Slow SQL Query] ALTER TABLE `comment` MODIFY COLUMN `commit_sha` VARCHAR(64) [] - 1m5.500234133s + [W] [Slow SQL Query] ALTER TABLE `release` MODIFY COLUMN `sha1` VARCHAR(64) [] - 22.06241145s + ``` +* **Features and enhancements** + * Repository settings have been refactored, lifting out the repository unit-related settings to their own page. ([#2221](https://codeberg.org/forgejo/forgejo/pulls/2221)) + - When additional units can be enabled, an "Add more..." link will be displayed for repository admins. This can be turned off. ([#2533](https://codeberg.org/forgejo/forgejo/pulls/2533)) + * Repository administrators can [allow anyone to edit the wiki](https://forgejo.org/docs/v7.0/user/wiki/#activation-and-permissions) in the repository Settings. ([#2001](https://codeberg.org/forgejo/forgejo/pulls/2001)) + * Instance administrators can enable [repository badges](https://forgejo.org/docs/v7.0/user/readme-badges/) in the [configuration file](https://forgejo.org/docs/v7.0/admin/config-cheat-sheet/#badges-badges). This feature depends on a shield generator service such as shields.io, and is disabled by default. ([#2070](https://codeberg.org/forgejo/forgejo/pulls/2070)) + * Instance administrators can configure the additional clone methods displayed on the repository home view. ([gitea#29320](https://github.com/go-gitea/gitea/pull/29320)) + * Instance administrators can [assign custom flags to repositories](https://codeberg.org/forgejo/forgejo/pulls/2079). This is disabled by default, and currently requires custom templates to do anything useful with the flags. ([#2079](https://codeberg.org/forgejo/forgejo/pulls/2079) & [#2097](https://codeberg.org/forgejo/forgejo/pulls/2097)) + * Fallback for [basic repo search using git-grep](https://forgejo.org/docs/v7.0/user/code-search/) when code indexer is disabled ([gitea#29998](https://github.com/go-gitea/gitea/pull/29998)) + * Repository administrators can disable forking instance-wide by setting the new `[repository].DISABLE_FORKS` setting. ([#2445](https://codeberg.org/forgejo/forgejo/pulls/2445)) + * Render permalinks to files with a line range by an inline preview in all places where markup is allowed ([#2669](https://codeberg.org/forgejo/forgejo/pulls/2669)) + * A user can now optionally set their preferred pronouns ([#1518](https://codeberg.org/forgejo/forgejo/pulls/1518)). + * [Always enable caches](https://codeberg.org/forgejo/forgejo/commit/e7cb8da2a8310ac167b6f613b283caa3316a7154). + * Forgejo now recognizes more [linguist attributes](https://forgejo.org/docs/v7.0/user/language-detection/), making it possible to include documentation in the repository language statistics, for example. ([#2088](https://codeberg.org/forgejo/forgejo/pulls/2088)) + * When displaying the message to open a pull request from a recently pushed branch, the recently pushed branch now links to the appropriate branch. ([#2141](https://codeberg.org/forgejo/forgejo/pulls/2141)) + * Users who signed up, but have not activated their accounts yet, are now able to [change their email before activation](https://codeberg.org/forgejo/forgejo/pulls/1891). ([#1891](https://codeberg.org/forgejo/forgejo/pulls/1891)) + * The "You pushed on branch ...." banner is now displayed for repositories you have a fork of with recently pushed branches too ([#2195](https://codeberg.org/forgejo/forgejo/pulls/2195)), and it will no longer consider branches that share no history with the default branch. ([#2196](https://codeberg.org/forgejo/forgejo/pulls/2196)) + * Forgejo will now highlight signed tags in a similar way it highlights signed commits. ([#2534](https://codeberg.org/forgejo/forgejo/pulls/2534)) + * Forgejo gained support for the more recent GitHub-style alert blocks. ([#2348](https://codeberg.org/forgejo/forgejo/pulls/2348)) + - The older style remains supported too. + * [[ACTIONS] Add vars context to cron jobs](https://codeberg.org/forgejo/forgejo/pulls/3059) + * [[ACTIONS] Allow viewing the latest Action Run on the web](https://codeberg.org/forgejo/forgejo/pulls/1900) + * [[AGIT] Automatically fill in the description](https://codeberg.org/forgejo/forgejo/pulls/2344) + * [[API] Add API to get PR by base/head](https://codeberg.org/forgejo/forgejo/pulls/2481) + * [[API] commentAssignment() to verify the id belongs](https://codeberg.org/forgejo/forgejo/pulls/2126) + * [[API] DELETE /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments/{comment}](https://codeberg.org/forgejo/forgejo/pulls/2157) + * [[API] endpoint for adding comments to reviews](https://codeberg.org/forgejo/forgejo/pulls/2122) + * [[API] GET /repos/{owner}/{repo}/pulls/{index}/reviews/{id}/comments/{comment}](https://codeberg.org/forgejo/forgejo/pulls/2127) + * [[API] support for repository flags](https://codeberg.org/forgejo/forgejo/pulls/2097) + * [[I18N] Clarify description in deletion modal](https://codeberg.org/forgejo/forgejo/pulls/2488) + * [[I18N] Clarify the description of SSH Keys](https://codeberg.org/forgejo/forgejo/pulls/2393) + * [[I18N] Data size unit localization](https://codeberg.org/forgejo/forgejo/pulls/2528) + * [[I18N] Improve branch select list ui in go templates (gitea#29729)](https://codeberg.org/forgejo/forgejo/pulls/2744) + * [[I18N] Improve localization of repo summary](https://codeberg.org/forgejo/forgejo/pulls/2756) + * [[I18N] Improve registration / password reset emails](https://codeberg.org/forgejo/forgejo/pulls/2529) + * [[I18N] Use correct translations for pull request](https://codeberg.org/forgejo/forgejo/pulls/2260) + * [[I18N] Improve translatability of activity heatmap](https://codeberg.org/forgejo/forgejo/pulls/2612) + * [[I18N] Improve English locale for admin settings](https://codeberg.org/forgejo/forgejo/pulls/2583) + * [I18N] Add plural support: [1](https://codeberg.org/forgejo/forgejo/pulls/2614), [2](https://codeberg.org/forgejo/forgejo/pulls/2695), [3](https://codeberg.org/forgejo/forgejo/pulls/2954), [4](https://codeberg.org/forgejo/forgejo/pulls/3031) + * [I18N] General improvements to English locale: [1](https://codeberg.org/forgejo/forgejo/pulls/2307), [2](https://codeberg.org/forgejo/forgejo/pulls/2437), [3](https://codeberg.org/forgejo/forgejo/pulls/2492), [4](https://codeberg.org/forgejo/forgejo/pulls/2610), [5](https://codeberg.org/forgejo/forgejo/pulls/2703), [6](https://codeberg.org/forgejo/forgejo/pulls/2941). + * [[I18N] Allow custom repo size format](https://codeberg.org/forgejo/forgejo/pulls/2974) + * [[PACKAGES] nuget basic manifest download](https://codeberg.org/forgejo/forgejo/pulls/2222) + * [[UI] Add label filters in organization issues dashboard](https://codeberg.org/forgejo/forgejo/pulls/2944) + * [[UI] Allow users to hide all "Add more units..." hints](https://codeberg.org/forgejo/forgejo/pulls/2533) + * [[UI] Display tag name as title for a tag with no release [gitea]](https://codeberg.org/forgejo/forgejo/pulls/2547) + * [[UI] Enable ambiguous character detection in configured contexts](https://codeberg.org/forgejo/forgejo/pulls/2427) + * [[UI] Improve display of 404/500 error pages](https://codeberg.org/forgejo/forgejo/pulls/2466) + * [[UI] Improve look of user profiles](https://codeberg.org/forgejo/forgejo/pulls/2875) + * [[UI] Include a branch link in the recently pushed banner](https://codeberg.org/forgejo/forgejo/pulls/2141) + * [[UI] Offer to remove WIP: prefix in sidebar](https://codeberg.org/forgejo/forgejo/pulls/2660) + * [[UI] Port console colors](https://codeberg.org/forgejo/forgejo/pulls/2419) + * [[UI] pulls "Edit File" button in "Files Changed" tab](https://codeberg.org/forgejo/forgejo/pulls/1992) + * [[UI] Remove add organization on dashboard switcher](https://codeberg.org/forgejo/forgejo/pulls/2895) + * [[UI] Restrict file size of blame operation](https://codeberg.org/forgejo/forgejo/pulls/2395) + * [[UI] Show follow symlink button](https://codeberg.org/forgejo/forgejo/pulls/2530) + * [[UI] split code conversations in diff tab](https://codeberg.org/forgejo/forgejo/pulls/2306) + * [[UI] Update look of repo/org tabs on homepage](https://codeberg.org/forgejo/forgejo/pulls/2593) + * [[UI] Visual separation between types of attachments](https://codeberg.org/forgejo/forgejo/pulls/2899) + * [[UI] [AGIT] Add AGit label to AGit-created PRs](https://codeberg.org/forgejo/forgejo/pulls/2444) + * [[UI] [AGIT] Add link to docs and tooltip to label](https://codeberg.org/forgejo/forgejo/pulls/2499) + * [Implement commit mail selection for other Git operations](https://codeberg.org/forgejo/forgejo/pulls/2383) + * [Improved Linguist compatibility](https://codeberg.org/forgejo/forgejo/pulls/2088) + * [improve nuget nuspec api](https://codeberg.org/forgejo/forgejo/pulls/2996) + * [Log SQL queries when the database return error](https://codeberg.org/forgejo/forgejo/pulls/2140) + * [New doctor check: fix-push-mirrors-without-git-remote](https://codeberg.org/forgejo/forgejo/pulls/1853) + * [New route to view latest run of specific workflows](https://codeberg.org/forgejo/forgejo/pulls/2304) + * [Check for Commit in opengraph](https://codeberg.org/forgejo/forgejo/pulls/2094) + * [Check if commit is already present in target branch](https://codeberg.org/forgejo/forgejo/pulls/2450) + * [Configure if protected branch rule should apply to admins](https://codeberg.org/forgejo/forgejo/pulls/2867) + * [Count downloads for tag archives](https://codeberg.org/forgejo/forgejo/pulls/2976) + * [depguard sha256-simd](https://codeberg.org/forgejo/forgejo/pulls/2234) + * [Don't consider orphan branches as recently pushed](https://codeberg.org/forgejo/forgejo/pulls/2196) + * [extend webfinger to respond to profile page URIs](https://codeberg.org/forgejo/forgejo/pulls/2883) + * [Highlight signed tags like signed commits](https://codeberg.org/forgejo/forgejo/pulls/2534) + * [Allow changing the email address before activation](https://codeberg.org/forgejo/forgejo/pulls/1891) + * [Allow changing the repo Wiki branch to main](https://codeberg.org/forgejo/forgejo/pulls/2264) + * [Allow forking without a repo ID](https://codeberg.org/forgejo/forgejo/pulls/2310) + * [Allow instance-wide disabling of forking](https://codeberg.org/forgejo/forgejo/pulls/2445) + * [Allow non-explicit push options](https://codeberg.org/forgejo/forgejo/pulls/2984) + * [Allow to exclude files in dump](https://codeberg.org/forgejo/forgejo/pulls/2876) + * [add bucket lookup type](https://codeberg.org/forgejo/forgejo/pulls/2482) + * [Add download URL for executable files](https://codeberg.org/forgejo/forgejo/pulls/1839) + * [Add gitignore template for Janet projects](https://codeberg.org/forgejo/forgejo/pulls/2557) + * [add optional storage init to doctor commands](https://codeberg.org/forgejo/forgejo/pulls/3034) + * [Add rel="nofollow" to issue filter links](https://codeberg.org/forgejo/forgejo/pulls/2367) + * [Add support for shields.io-based badges](https://codeberg.org/forgejo/forgejo/pulls/2070) + * [Add Zig gitignore](https://codeberg.org/forgejo/forgejo/pulls/2352) + * [Recognize SSH signed tags too](https://codeberg.org/forgejo/forgejo/pulls/2520) + * [Repository flags](https://codeberg.org/forgejo/forgejo/pulls/2079) + * [support `.forgejo` dir for issue and PR templates](https://codeberg.org/forgejo/forgejo/pulls/2290) + * [Support Include/Exclude Filters for Grep](https://codeberg.org/forgejo/forgejo/pulls/3058) + * [Use 'Text' instead of 'Plaintext'](https://codeberg.org/forgejo/forgejo/pulls/2833) + * [Render code tags in commit messages](https://codeberg.org/forgejo/forgejo/commit/3ccb0c2512cb551943945aaa3f2bd0b1e2abd3b8). + * [Refactor markdown attention render](https://codeberg.org/forgejo/forgejo/commit/ec2201a3da5f18e55bfc0a54114ac935804f4ef8). + * [Add default board to new projects, remove uncategorized pseudo-board](https://codeberg.org/forgejo/forgejo/commit/8ffb9c6fb1571a1221978440f108911057df25db). + * [Add more stats tables](https://codeberg.org/forgejo/forgejo/commit/926367fe1d778fe7c9f5bc6b8e8c514b619ef038). + * [Improve branch select list ui in go templates](https://codeberg.org/forgejo/forgejo/commit/729849a2fd026adbb91e3ff3259290f61bd919f0). + * [Update allowed attachment types](https://codeberg.org/forgejo/forgejo/commit/04b79bb48b490644c46e58da46af4b62a40e5e03). + * [Completely style the webkit autofill](https://codeberg.org/forgejo/forgejo/commit/9916f3ed64a715fb9a31a0fcad6452276e275615). + * [Set user's 24h preference from their current OS locale](https://codeberg.org/forgejo/forgejo/commit/427ab550a6a35e7369bc1b33a188bb3030c32ec0). + * [Make wiki default branch name changeable](https://codeberg.org/forgejo/forgejo/commit/7ea8993a0e342e7a30cb2da03216697b4819935a). + * [Make admin pages wider because of left sidebar added and some tables become too narrow](https://codeberg.org/forgejo/forgejo/commit/145bebc829c03cbb078e518d7364d27bcf60d96c). + * [Make PR form use toast to show error message](https://codeberg.org/forgejo/forgejo/commit/221a28436a080447f429fa2089d264e56f4980e2). + * [Rename Action.GetDisplayName to GetActDisplayName](https://codeberg.org/forgejo/forgejo/commit/be9189eddc84e942710b16b1c8c54c10aad01b63). + * [Unify search boxes](https://codeberg.org/forgejo/forgejo/commit/847f03b6a65ee251bf764f54f6114737346a43b6). + * [Detect broken git hooks](https://codeberg.org/forgejo/forgejo/commit/963df8290784d82385f7e8ad9f5c9abfd2fa2860). + * [Filter for default-branch selection](https://codeberg.org/forgejo/forgejo/commit/1090734255d70deb9886de2c1a8bb971096223ee). + * [Include resource state events in Gitlab downloads](https://codeberg.org/forgejo/forgejo/commit/bc7a247b9ea68643e3c59d4b4376dea097ffcc68). + * [Properly migrate target branch change GitLab comment](https://codeberg.org/forgejo/forgejo/commit/f0acc71ba13713f07602294b4a33028125343d47). + * [Recolor dark theme to blue shade](https://codeberg.org/forgejo/forgejo/commit/ff581d5a2415f7a3321fa6ba656ae0e972674d6c). + * [Unify organizations header](https://codeberg.org/forgejo/forgejo/commit/4b494d341f3142c066bc5b2b3cfd50f924d64fd3). + * [Auto-update the system status in admin dashboard](https://codeberg.org/forgejo/forgejo/commit/4f050f358a15dd51903e01b330a5419b2ac06693). + * [Show more settings for empty repositories](https://codeberg.org/forgejo/forgejo/commit/b03af9efb275f935bb265c7f031225caaafefaff). + * [Downscale pasted PNG images based on metadata](https://codeberg.org/forgejo/forgejo/commit/b3f2447bc4b6a7220da748cc6eb24bd5568bee7c). + * [Show `View at this point in history` for every commit](https://codeberg.org/forgejo/forgejo/commit/27bc2b9d9597de89d2c6b68581c6729bb16a4572). + * [Drop "@" from email sender to avoid spam filters](https://codeberg.org/forgejo/forgejo/commit/9a1d5c549cb6d32219647ea1a771b8a82d5ac89f). + * [Allow non-admin users to delete review requests](https://codeberg.org/forgejo/forgejo/commit/77c56e29ded5665bdc09d0a568159aa7127b44b1). + * [Some performance optimization on dashboard and issues page](https://codeberg.org/forgejo/forgejo/commit/d996c5d5179c99855e69156a034eca055e9329a4). + * [Improve user search display name](https://codeberg.org/forgejo/forgejo/commit/c3e462921ee31536e59b37e654ed20e92a37ffe6). + * [Fix UI Spacing Errors in mirror settings](https://codeberg.org/forgejo/forgejo/commit/64faecefe10613840709a68c1b8b708115d69d6e). + * [Include username in email headers](https://codeberg.org/forgejo/forgejo/commit/360b3fd17c3315ad9ad9c4e6ac02eda73f48d8ae). + * [Also match weakly validated ETags](https://codeberg.org/forgejo/forgejo/commit/28fe3db1fb0f89bcb55829ced33c1282f85f6e97). + * [Propagate install_if and provider_priority to APKINDEX](https://codeberg.org/forgejo/forgejo/commit/2da233ad8be107de29190720f1c30199410fe0cd). + * [Fix display latest sync time for pull mirrors on the repo page](https://codeberg.org/forgejo/forgejo/commit/4674aea25b54baf08594c54f061dee9e44190f02). + * [Remove trust model selection from repository creation on web page because it can be changed in settings later](https://codeberg.org/forgejo/forgejo/commit/c08d263a1900aa5ee92f56af8ad1c7a2697d02e1). + * [Add ability to see open and closed issues at the same time](https://codeberg.org/forgejo/forgejo/commit/2c3da59e275b69ebf984bb70954f42a7bcb0b49d). + * [Move sign in labels to be above inputs](https://codeberg.org/forgejo/forgejo/commit/4af0944b2604dd2b2e413864492135faea097298). + * [Move the captcha script loader to the template which really needs it](https://codeberg.org/forgejo/forgejo/commit/a04f8c0f81f55a8b927ce0fad8127db39396f892). + * [Display latest sync time for pull mirrors on the repo page](https://codeberg.org/forgejo/forgejo/commit/2d343f8987025015f5b61e328cc9e45082e6d3f2). + * [Show in Web UI if file is vendored and generated](https://codeberg.org/forgejo/forgejo/commit/7ed18566e10b298309dcc99d97447cb1932ae09a). + * [Add orphaned topic consistency check](https://codeberg.org/forgejo/forgejo/commit/e02095c5b6e04f70ae6562f5aee169f7ee93cf7a). + * [Convert to url auth to header auth in tests](https://codeberg.org/forgejo/forgejo/commit/838db2f8911690fa2115c6827ed73687db71bef1). + * [Add option to set language in admin user view](https://codeberg.org/forgejo/forgejo/commit/318634ef74dc0a9c285991692e72d3df90b8583c). + * [Fix incorrect run order of action jobs](https://codeberg.org/forgejo/forgejo/commit/f4561c44b1cad700bf41537eb4db487fff34f6c9). + * [Add missing exclusive in advanced label options](https://codeberg.org/forgejo/forgejo/commit/77506c6f6cbfa5c15d8373743415f47b2adb404d). + * [Add combined index for issue_user.uid and issue_id](https://codeberg.org/forgejo/forgejo/commit/e08f1a9cbd582c73918e401eeba36261627f44a7). + * [Add edit option for README.md](https://codeberg.org/forgejo/forgejo/commit/08552f0076204b99258f9135c77a962c302521dc). + * [Fix link to `Code` tab on wiki commits](https://codeberg.org/forgejo/forgejo/commit/709a376c518d0cfde10bb911b32fd0ea82c67b52). + * [Remove autofocus in search box](https://codeberg.org/forgejo/forgejo/commit/eae555ff2395cc1ad178f3a977d83742ae73e1d9). + * [Allow to set explore page default sort](https://codeberg.org/forgejo/forgejo/commit/16ba16dbe951763cfc026b7351e26009d1a25fdc). + * [Improve PR diff view on mobile](https://codeberg.org/forgejo/forgejo/commit/49dddd87b19aebe83e1c54a455e62529a19f61b4). + * [Properly migrate automatic merge GitLab comments](https://codeberg.org/forgejo/forgejo/commit/542badbb76408c17ce6692e99fff680bee69face). + * [Display issue task list on project cards](https://codeberg.org/forgejo/forgejo/commit/4776fde9e1caa7cee5671715144a668e19a0323c). + * [Add Index to pull_auto_merge.doer_id](https://codeberg.org/forgejo/forgejo/commit/c8602a8dfa05f653e7de8ed2e677c8967b8688f5). + * [Fix display member unit in the menu bar if there are no hidden members in public org](https://codeberg.org/forgejo/forgejo/commit/0e021cd33ee3eb3d8f204bd075e2597b7ec8b391). + * [List all Debian package versions in `Packages`](https://codeberg.org/forgejo/forgejo/commit/b36e2ca4195298d2e4516e3022b953543f62f470). + * [Allow pull requests Manually Merged option to be used by non-admins](https://codeberg.org/forgejo/forgejo/commit/1756e30e102d079f8425aa2061ef80fd36c2e57d). + * [Only show diff file tree when more than one file changed](https://codeberg.org/forgejo/forgejo/commit/572f0963edc71239634ee782a3c69213479f34ba). + * [Show placeholder email in privacy popup](https://codeberg.org/forgejo/forgejo/commit/31f8880bc252a25075f8752e2722b316c6e46ec7). + * [Revamp repo header](https://codeberg.org/forgejo/forgejo/commit/7d62615513b8985360de497e9a051b51ca0faaf2). + * [Add link to members and repositories at teams page](https://codeberg.org/forgejo/forgejo/commit/4f4ddcf3c593b474846d40e47b4351d3deb39202). + * [Add link for repositories README file](https://codeberg.org/forgejo/forgejo/commit/7210f23fa0f11da093b307029d7ab91ed40807fb). + * [Add `must-change-password` cli parameter](https://codeberg.org/forgejo/forgejo/commit/9bea276055edc9527e3d6d66df3bbf0d20326f8b). + * [Unify password changing and invalidate auth tokens](https://codeberg.org/forgejo/forgejo/commit/688d4a1f719d2df4d2626453f4bc042c1874a375). + * [Add slow SQL query warning](https://codeberg.org/forgejo/forgejo/commit/664192767c41b9d0759bcc3915c7bd6ccecc52ae). + * [Pre-register OAuth application for tea](https://codeberg.org/forgejo/forgejo/commit/a825cc0f3423f0a5c8157c436a0c7b489ef536c1). + * [Differentiate between `push` and `pull` `mirror sync in progress`](https://codeberg.org/forgejo/forgejo/commit/e709bc199fe33456c4ecd1cd28029bd31b529832). + * [Cargo package - Fix missing domain in cargo sparse url](https://codeberg.org/forgejo/forgejo/commit/a112cf34d391cc04770021f9ffaa29e383cb9d51). + * [Link to file from its history](https://codeberg.org/forgejo/forgejo/commit/33de64cb21505259338e393ef0d15ccb0f757475). + * [Add a shortcut to user's profile page to admin user details](https://codeberg.org/forgejo/forgejo/commit/e96e440b8bde5516ffc7bba42691e26084a96588). + * [Doctor: delete action entries without existing user](https://codeberg.org/forgejo/forgejo/commit/15fa0383fb5dd9ad1702dbc34ba7100c0cdbcc8c). + * [Add anchor to review types](https://codeberg.org/forgejo/forgejo/commit/89c9a498fdd6184df8afda8b5b488462e65b9e71). + * [Show total TrackedTime on issue/pull/milestone lists](https://codeberg.org/forgejo/forgejo/commit/adbc995c347e158a56264f2488997d7d59a4dd8b). + * [Improve commit record's ui in comment list](https://codeberg.org/forgejo/forgejo/commit/ed1798f66d30e3755f01e24f8cb4aa5e8b6628a0). + * [Don't show new pr button when page is not compare pull](https://codeberg.org/forgejo/forgejo/commit/b693611b35c5ae17cfc820bc3e731608a5251464). + * [Add `Hide/Show all checks` button to commit status check](https://codeberg.org/forgejo/forgejo/commit/dcb648ee71853073d54e8a6e107b764212ede58e). + * [Improvements of releases list and tags list](https://codeberg.org/forgejo/forgejo/commit/3fcad582c9b9bfe66f4a346652f82b1aaf18430d). + * [Support pasting URLs over markdown text](https://codeberg.org/forgejo/forgejo/commit/45112876766cb81ed7edd2b72a3ab93e6deab8bb). + * [Customizable "Open with" applications for repository clone](https://codeberg.org/forgejo/forgejo/commit/44221a3cd747a01d55093b15a12bf053b534da35). + * [Allow options to disable user deletion from the interface on app.ini](https://codeberg.org/forgejo/forgejo/commit/767e9634d3d02acab27f05e1783391c9c7f6292e). + * [Extend issue template yaml engine](https://codeberg.org/forgejo/forgejo/commit/ff8f7a7a0d1d0f57113a6ad8b499f7c1094288f5). + * [Filter Repositories by type](https://codeberg.org/forgejo/forgejo/commit/83e04328dfff3b09e5d28dd972ebee0865f96b0e). + * [Implement code frequency graph](https://codeberg.org/forgejo/forgejo/commit/f097799953c5f510b7e3314f1e3e115761f207d0). + * [Implement recent commits graph](https://codeberg.org/forgejo/forgejo/commit/428008ac19185125b7cb1e3d379254d7b1932529). + * [Show commit status for releases](https://codeberg.org/forgejo/forgejo/commit/369fe5696697cef33a188d9b985ac4b9824a4bdf). + * [Actions Artifacts v4 backend](https://codeberg.org/forgejo/forgejo/commit/66632c4958041abdffe6adafc278d34ef515c44f). + * [Add merge style `fast-forward-only`](https://codeberg.org/forgejo/forgejo/commit/83123b493f3ae25d07d81c86b1a78afe1c17db53). + * [Retarget depending pulls when the parent branch is deleted](https://codeberg.org/forgejo/forgejo/commit/49eb16867728913d1eb2ced96e0b0b0a358f6ebe). + * [Add global setting how timestamps should be rendered](https://codeberg.org/forgejo/forgejo/commit/cdc33b29a012e61b42f192d79f9486fa8e21b2ed). + * [Add skip ci functionality](https://codeberg.org/forgejo/forgejo/commit/816e46ee7ce4b2649479554a940ecbe1cc505a3d) + * [Show latest commit for file](https://codeberg.org/forgejo/forgejo/commit/885cc32b14584ee2d01009768895b7a776441504). + * [Allow to sync tags from admin dashboard](https://codeberg.org/forgejo/forgejo/commit/4567a3a1ad0490d9077102e0e7b5de35790e5803). + * [Add Profile Readme for Organisations](https://codeberg.org/forgejo/forgejo/commit/603573366a203efae06f818a0b220be964cdac21). + * [Implement contributors graph](https://codeberg.org/forgejo/forgejo/commit/e9be8b25ae57189c4b29eaa393a397cf634d21d7). + * [Artifact deletion in actions ui](https://codeberg.org/forgejo/forgejo/commit/c551d3f3ab13379b0740fc45bc4dfc8f2fb84e16). + * [Add API routes to get runner registration token](https://codeberg.org/forgejo/forgejo/commit/baf0d402d9cb47849394202fcfc7c2e23b0faac3). + * [Add support for forking single branch](https://codeberg.org/forgejo/forgejo/commit/5e02e3b7ee8294e2ec94968ece9af56bf1aa1534). + * [Add support for sha256 repositories](https://codeberg.org/forgejo/forgejo/commit/d68a613ba8fd860863a3465b5b5945b191b87b25). + * [Add admin API route for managing user's badges](https://codeberg.org/forgejo/forgejo/commit/82b7de1360870db7a8b368a3f80ede887e32e128). +* **Bug fixes:** + * The repository home view will no longer redirect to external units. ([#2064](https://codeberg.org/forgejo/forgejo/pulls/2064)) + * User and Organization `.profile` repositories now search for a `README.md` file case insensitively. ([#2090](https://codeberg.org/forgejo/forgejo/pulls/2090)) + * When viewing a file, the RSS feed link is only displayed when there is an RSS feed provided for the context: when viewing a file on a branch. ([#2103](https://codeberg.org/forgejo/forgejo/pulls/2103)) + * Repository topic searches are now correctly paged, which should make topic management on larger instances orders of magnitudes faster. ([#2060](https://codeberg.org/forgejo/forgejo/pulls/2060)) + * Mentioning a user in a comment or similar place ignores apostrophes now. ([#2485](https://codeberg.org/forgejo/forgejo/pulls/2485)) + * Setting the `[repository].DISABLE_STARS` setting to `true` disables the functionality completely, rather than just hiding it from the user interface. + * Forking a repository is now available at a predictable URL, and does not require knowing the repository id. ([#2310](https://codeberg.org/forgejo/forgejo/pulls/2310)) + * Issue and pull request templates can now be placed in a `.forgejo` directory, like workflows. ([#2290](https://codeberg.org/forgejo/forgejo/pulls/2290)) + * [[A11Y] Fix accessibility and translatability of repo explore counters](https://codeberg.org/forgejo/forgejo/pulls/2862) + * [[A11Y] Focus styling and fix Watch/Unwatch buttons](https://codeberg.org/forgejo/forgejo/pulls/2379) + * [[A11Y] Label Stars/Forks links in repo explore](https://codeberg.org/forgejo/forgejo/pulls/2634) + * [[A11Y] Taborder in repo explore](https://codeberg.org/forgejo/forgejo/pulls/2636) + * [[ACTIONS] add proper payload to scheduled events](https://codeberg.org/forgejo/forgejo/pulls/2015) + * [[ACTIONS] Do not update PRs based on events that happened before they existed](https://codeberg.org/forgejo/forgejo/pulls/2932) + * [[ACTIONS] GetScheduledMergeByPullID may involve a system user](https://codeberg.org/forgejo/forgejo/pulls/1908) + * [[ACTIONS] Link to Workflow in View](https://codeberg.org/forgejo/forgejo/pulls/1866) + * [[ACTIONS] the ref of a scheduled action is always the default branch](https://codeberg.org/forgejo/forgejo/pulls/1941) + * [[API] Adjust name of operation](https://codeberg.org/forgejo/forgejo/pulls/2189) + * [[API] `/api/v1/{owner}/{repo}/issue_templates`](https://codeberg.org/forgejo/forgejo/pulls/2292) + * [[API] Document correct status code for creating a tag](https://codeberg.org/forgejo/forgejo/pulls/2201) + * [[API] /api/forgejo/v1/version auth check](https://codeberg.org/forgejo/forgejo/pulls/2582) + * [[API] inconsistencies](https://codeberg.org/forgejo/forgejo/pulls/2182) + * [[API] /issues/search endpoint](https://codeberg.org/forgejo/forgejo/pulls/2020) + * [[API] Make HTTPS schema default for Swagger](https://codeberg.org/forgejo/forgejo/pulls/1896) + * [[I18N] Add missing translation for more_items](https://codeberg.org/forgejo/forgejo/pulls/2828) + * [[I18N] Eliminate wrapping quotes in English locale](https://codeberg.org/forgejo/forgejo/pulls/2467) + * [[I18N] English fixes and improvements](https://codeberg.org/forgejo/forgejo/pulls/2631) + * [[I18N] Fix milestone sorting translation keys](https://codeberg.org/forgejo/forgejo/pulls/2644) + * [[I18N] Use correct translation on closed milestones](https://codeberg.org/forgejo/forgejo/pulls/2957) + * [[I18N] Use new translation key](https://codeberg.org/forgejo/forgejo/pulls/2760) + * [[PACKAGES] Delete redundant snap packaging recipe](https://codeberg.org/forgejo/forgejo/pulls/2693) + * [[PACKAGES] Fix Alpine Registry packages with noarch not being found](https://codeberg.org/forgejo/forgejo/pulls/2285) + * [[PACKAGES] Generate install if condition for Alpine](https://codeberg.org/forgejo/forgejo/pulls/2176) + * [[PACKAGES] Packagist webhook: support all events](https://codeberg.org/forgejo/forgejo/pulls/2646) + * [[PACKAGES] Fix for PyPi Registry PEP 503 Compliance](https://codeberg.org/forgejo/forgejo/pulls/3197) + * [[UI] Adjust the signed tag verification line](https://codeberg.org/forgejo/forgejo/pulls/2966) + * [[UI] Better color for labels/counters](https://codeberg.org/forgejo/forgejo/pulls/2935) + * [[UI] Better number for UserCards pagination](https://codeberg.org/forgejo/forgejo/pulls/2584) + * [[UI] Center icon and callout text](https://codeberg.org/forgejo/forgejo/pulls/3010) + * [[UI] Consistent styling for Sort filter](https://codeberg.org/forgejo/forgejo/pulls/2920) + * [[UI] Disable the RSS feed in file view for non-branches](https://codeberg.org/forgejo/forgejo/pulls/2103) + * [[UI] Disable 'View at this point in history' for wikis](https://codeberg.org/forgejo/forgejo/pulls/2999) + * [[UI] Display error message if doer is unable to fork](https://codeberg.org/forgejo/forgejo/pulls/2649) + * [[UI] Don't use `
` in alert block](https://codeberg.org/forgejo/forgejo/pulls/2741) + * [[UI] Fix admin layout](https://codeberg.org/forgejo/forgejo/pulls/3087) + * [[UI] Fix crash in issue forms](https://codeberg.org/forgejo/forgejo/pulls/3012) + * [[UI] Fix Ctrl+Enter on submitting review comment](https://codeberg.org/forgejo/forgejo/pulls/2370) + * [[UI] Fix diff patch operation in web UI](https://codeberg.org/forgejo/forgejo/pulls/2449) + * [[UI] Fixes for project selector in sidebar](https://codeberg.org/forgejo/forgejo/pulls/2608) + * [[UI] Fix must-change-password help dialog](https://codeberg.org/forgejo/forgejo/pulls/2676) + * [[UI] Fix relative links on orgmode](https://codeberg.org/forgejo/forgejo/pulls/2385) + * [[UI] Fix selector inner radius](https://codeberg.org/forgejo/forgejo/pulls/2860) + * [[UI] Fix tone of callout boxes for Forgejo dark](https://codeberg.org/forgejo/forgejo/pulls/3085) + * [[UI] Fix tooltip for 1000+ stars/forks](https://codeberg.org/forgejo/forgejo/pulls/3147) + * [[UI] include hostname in admin panel URL in new user emails](https://codeberg.org/forgejo/forgejo/pulls/1940) + * [[UI] Increase contrast of code block](https://codeberg.org/forgejo/forgejo/pulls/2874) + * [[UI] Limit amount of javascript errors being shown](https://codeberg.org/forgejo/forgejo/pulls/2175) + * [[UI] Make settings tab not active when on repository "Add units" tab](https://codeberg.org/forgejo/forgejo/pulls/2524) + * [[UI] Make write and preview tabs interactive](https://codeberg.org/forgejo/forgejo/pulls/2681) + * [[UI] New issue button position consistency](https://codeberg.org/forgejo/forgejo/pulls/2845) + * [[UI] Fix orgmode link resolver for text descriptions](https://codeberg.org/forgejo/forgejo/pulls/2276) + * [[UI] Preview: set font-size on preview content](https://codeberg.org/forgejo/forgejo/pulls/2349) + * [[UI] Fix primary button background inconsistency](https://codeberg.org/forgejo/forgejo/pulls/3002) + * [[UI] Fix regression of issue edit not working](https://codeberg.org/forgejo/forgejo/pulls/3043) + * [[UI] Fix relative links rendering](https://codeberg.org/forgejo/forgejo/pulls/2166) + * [[UI] Remember topic only in repo search](https://codeberg.org/forgejo/forgejo/pulls/2575) + * [[UI] Remove min-height from wiki elements](https://codeberg.org/forgejo/forgejo/pulls/2080) + * [[UI] Render emojis in labels in issue info popup](https://codeberg.org/forgejo/forgejo/pulls/2888) + * [[UI] Render correct label link](https://codeberg.org/forgejo/forgejo/pulls/3187) + * [[UI] Render inline file permalinks](https://codeberg.org/forgejo/forgejo/pulls/2669) + * [[UI] Fix repo badges when the label or text contains dashes](https://codeberg.org/forgejo/forgejo/pulls/2711) + * [[UI] Fix repo unarchivation button](https://codeberg.org/forgejo/forgejo/pulls/2550) + * [[UI] Restrict when to make link absolute in markdown](https://codeberg.org/forgejo/forgejo/pulls/2403) + * [[UI] Revert darker tone on labels](https://codeberg.org/forgejo/forgejo/pulls/2881) + * [[UI] Simplify converting struct to map in admin stats](https://codeberg.org/forgejo/forgejo/pulls/2442) + * [[UI] Fix the Fork button in repo headers](https://codeberg.org/forgejo/forgejo/pulls/2495) + * [[UI] Use correct logout URL](https://codeberg.org/forgejo/forgejo/pulls/2475) + * [[UI] Use separate keys for tabs on login screen](https://codeberg.org/forgejo/forgejo/pulls/2630) + * [[UI] "view file" button in diff compare view](https://codeberg.org/forgejo/forgejo/pulls/3046) + * [add Cache-Control header for health-check](https://codeberg.org/forgejo/forgejo/pulls/3060) + * [add max idle time setting for db connections](https://codeberg.org/forgejo/forgejo/pulls/2418) + * [Allow `'s` in mentions](https://codeberg.org/forgejo/forgejo/pulls/2485) + * [Avoid `WHERE IN` for comment migration query](https://codeberg.org/forgejo/forgejo/pulls/1961) + * [Cleanup characters forbidden on Windows from test fixture filenames](https://codeberg.org/forgejo/forgejo/pulls/2178) + * [Correct changed files for CODEOWNERS](https://codeberg.org/forgejo/forgejo/pulls/2507) + * [Correct default licenses to work as desired](https://codeberg.org/forgejo/forgejo/pulls/1888) + * [Detect protected branch on branch rename](https://codeberg.org/forgejo/forgejo/pulls/2811) + * [Disabling Stars should disable the routes too](https://codeberg.org/forgejo/forgejo/pulls/2471) + * [doctor: Don't say All done when no checks were run](https://codeberg.org/forgejo/forgejo/pulls/1907) + * [Do not allow deletion of internal references](https://codeberg.org/forgejo/forgejo/pulls/2834) + * [Don't color dot literal color names](https://codeberg.org/forgejo/forgejo/pulls/2905) + * [Don't delete inactive emails explicitly](https://codeberg.org/forgejo/forgejo/pulls/2880) + * [Don't overwrite protected branch accidentally](https://codeberg.org/forgejo/forgejo/pulls/2473) + * [Don't redirect the repo to external units](https://codeberg.org/forgejo/forgejo/pulls/2064) + * [Don't remove builtin OAuth2 applications](https://codeberg.org/forgejo/forgejo/pulls/3067) + * [Ensure `HasIssueContentHistory` takes into account `comment_id`](https://codeberg.org/forgejo/forgejo/pulls/2518) + * [Find README.md for user profiles case insensitively](https://codeberg.org/forgejo/forgejo/pulls/2090) + * [Fix header name in swagger response](https://codeberg.org/forgejo/forgejo/pulls/2526) + * [Fix pull request reopen conditions](https://codeberg.org/forgejo/forgejo/pulls/2373) + * [Fix unblock action](https://codeberg.org/forgejo/forgejo/pulls/3086) + * [Fix VSCode settings](https://codeberg.org/forgejo/forgejo/pulls/1881) + * [Gracefully handle missing branches on a repos branches page](https://codeberg.org/forgejo/forgejo/pulls/2139) + * [Initialize Git for hook regeneration](https://codeberg.org/forgejo/forgejo/pulls/2416) + * [Internal Server Error when resolving comments](https://codeberg.org/forgejo/forgejo/pulls/2282) + * [Load `AllUnitsEnabled` when necessary](https://codeberg.org/forgejo/forgejo/pulls/2420) + * [Makefile: check git diff exitCode](https://codeberg.org/forgejo/forgejo/pulls/2651) + * [Make pprof labels conformant with prometheus spec](https://codeberg.org/forgejo/forgejo/pulls/2933) + * [Make reference URL absolute](https://codeberg.org/forgejo/forgejo/pulls/2100) + * [misleading comparisons when comparing branches](https://codeberg.org/forgejo/forgejo/pulls/2194) + * [Block issue creation when blocked by repo owner](https://codeberg.org/forgejo/forgejo/pulls/2052) + * [NPE in `ToPullReviewList`](https://codeberg.org/forgejo/forgejo/pulls/2057) + * [NPE in `UsernameSubRoute`](https://codeberg.org/forgejo/forgejo/pulls/1981) + * [Only pass selected repository IDs to pagination](https://codeberg.org/forgejo/forgejo/pulls/1848) + * [panic in `canSoftDeleteContentHistory`](https://codeberg.org/forgejo/forgejo/pulls/2134) + * [prevent removing session cookie when redirect_uri query contains ://](https://codeberg.org/forgejo/forgejo/pulls/2590) + * [pull_request_template branch link](https://codeberg.org/forgejo/forgejo/pulls/2232) + * [Rate limit pre-activation email change separately](https://codeberg.org/forgejo/forgejo/pulls/2043) + * [Refactor LFS GC functions](https://codeberg.org/forgejo/forgejo/pulls/3056) + * [Reflect Cargo index state in settings](https://codeberg.org/forgejo/forgejo/pulls/2698) + * [Remember topic only in repo search](https://codeberg.org/forgejo/forgejo/pulls/2489) + * [Require Latex code to have a end sequence](https://codeberg.org/forgejo/forgejo/pulls/1822) + * [Respond with JSON Resource Descriptor Content-Type per RFC7033](https://codeberg.org/forgejo/forgejo/pulls/2882) + * [Fix session generation for database](https://codeberg.org/forgejo/forgejo/pulls/2045) + * [Sort file list case insensitively](https://codeberg.org/forgejo/forgejo/pulls/2522) + * [Fix the topic search paging](https://codeberg.org/forgejo/forgejo/pulls/2060) + * [Typo fix & clarify RegistrationToken](https://codeberg.org/forgejo/forgejo/pulls/2191) + * [Update checker setting updates](https://codeberg.org/forgejo/forgejo/pulls/2925) + * [Use correct format for attr-check error log](https://codeberg.org/forgejo/forgejo/pulls/2866) + * [Use correct head commit for CODEOWNER](https://codeberg.org/forgejo/forgejo/pulls/2658) + * [Use correct template for commitmail error](https://codeberg.org/forgejo/forgejo/pulls/2973) + * [Workaround borked Git version](https://codeberg.org/forgejo/forgejo/pulls/2335) + * [Remove scheduled action tasks if the repo is archived](https://codeberg.org/forgejo/forgejo/commit/87870ade49eb76ff57a8593ba35df10e0d617aa5). + * [Relax generic package filename restrictions](https://codeberg.org/forgejo/forgejo/commit/ea4755be6dfc8fc1f3c794eeaa2e2322b97d192e). + * [Prevent re-review and dismiss review actions on closed and merged PRs](https://codeberg.org/forgejo/forgejo/commit/23676bfea7ccbbe166a554115ea1f5f02800e379). + * [Add a warning for disallowed email domains](https://codeberg.org/forgejo/forgejo/commit/2559c80bec27a41967b355d214253a83b9ee5dad). + * [Skip email domain check when admins edit user emails](https://codeberg.org/forgejo/forgejo/commit/e7afba21ce2b02eb4230ba03752bd8b937f3e6ef). + * [Skip email domain check when admin users adds user manually](https://codeberg.org/forgejo/forgejo/commit/b6057a34db38e563473db00543a1e39fd743ca34). + * [Add support for API blob upload of release attachments](https://codeberg.org/forgejo/forgejo/commit/47a913d40d3417858f2ee51a7dbed64ca84eff60). + * [Allow options to disable user gpg keys configuration from the interface on app.ini](https://codeberg.org/forgejo/forgejo/commit/ee6ff937c0782b9cdc7ae1bc62b7eda83982d40f). + * [Allow options to disable user ssh keys configuration from the interface on app.ini](https://codeberg.org/forgejo/forgejo/commit/bb09ad2b63570c80418b4b9a10f7dbbb349448ab). + * [Fix content size does not match error when uploading lfs file](https://codeberg.org/forgejo/forgejo/commit/fb137d1e49c0436f1db093e2dc0a2350d63e1e29). + * [Add API to get merged PR of a commit](https://codeberg.org/forgejo/forgejo/commit/1608ef0ce9ce2ea1c87aef715d111cf441637d01). + * [Add API to get PR by base/head](https://codeberg.org/forgejo/forgejo/commit/feb189554e758ed27d1e309e5ec309d663e8f338). + * [Add attachment support for code review comments](https://codeberg.org/forgejo/forgejo/commit/f95fb8cc44d790e0ae71d3f879124a6ee9b07f66). + * [Add support for action artifact serve direct](https://codeberg.org/forgejo/forgejo/commit/1f8ad34e4391673a2eda434ea5e48ea084cdc814). + * [Show whether a PR is WIP inside popups](https://codeberg.org/forgejo/forgejo/commit/50f55f11c4f785b72a39e59b0fc12ae70ab8d8b5). + * [Add artifacts v4 jwt to job message and accept it](https://codeberg.org/forgejo/forgejo/commit/a9bc590d5d10b97bd8aa050ffb720e141a600064). + * [Fix some RPM registry flaws](https://codeberg.org/forgejo/forgejo/commit/461d8b53c2e51a8a6a1715ba40ac61d7e9f93971). + * [Add branch protection setting for ignoring stale approvals](https://codeberg.org/forgejo/forgejo/commit/5d3fdd121279c758f247a76e020799aa5e548feb). + * [Added instance-level variables](https://codeberg.org/forgejo/forgejo/commit/d0f24ff4cad05c1145afeca791e7d02fe146d46a). + * [Fix the wrong HTTP response status code for duplicate packages](https://codeberg.org/forgejo/forgejo/commit/5b6258a0b94737ec3db1ce418d0c933512a71f78). + * [Don't run push mirrors for archived repos](https://codeberg.org/forgejo/forgejo/commit/f3ba3e922dde7d12999a90d6cee15805a56cc7ff). + * [Support for grouping RPMs using paths](https://codeberg.org/forgejo/forgejo/commit/ba4d0b8ffbd78473273800f586ae8bde55cda6c5). + * [Fixes #27605: inline math blocks can't be preceded/followed by alphanumerical characters](https://codeberg.org/forgejo/forgejo/commit/2adc3a45fbd60126c0eab66b9cdd177a63bd4704). + * [Fix GPG subkey verify](https://codeberg.org/forgejo/forgejo/commit/5a674dd02ed3ea2853afa02dc15dcdadba069a6e). + * [Include encoding in signature payload](https://codeberg.org/forgejo/forgejo/commit/6925c0eee43980133896f9e4ee7e48e5751e9417). + * [Fix milestoneID filter bug in issue list](https://codeberg.org/forgejo/forgejo/commit/0da787f23737d252e6c80aa1a1f665e09dba0ea9). + * [Fix Citation modal responsiveness and clipboard copy](https://codeberg.org/forgejo/forgejo/commit/ca39d743636c9732f4422e130bac974555fb43c2). + * [Fix incorrect locale Tr for gpg command](https://codeberg.org/forgejo/forgejo/commit/071d871dcf8dd8097dc0af6d4baf304a2fbbe4e2). + * [Improve a11y document and dropdown item](https://codeberg.org/forgejo/forgejo/commit/1d4bf7e211db0866774fa3f6f563e15ffadac1f6). + * [Determine fuzziness of bleve indexer by keyword length](https://codeberg.org/forgejo/forgejo/commit/ab5f0b7558229b3ab5c3946a51e58b4caae775b0). + * [Fix ellipsis button not working if the last commit loading is deferred](https://codeberg.org/forgejo/forgejo/commit/1e29bccddbeb29eec3ceb507612851021ab4d60d). + * [Fix incorrect diff expander for deletion of last lines in a file](https://codeberg.org/forgejo/forgejo/commit/85bf170ff0d54471fe88903009a3fec4ef3e6e8c). + * [Do not exceed display for the PR page buttons on smaller screens](https://codeberg.org/forgejo/forgejo/commit/e7297d423f566a383c8861c4aaee028606591038). + * [Move citation button to proper place](https://codeberg.org/forgejo/forgejo/commit/eb4061babacfee2b72f4a33412530eb9f0de3b25). + * [Expire artifacts before deleting them physically](https://codeberg.org/forgejo/forgejo/commit/7f64e4d2a3f20b7d7de6542de5e0856c643e821f). + * [Fix can not select team reviewers when reviewers is empty](https://codeberg.org/forgejo/forgejo/commit/df439b6a983865ba559e517e5e93f5f1a53a97a0). + * [Fix default avatar image size in PR diff page](https://codeberg.org/forgejo/forgejo/commit/3aed8ae03475a430c0dc8e33f42fa9269a4844bd). + * [Fix branch list bug which displayed default branch twice](https://codeberg.org/forgejo/forgejo/commit/0e6fd0d1c1e31d22707e6f06124d5bf76361eaab). + * [Set the `isPermaLink` attribute to `false` in the `guid` sub-element](https://codeberg.org/forgejo/forgejo/commit/5574968ecbc34908dfa17b28bfc79c3490eaa685). + * [Fix long package version names overflowing](https://codeberg.org/forgejo/forgejo/commit/3d474110c181df7854576d78e46209908f7e1b52). + * [Fix wrong link in user and organization profile when using relative url](https://codeberg.org/forgejo/forgejo/commit/42149ff1a816501643ec2407ed61a83bf5b65059). + * [Fix session key conflict with database keyword](https://codeberg.org/forgejo/forgejo/commit/4c29c75968f520123f125e8305b2c29198664251). + * [Fix commit status in repo list](https://codeberg.org/forgejo/forgejo/commit/0abb5633e34fd14c2d49de0b4c98f7ba7d98a37e). + * [Fix incorrect action duration time when rerun the job before executed once](https://codeberg.org/forgejo/forgejo/commit/07ba4d9f87cf21b7ce87158ae5651cae3bb35604). + * [Fix missing mail reply address](https://codeberg.org/forgejo/forgejo/commit/3081e7e1536356346f73fb4a0d00101863b2cf05). + * [Filter inactive auth sources](https://codeberg.org/forgejo/forgejo/commit/e378545f3083990eb36ff5d72477662d9787280d). + * [Refactor Find Sources and fix bug when view a user who belongs to an inactive auth source](https://codeberg.org/forgejo/forgejo/commit/1bf5527eac6b947010c8faf408f6747de2a2384f). + * [Fix issue not showing on default board and add test](https://codeberg.org/forgejo/forgejo/commit/1eae2aadae0583ab092d6ed857bb727829aa52b7). + * [Improve file history UI and fix URL escaping bug](https://codeberg.org/forgejo/forgejo/commit/d1527dac3d1e68caf5a6f54c08144e28256e5c47). + * [Fix ldap admin privileges update bug](https://codeberg.org/forgejo/forgejo/commit/7ad31567cdc8206e0080b851a9b880729266b084). +* **other** + * [[PERFORMANCE] git check-attr on bare repo if supported](https://codeberg.org/forgejo/forgejo/pulls/2763) + * [[REFACTOR] [AGIT] Refactor the AGit code](https://codeberg.org/forgejo/forgejo/pulls/2386) + * [[REFACTOR] generation of JWT secret](https://codeberg.org/forgejo/forgejo/pulls/2227) + * [[REFACTOR] PKT protocol](https://codeberg.org/forgejo/forgejo/pulls/2868) + * [Remove .exe suffix when cross-compiling on Windows](https://codeberg.org/forgejo/forgejo/commit/6acce16ee3a03df1cc06c46398f594009a0e31b9). + * [Refactor repo header/list](https://codeberg.org/forgejo/forgejo/commit/65e190ae8bd6c72d8701a58d67b256c87b92c189). + * [Update register application URL for GitLab](https://codeberg.org/forgejo/forgejo/commit/64fcf0cb64d455d5ca1420aa832aa057cf61e6dd). + * [Update golang links to use https](https://codeberg.org/forgejo/forgejo/commit/8ef53c871bcb5c007b3640a347c7868585c9e4de). + * [Teams: new View button](https://codeberg.org/forgejo/forgejo/commit/e3afe4a248ac3a961f332e2ba221bedafa3dfb7e). + * [Commit-Dropdown: Show Author of commit if available](https://codeberg.org/forgejo/forgejo/commit/300c8dedfd01ba0ea63486b644e93aa2be6785b2). + * [Refactor dropzone](https://codeberg.org/forgejo/forgejo/commit/c1ac3e5891a49bedc5e54ed5811cb2c0e058c43c). + * [When the title in the issue has a value, set the text cursor at the end of the text.](https://codeberg.org/forgejo/forgejo/commit/8c2559a72603e07fe682efddd698e1fc190b2728). + * [Load citation JS only when needed](https://codeberg.org/forgejo/forgejo/commit/f2fc2dcfc9305a42242421c718ee3673bd1c851c). + * [Refactor markdown attention render](https://codeberg.org/forgejo/forgejo/commit/ec2201a3da5f18e55bfc0a54114ac935804f4ef8). + * [Light theme color enhancements](https://codeberg.org/forgejo/forgejo/commit/23e2ace77d1612cda09bc0d08690314e7321cca3). + * [Dark theme color enhancements](https://codeberg.org/forgejo/forgejo/commit/704a59e59584041f95939e3d90260173906f946a). + * [Refactor markup/csv: don't read all to memory](https://codeberg.org/forgejo/forgejo/commit/d413a8fcacc81b6f7039371408034c9c2fc6c15f). + * [Move all login and account creation page labels to be above inputs](https://codeberg.org/forgejo/forgejo/commit/3acea02eb66ea09248ff29eb6b9cefce29fcea37). + * [Fix Gitpod logic of setting ROOT_URL](https://codeberg.org/forgejo/forgejo/commit/e52d87758272c417bb9b30e944f9b0bd33d28cb7). + * [Fix broken following organization](https://codeberg.org/forgejo/forgejo/commit/fd3b4afa2b3621ece2d7d1587fd4b017142d75a0). + * [Don't do a full page load when clicking `Watch` or `Star`](https://codeberg.org/forgejo/forgejo/commit/6992ef98fc227a60cf06e0a06b9ae2492b3d61be). + * [Fix non-alphabetic sorting of repo topics](https://codeberg.org/forgejo/forgejo/commit/a240d5dfa7e261f2fb703cf24b1ba4dc6aa47bfd). + * [Make cross-reference issue links work in markdown documents again](https://codeberg.org/forgejo/forgejo/commit/12c0487e01d3fd9fe289345c53e8a220be55e864). + * [Fix tooltip of variable edit button](https://codeberg.org/forgejo/forgejo/commit/361839fb1c8bdfb8291bbcf9bd650b21a605bbd7). + * [Disable query token param in integration tests](https://codeberg.org/forgejo/forgejo/commit/33439b733a4f69640350b9cda370963ebe9d1e0a). + * [Add merge arrow direction and update styling](https://codeberg.org/forgejo/forgejo/commit/e522e774cae2240279fc48c349fc513c9d3353ee). + * [Add links to owner home page in explore](https://codeberg.org/forgejo/forgejo/commit/dd5693387e0642e1aba05b01eeb18139ce90ef5e). + * [Render PyPi long description as document](https://codeberg.org/forgejo/forgejo/commit/876a0cb3d652f42545abdb33dc4fd71a7c3343bf). + * [Ignore temporary files for directory size](https://codeberg.org/forgejo/forgejo/commit/cb8298b7178f5dde302604bfe34c658b725f16f8). + * [Make pushUpdate error verbose](https://codeberg.org/forgejo/forgejo/commit/1bfcdeef4cca0f5509476358e5931c13d37ed1ca). + * [Add download URL for executable files](https://codeberg.org/forgejo/forgejo/commit/9341b37520e5626352bf2df52e8dbace2985c0d7). + * [Improve profile for Organizations](https://codeberg.org/forgejo/forgejo/commit/089ac06969030b0886d4e20bf8f7a757f785f158). + * [Fix Show/hide filetree button on small displays](https://codeberg.org/forgejo/forgejo/commit/e31c6cfe6e30341c502302d1c0a03138f8bf5c9f). + * [Fix merge base commit for fast-forwarded GitLab PRs](https://codeberg.org/forgejo/forgejo/commit/02dae3f84b80047bef391960eea1350d551e4d72). + * [Align ISSUE_TEMPLATE with the new label system](https://codeberg.org/forgejo/forgejo/commit/248b7ee850ecdb538b22ddcfbe80b6f91be32b70). + * [Improve the list header in milestone page](https://codeberg.org/forgejo/forgejo/commit/8abc1aae4ab5b03be0bcbdd390bb903b54ccd21a). + +## 1.21.11-1 + +This stable release contains a single bug fix for a regression introduced in v1.21.11-0 by which creating a tag via the API would fail with error 500 on a repository a where Forgejo Actions workflow triggered by tags exists. + +* Recommended Action + + We recommend that all Forgejo installations are [upgraded](https://forgejo.org/docs/v1.21/admin/upgrade/) to the latest version as soon as possible. + +* [Forgejo Semantic Version](https://forgejo.org/docs/v1.21/user/semver/) + + The semantic version was updated to `6.0.12+0-gitea-1.21.10` + +* Bug fix + + * [error 500 on tag creation when a workflow exists](https://codeberg.org/forgejo/forgejo/issues/3327) + +## 1.21.11-0 + +[The complete list of new commits included in the Forgejo v1.21.11-0 release can be reviewed here](https://codeberg.org/forgejo/forgejo/compare/v1.21.10-0...v1.21.11-0), or from the comand line with: + +```shell +$ git clone https://codeberg.org/forgejo/forgejo +$ git -C forgejo log --oneline --no-merges v1.21.10-0..v1.21.11-0 +``` + +This stable release contains bug fixes and **security fixes**. + +* Recommended Action + + We strongly recommend that all Forgejo installations are [upgraded](https://forgejo.org/docs/v1.21/admin/upgrade/) to the latest version as soon as possible. + +* [Forgejo Semantic Version](https://forgejo.org/docs/v1.21/user/semver/) + + The semantic version was updated to `6.0.11+0-gitea-1.21.10` + +* Security fix + + * [Fixed a privilege escalation through git push options](https://codeberg.org/forgejo/forgejo/commit/cc80e661531794fff7f8a336eaaefdb7e3bd3956) that allows any user to change the visibility of any repository they can see, regardless of their level of access. + * [Fixed a bug that allows user-supplied, non-sandboxed JavaScript to be run from the same domain as the forge](https://codeberg.org/forgejo/forgejo/commit/8dcc7d9e8ce36d94bae1a1becddc4735f51add3c), via `/{owner}/{repo}/render/branch/{branch}/{filename}` URLs. + +* Bug fixes + + * [Use system action user to trigger scheduled action workflows](https://codeberg.org/forgejo/forgejo/commit/387aea4434488555838e55e067242509bc1510a6) + * [Close file in upload function](https://codeberg.org/forgejo/forgejo/commit/fd47240545ab1c4f10d07434c2ba00fff044236a) + * [Prevent registering runners for deleted repositories](https://codeberg.org/forgejo/forgejo/commit/fd47240545ab1c4f10d07434c2ba00fff044236a). Prevents 500 Internal Server Error in admin interface. + * [More reliable pagination support when migrating from gitbucket](https://codeberg.org/forgejo/forgejo/commit/e702e79625980b08ec060a1690b76502455acad9) + * [Fix automerge when used with actions](https://codeberg.org/forgejo/forgejo/commit/4889a3a1713d91a5ae95af4edf1bb3352d1871fd) ## 1.21.10-0 @@ -15,7 +522,7 @@ $ git -C forgejo log --oneline --no-merges v1.21.8-0..v1.21.10-0 This stable release contains bug fixes and a **security fix**. -Note that there is no `Forgejo v1.21.9-0` release. The release numbering of the `Forgejo v1.21` patch series follows the Gitea release numbering. However, the publication of `Gitea v1.21.9` and `Gitea v1.21.10` were a few days appart because of a regression that is not present on Forgejo and there was no need to publish `Forgejo v1.21.9-0`. +Note that there is no `Forgejo v1.21.9-0` release. The release numbering of the `Forgejo v1.21` patch series follows the Gitea release numbering. However, the publication of `Gitea v1.21.9` and `Gitea v1.21.10` were a few days apart because of a regression that is not present on Forgejo and there was no need to publish `Forgejo v1.21.9-0`. * Recommended Action @@ -977,7 +1484,7 @@ This stable release contains security fixes. * Security fixes - * [An additional verification](https://codeberg.org/forgejo/forgejo/commit/a259a928a) was implemented to prevent [open redirects](https://en.wikipedia.org/wiki/Open_redirect). + * [An additional verification](https://codeberg.org/forgejo/forgejo/commit/a259a928a) was implemented to prevent [open redirects](https://en.wikipedia.org/wiki/Open_redirect). * Bug fixes diff --git a/build/backport-locales.go b/build/backport-locales.go index d112dd72bd..3df83ea6d9 100644 --- a/build/backport-locales.go +++ b/build/backport-locales.go @@ -18,8 +18,8 @@ import ( func main() { if len(os.Args) != 2 { - println("usage: backport-locales ") - println("eg: backport-locales release/v1.19") + fmt.Println("usage: backport-locales ") + fmt.Println("eg: backport-locales release/v1.19") os.Exit(1) } diff --git a/build/code-batch-process.go b/build/code-batch-process.go index b3ee399420..cc2ab68026 100644 --- a/build/code-batch-process.go +++ b/build/code-batch-process.go @@ -69,6 +69,7 @@ func newFileCollector(fileFilter string, batchSize int) (*fileCollector, error) co.includePatterns = append(co.includePatterns, regexp.MustCompile(`.*\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`.*\bbindata\.go$`)) + co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`\.pb\.go$`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/gitea-repositories-meta`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`tests/integration/migration-test`)) co.excludePatterns = append(co.excludePatterns, regexp.MustCompile(`modules/git/tests`)) @@ -203,17 +204,6 @@ Example: `, "file-batch-exec") } -func getGoVersion() string { - goModFile, err := os.ReadFile("go.mod") - if err != nil { - log.Fatalf(`Faild to read "go.mod": %v`, err) - os.Exit(1) - } - goModVersionRegex := regexp.MustCompile(`go \d+\.\d+`) - goModVersionLine := goModVersionRegex.Find(goModFile) - return string(goModVersionLine[3:]) -} - func newFileCollectorFromMainOptions(mainOptions map[string]string) (fc *fileCollector, err error) { fileFilter := mainOptions["file-filter"] if fileFilter == "" { @@ -278,7 +268,8 @@ func main() { log.Print("the -d option is not supported by gitea-fmt") } cmdErrors = append(cmdErrors, giteaFormatGoImports(files, containsString(subArgs, "-w"))) - cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra", "-lang", getGoVersion()}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("gofmt", append([]string{"-w", "-r", "interface{} -> any"}, substArgs...))) + cmdErrors = append(cmdErrors, passThroughCmd("go", append([]string{"run", os.Getenv("GOFUMPT_PACKAGE"), "-extra"}, substArgs...))) default: log.Fatalf("unknown cmd: %s %v", subCmd, subArgs) } diff --git a/build/generate-emoji.go b/build/generate-emoji.go index 17a9670f06..5a88e456ee 100644 --- a/build/generate-emoji.go +++ b/build/generate-emoji.go @@ -142,7 +142,7 @@ func generate() ([]byte, error) { } } - // gitea customizations + // Forgejo customizations i, ok := aliasMap["tada"] if ok { data[i].Aliases = append(data[i].Aliases, "hooray") diff --git a/cmd/admin_user_change_password.go b/cmd/admin_user_change_password.go index 824d66d112..bd9063a8e4 100644 --- a/cmd/admin_user_change_password.go +++ b/cmd/admin_user_change_password.go @@ -36,6 +36,7 @@ var microcmdUserChangePassword = &cli.Command{ &cli.BoolFlag{ Name: "must-change-password", Usage: "User must change password", + Value: true, }, }, } @@ -57,23 +58,18 @@ func runChangePassword(c *cli.Context) error { return err } - var mustChangePassword optional.Option[bool] - if c.IsSet("must-change-password") { - mustChangePassword = optional.Some(c.Bool("must-change-password")) - } - opts := &user_service.UpdateAuthOptions{ Password: optional.Some(c.String("password")), - MustChangePassword: mustChangePassword, + MustChangePassword: optional.Some(c.Bool("must-change-password")), } if err := user_service.UpdateAuth(ctx, user, opts); err != nil { switch { case errors.Is(err, password.ErrMinLength): - return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength) + return fmt.Errorf("password is not long enough, needs to be at least %d characters", setting.MinPasswordLength) case errors.Is(err, password.ErrComplexity): - return errors.New("Password does not meet complexity requirements") + return errors.New("password does not meet complexity requirements") case errors.Is(err, password.ErrIsPwned): - return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords") + return errors.New("the password is in a list of stolen passwords previously exposed in public data breaches, please try again with a different password, to see more details: https://haveibeenpwned.com/Passwords") default: return err } diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index 10965c7e8f..dfc484aeb2 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -8,6 +8,7 @@ import ( "fmt" auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" pwd "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/optional" @@ -46,9 +47,10 @@ var microcmdUserCreate = &cli.Command{ Usage: "Generate a random password for the user", }, &cli.BoolFlag{ - Name: "must-change-password", - Usage: "Set this option to false to prevent forcing the user to change their password after initial login", - Value: true, + Name: "must-change-password", + Usage: "Set this option to false to prevent forcing the user to change their password after initial login", + Value: true, + DisableDefaultText: true, }, &cli.IntFlag{ Name: "random-password-length", @@ -72,10 +74,10 @@ func runCreateUser(c *cli.Context) error { } if c.IsSet("name") && c.IsSet("username") { - return errors.New("Cannot set both --name and --username flags") + return errors.New("cannot set both --name and --username flags") } if !c.IsSet("name") && !c.IsSet("username") { - return errors.New("One of --name or --username flags must be set") + return errors.New("one of --name or --username flags must be set") } if c.IsSet("password") && c.IsSet("random-password") { @@ -111,12 +113,21 @@ func runCreateUser(c *cli.Context) error { return errors.New("must set either password or random-password flag") } - changePassword := c.Bool("must-change-password") - - // If this is the first user being created. - // Take it as the admin and don't force a password update. - if n := user_model.CountUsers(ctx, nil); n == 0 { - changePassword = false + isAdmin := c.Bool("admin") + mustChangePassword := true // always default to true + if c.IsSet("must-change-password") { + // if the flag is set, use the value provided by the user + mustChangePassword = c.Bool("must-change-password") + } else { + // check whether there are users in the database + hasUserRecord, err := db.IsTableNotEmpty(&user_model.User{}) + if err != nil { + return fmt.Errorf("IsTableNotEmpty: %w", err) + } + if !hasUserRecord { + // if this is the first admin being created, don't force to change password (keep the old behavior) + mustChangePassword = false + } } restricted := optional.None[bool]() @@ -132,8 +143,8 @@ func runCreateUser(c *cli.Context) error { Name: username, Email: c.String("email"), Passwd: password, - IsAdmin: c.Bool("admin"), - MustChangePassword: changePassword, + IsAdmin: isAdmin, + MustChangePassword: mustChangePassword, Visibility: visibility, } diff --git a/cmd/hook.go b/cmd/hook.go index 04df7ce18c..81955e753b 100644 --- a/cmd/hook.go +++ b/cmd/hook.go @@ -782,7 +782,7 @@ func writeFlushPktLine(ctx context.Context, out io.Writer) error { return nil } -// Write an Pkt-Line based on `data` to `out` according to the specifcation. +// Write an Pkt-Line based on `data` to `out` according to the specification. // https://git-scm.com/docs/protocol-common func writeDataPktLine(ctx context.Context, out io.Writer, data []byte) error { // Implementations SHOULD NOT send an empty pkt-line ("0004"). diff --git a/cmd/serv.go b/cmd/serv.go index d5c54f91b8..0e3006b36b 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -136,7 +136,7 @@ func runServ(c *cli.Context) error { setup(ctx, c.Bool("debug")) if setting.SSH.Disabled { - println("Forgejo: SSH has been disabled") + fmt.Println("Forgejo: SSH has been disabled") return nil } @@ -164,13 +164,13 @@ func runServ(c *cli.Context) error { } switch key.Type { case asymkey_model.KeyTypeDeploy: - println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Forgejo does not provide shell access.") + fmt.Println("Hi there! You've successfully authenticated with the deploy key named " + key.Name + ", but Forgejo does not provide shell access.") case asymkey_model.KeyTypePrincipal: - println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Forgejo does not provide shell access.") + fmt.Println("Hi there! You've successfully authenticated with the principal " + key.Content + ", but Forgejo does not provide shell access.") default: - println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Forgejo does not provide shell access.") + fmt.Println("Hi there, " + user.Name + "! You've successfully authenticated with the key named " + key.Name + ", but Forgejo does not provide shell access.") } - println("If this is unexpected, please log in with password and setup Forgejo under another user.") + fmt.Println("If this is unexpected, please log in with password and setup Forgejo under another user.") return nil } else if c.Bool("debug") { log.Debug("SSH_ORIGINAL_COMMAND: %s", os.Getenv("SSH_ORIGINAL_COMMAND")) diff --git a/cmd/web.go b/cmd/web.go index ef82486c1f..44babd51c5 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -114,7 +114,7 @@ func showWebStartupMessage(msg string) { log.Info("* WorkPath: %s", setting.AppWorkPath) log.Info("* CustomPath: %s", setting.CustomPath) log.Info("* ConfigFile: %s", setting.CustomConf) - log.Info("%s", msg) + log.Info("%s", msg) // show startup message } func serveInstall(ctx *cli.Context) error { diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 304eb3af75..4eee2cd1ff 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -407,8 +407,8 @@ USER = root ;; Database connection max life time, default is 0 or 3s mysql (See #6804 & #7071 for reasoning) ;CONN_MAX_LIFETIME = 3s ;; -;; Database maximum number of open connections, default is 0 meaning no maximum -;MAX_OPEN_CONNS = 0 +;; Database maximum number of open connections, default is 100 which is the lowest default from Postgres (MariaDB + MySQL default to 151). Ensure you only increase the value if you configured your database server accordingly. +;MAX_OPEN_CONNS = 100 ;; ;; Whether execute database models migrations automatically ;AUTO_MIGRATION = true @@ -2394,22 +2394,6 @@ LEVEL = Info ;; Enable issue by repository metrics; default is false ;ENABLED_ISSUE_BY_REPOSITORY = false -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;[task] -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; -;; Task queue type, could be `channel` or `redis`. -;QUEUE_TYPE = channel -;; -;; Task queue length, available only when `QUEUE_TYPE` is `channel`. -;QUEUE_LENGTH = 1000 -;; -;; Task queue connection string, available only when `QUEUE_TYPE` is `redis`. -;; If there is a password of redis, use `redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` or `redis+cluster://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s` for `redis-clsuter`. -;QUEUE_CONN_STR = "redis://127.0.0.1:6379/0?pool_size=100&idle_timeout=180s" - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;[migrations] diff --git a/go.mod b/go.mod index 5c5c914038..683ea3806e 100644 --- a/go.mod +++ b/go.mod @@ -64,7 +64,7 @@ require ( github.com/json-iterator/go v1.1.12 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/keybase/go-crypto v0.0.0-20200123153347-de78d2cb44f4 - github.com/klauspost/compress v1.17.7 + github.com/klauspost/compress v1.17.8 github.com/klauspost/cpuid/v2 v2.2.6 github.com/lib/pq v1.10.9 github.com/markbates/goth v1.78.0 diff --git a/go.sum b/go.sum index f9b4bb00c9..ce55e89399 100644 --- a/go.sum +++ b/go.sum @@ -551,8 +551,8 @@ github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= github.com/klauspost/compress v1.15.6/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= -github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= -github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c= diff --git a/manual-testing b/manual-testing deleted file mode 160000 index 877d11b403..0000000000 --- a/manual-testing +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 877d11b403b2b573fe435b792245b403367a2bb2 diff --git a/models/actions/run_job_list.go b/models/actions/run_job_list.go index 6ea6cb9d3b..6c5d3b3252 100644 --- a/models/actions/run_job_list.go +++ b/models/actions/run_job_list.go @@ -16,14 +16,9 @@ import ( type ActionJobList []*ActionRunJob func (jobs ActionJobList) GetRunIDs() []int64 { - ids := make(container.Set[int64], len(jobs)) - for _, j := range jobs { - if j.RunID == 0 { - continue - } - ids.Add(j.RunID) - } - return ids.Values() + return container.FilterSlice(jobs, func(j *ActionRunJob) (int64, bool) { + return j.RunID, j.RunID != 0 + }) } func (jobs ActionJobList) LoadRuns(ctx context.Context, withRepo bool) error { diff --git a/models/actions/run_list.go b/models/actions/run_list.go index 388bfc4f86..4046c7d369 100644 --- a/models/actions/run_list.go +++ b/models/actions/run_list.go @@ -19,19 +19,15 @@ type RunList []*ActionRun // GetUserIDs returns a slice of user's id func (runs RunList) GetUserIDs() []int64 { - ids := make(container.Set[int64], len(runs)) - for _, run := range runs { - ids.Add(run.TriggerUserID) - } - return ids.Values() + return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { + return run.TriggerUserID, true + }) } func (runs RunList) GetRepoIDs() []int64 { - ids := make(container.Set[int64], len(runs)) - for _, run := range runs { - ids.Add(run.RepoID) - } - return ids.Values() + return container.FilterSlice(runs, func(run *ActionRun) (int64, bool) { + return run.RepoID, true + }) } func (runs RunList) LoadTriggerUser(ctx context.Context) error { diff --git a/models/actions/runner_list.go b/models/actions/runner_list.go index 87f0886b47..3ef8ebb254 100644 --- a/models/actions/runner_list.go +++ b/models/actions/runner_list.go @@ -16,14 +16,9 @@ type RunnerList []*ActionRunner // GetUserIDs returns a slice of user's id func (runners RunnerList) GetUserIDs() []int64 { - ids := make(container.Set[int64], len(runners)) - for _, runner := range runners { - if runner.OwnerID == 0 { - continue - } - ids.Add(runner.OwnerID) - } - return ids.Values() + return container.FilterSlice(runners, func(runner *ActionRunner) (int64, bool) { + return runner.OwnerID, runner.OwnerID != 0 + }) } func (runners RunnerList) LoadOwners(ctx context.Context) error { @@ -41,16 +36,9 @@ func (runners RunnerList) LoadOwners(ctx context.Context) error { } func (runners RunnerList) getRepoIDs() []int64 { - repoIDs := make(container.Set[int64], len(runners)) - for _, runner := range runners { - if runner.RepoID == 0 { - continue - } - if _, ok := repoIDs[runner.RepoID]; !ok { - repoIDs[runner.RepoID] = struct{}{} - } - } - return repoIDs.Values() + return container.FilterSlice(runners, func(runner *ActionRunner) (int64, bool) { + return runner.RepoID, runner.RepoID > 0 + }) } func (runners RunnerList) LoadRepos(ctx context.Context) error { diff --git a/models/actions/schedule_list.go b/models/actions/schedule_list.go index b806550b87..5361b94801 100644 --- a/models/actions/schedule_list.go +++ b/models/actions/schedule_list.go @@ -18,19 +18,15 @@ type ScheduleList []*ActionSchedule // GetUserIDs returns a slice of user's id func (schedules ScheduleList) GetUserIDs() []int64 { - ids := make(container.Set[int64], len(schedules)) - for _, schedule := range schedules { - ids.Add(schedule.TriggerUserID) - } - return ids.Values() + return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) { + return schedule.TriggerUserID, true + }) } func (schedules ScheduleList) GetRepoIDs() []int64 { - ids := make(container.Set[int64], len(schedules)) - for _, schedule := range schedules { - ids.Add(schedule.RepoID) - } - return ids.Values() + return container.FilterSlice(schedules, func(schedule *ActionSchedule) (int64, bool) { + return schedule.RepoID, true + }) } func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { @@ -44,6 +40,9 @@ func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { schedule.TriggerUser = user_model.NewActionsUser() } else { schedule.TriggerUser = users[schedule.TriggerUserID] + if schedule.TriggerUser == nil { + schedule.TriggerUser = user_model.NewGhostUser() + } } } return nil diff --git a/models/actions/schedule_spec_list.go b/models/actions/schedule_spec_list.go index e9ae268a6e..f7dac72f8b 100644 --- a/models/actions/schedule_spec_list.go +++ b/models/actions/schedule_spec_list.go @@ -16,11 +16,9 @@ import ( type SpecList []*ActionScheduleSpec func (specs SpecList) GetScheduleIDs() []int64 { - ids := make(container.Set[int64], len(specs)) - for _, spec := range specs { - ids.Add(spec.ScheduleID) - } - return ids.Values() + return container.FilterSlice(specs, func(spec *ActionScheduleSpec) (int64, bool) { + return spec.ScheduleID, true + }) } func (specs SpecList) LoadSchedules(ctx context.Context) error { @@ -46,11 +44,9 @@ func (specs SpecList) LoadSchedules(ctx context.Context) error { } func (specs SpecList) GetRepoIDs() []int64 { - ids := make(container.Set[int64], len(specs)) - for _, spec := range specs { - ids.Add(spec.RepoID) - } - return ids.Values() + return container.FilterSlice(specs, func(spec *ActionScheduleSpec) (int64, bool) { + return spec.RepoID, true + }) } func (specs SpecList) LoadRepos(ctx context.Context) error { diff --git a/models/actions/task.go b/models/actions/task.go index 96a6d2e80c..9946cf5233 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -11,6 +11,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" @@ -227,7 +228,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask if runner.RepoID != 0 { jobCond = builder.Eq{"repo_id": runner.RepoID} } else if runner.OwnerID != 0 { - jobCond = builder.In("repo_id", builder.Select("id").From("repository").Where(builder.Eq{"owner_id": runner.OwnerID})) + jobCond = builder.In("repo_id", builder.Select("`repository`.id").From("repository"). + Join("INNER", "repo_unit", "`repository`.id = `repo_unit`.repo_id"). + Where(builder.Eq{"`repository`.owner_id": runner.OwnerID, "`repo_unit`.type": unit.TypeActions})) } if jobCond.IsValid() { jobCond = builder.In("run_id", builder.Select("id").From("action_run").Where(jobCond)) diff --git a/models/actions/task_list.go b/models/actions/task_list.go index b07d00b8db..5e17f91441 100644 --- a/models/actions/task_list.go +++ b/models/actions/task_list.go @@ -16,14 +16,9 @@ import ( type TaskList []*ActionTask func (tasks TaskList) GetJobIDs() []int64 { - ids := make(container.Set[int64], len(tasks)) - for _, t := range tasks { - if t.JobID == 0 { - continue - } - ids.Add(t.JobID) - } - return ids.Values() + return container.FilterSlice(tasks, func(t *ActionTask) (int64, bool) { + return t.JobID, t.JobID != 0 + }) } func (tasks TaskList) LoadJobs(ctx context.Context) error { diff --git a/models/activities/action_list.go b/models/activities/action_list.go index fdf0f35d4f..aafb7f8a26 100644 --- a/models/activities/action_list.go +++ b/models/activities/action_list.go @@ -22,11 +22,9 @@ import ( type ActionList []*Action func (actions ActionList) getUserIDs() []int64 { - userIDs := make(container.Set[int64], len(actions)) - for _, action := range actions { - userIDs.Add(action.ActUserID) - } - return userIDs.Values() + return container.FilterSlice(actions, func(action *Action) (int64, bool) { + return action.ActUserID, true + }) } func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_model.User, error) { @@ -50,11 +48,9 @@ func (actions ActionList) LoadActUsers(ctx context.Context) (map[int64]*user_mod } func (actions ActionList) getRepoIDs() []int64 { - repoIDs := make(container.Set[int64], len(actions)) - for _, action := range actions { - repoIDs.Add(action.RepoID) - } - return repoIDs.Values() + return container.FilterSlice(actions, func(action *Action) (int64, bool) { + return action.RepoID, true + }) } func (actions ActionList) LoadRepositories(ctx context.Context) error { @@ -80,18 +76,19 @@ func (actions ActionList) loadRepoOwner(ctx context.Context, userMap map[int64]* userMap = make(map[int64]*user_model.User) } - userSet := make(container.Set[int64], len(actions)) - for _, action := range actions { + missingUserIDs := container.FilterSlice(actions, func(action *Action) (int64, bool) { if action.Repo == nil { - continue - } - if _, ok := userMap[action.Repo.OwnerID]; !ok { - userSet.Add(action.Repo.OwnerID) + return 0, false } + _, alreadyLoaded := userMap[action.Repo.OwnerID] + return action.Repo.OwnerID, !alreadyLoaded + }) + if len(missingUserIDs) == 0 { + return nil } if err := db.GetEngine(ctx). - In("id", userSet.Values()). + In("id", missingUserIDs). Find(&userMap); err != nil { return fmt.Errorf("find user: %w", err) } @@ -135,6 +132,9 @@ func (actions ActionList) LoadComments(ctx context.Context) error { commentIDs = append(commentIDs, action.CommentID) } } + if len(commentIDs) == 0 { + return nil + } commentsMap := make(map[int64]*issues_model.Comment, len(commentIDs)) if err := db.GetEngine(ctx).In("id", commentIDs).Find(&commentsMap); err != nil { diff --git a/models/activities/notification_list.go b/models/activities/notification_list.go index a6140d6f73..32d2a5c051 100644 --- a/models/activities/notification_list.go +++ b/models/activities/notification_list.go @@ -196,15 +196,11 @@ func (nl NotificationList) LoadAttributes(ctx context.Context) error { return nil } +// getPendingRepoIDs returns all the repositoty ids which haven't been loaded func (nl NotificationList) getPendingRepoIDs() []int64 { - ids := make(container.Set[int64], len(nl)) - for _, notification := range nl { - if notification.Repository != nil { - continue - } - ids.Add(notification.RepoID) - } - return ids.Values() + return container.FilterSlice(nl, func(n *Notification) (int64, bool) { + return n.RepoID, n.Repository == nil + }) } // LoadRepos loads repositories from database diff --git a/models/asymkey/ssh_key_fingerprint.go b/models/asymkey/ssh_key_fingerprint.go index b9cfb1b251..1ed3b5df2a 100644 --- a/models/asymkey/ssh_key_fingerprint.go +++ b/models/asymkey/ssh_key_fingerprint.go @@ -76,23 +76,14 @@ func calcFingerprintNative(publicKeyContent string) (string, error) { // CalcFingerprint calculate public key's fingerprint func CalcFingerprint(publicKeyContent string) (string, error) { // Call the method based on configuration - var ( - fnName, fp string - err error - ) - if len(setting.SSH.KeygenPath) == 0 { - fnName = "calcFingerprintNative" - fp, err = calcFingerprintNative(publicKeyContent) - } else { - fnName = "calcFingerprintSSHKeygen" - fp, err = calcFingerprintSSHKeygen(publicKeyContent) - } + useNative := setting.SSH.KeygenPath == "" + calcFn := util.Iif(useNative, calcFingerprintNative, calcFingerprintSSHKeygen) + fp, err := calcFn(publicKeyContent) if err != nil { if IsErrKeyUnableVerify(err) { - log.Info("%s", publicKeyContent) return "", err } - return "", fmt.Errorf("%s: %w", fnName, err) + return "", fmt.Errorf("CalcFingerprint(%s): %w", util.Iif(useNative, "native", "ssh-keygen"), err) } return fp, nil } diff --git a/models/auth/oauth2.go b/models/auth/oauth2.go index 83d60e3abe..5eabc7d9b4 100644 --- a/models/auth/oauth2.go +++ b/models/auth/oauth2.go @@ -144,6 +144,11 @@ func (app *OAuth2Application) TableName() string { // ContainsRedirectURI checks if redirectURI is allowed for app func (app *OAuth2Application) ContainsRedirectURI(redirectURI string) bool { + // OAuth2 requires the redirect URI to be an exact match, no dynamic parts are allowed. + // https://stackoverflow.com/questions/55524480/should-dynamic-query-parameters-be-present-in-the-redirection-uri-for-an-oauth2 + // https://www.rfc-editor.org/rfc/rfc6819#section-5.2.3.3 + // https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-12#section-3.1 contains := func(s string) bool { s = strings.TrimSuffix(strings.ToLower(s), "/") for _, u := range app.RedirectURIs { diff --git a/models/auth/source.go b/models/auth/source.go index 1a3a1b20a6..d03d4975dc 100644 --- a/models/auth/source.go +++ b/models/auth/source.go @@ -33,6 +33,7 @@ const ( DLDAP // 5 OAuth2 // 6 SSPI // 7 + Remote // 8 ) // String returns the string name of the LoginType @@ -53,6 +54,7 @@ var Names = map[Type]string{ PAM: "PAM", OAuth2: "OAuth2", SSPI: "SPNEGO with SSPI", + Remote: "Remote", } // Config represents login config as far as the db is concerned @@ -181,6 +183,10 @@ func (source *Source) IsSSPI() bool { return source.Type == SSPI } +func (source *Source) IsRemote() bool { + return source.Type == Remote +} + // HasTLS returns true of this source supports TLS. func (source *Source) HasTLS() bool { hasTLSer, ok := source.Cfg.(HasTLSer) diff --git a/models/db/collation.go b/models/db/collation.go index 61260d6d17..39d28fa2ff 100644 --- a/models/db/collation.go +++ b/models/db/collation.go @@ -150,7 +150,7 @@ func preprocessDatabaseCollation(x *xorm.Engine) { // check column collation, and show warning/error to end users -- no need to fatal, do not block the startup if !r.IsCollationCaseSensitive(r.DatabaseCollation) { - log.Warn("Current database is using a case-insensitive collation %q, although Gitea could work with it, there might be some rare cases which don't work as expected.", r.DatabaseCollation) + log.Warn("Current database is using a case-insensitive collation %q, although Forgejo could work with it, there might be some rare cases which don't work as expected.", r.DatabaseCollation) } if len(r.InconsistentCollationColumns) > 0 { diff --git a/models/db/engine.go b/models/db/engine.go index 09f64b6c8f..1f0cd1a5dd 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -293,8 +293,8 @@ func MaxBatchInsertSize(bean any) int { } // IsTableNotEmpty returns true if table has at least one record -func IsTableNotEmpty(tableName string) (bool, error) { - return x.Table(tableName).Exist() +func IsTableNotEmpty(beanOrTableName any) (bool, error) { + return x.Table(beanOrTableName).Exist() } // DeleteAllRecords will delete all the records of this table diff --git a/models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/issue.yml b/models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/issue.yml new file mode 100644 index 0000000000..7fe592ed5a --- /dev/null +++ b/models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/issue.yml @@ -0,0 +1,12 @@ +- + id: 1001 + repo_id: 1 + index: 1001 + poster_id: 1 + name: issue1 + content: content for the first issue + is_pull: true + created: 111111111 + created_unix: 946684800 + updated_unix: 978307200 + is_closed: false diff --git a/models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/pull_request.yml b/models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/pull_request.yml new file mode 100644 index 0000000000..93f27c747c --- /dev/null +++ b/models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/pull_request.yml @@ -0,0 +1,13 @@ +- + id: 1001 + type: 0 # pull request + status: 2 # mergable + issue_id: 1001 + index: 1001 + head_repo_id: 1 + base_repo_id: 1 + head_branch: branchmax + base_branch: master + merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 + has_merged: false + flow: 0 diff --git a/models/fixtures/action_run.yml b/models/fixtures/action_run.yml index 405de2c548..9c60b352f9 100644 --- a/models/fixtures/action_run.yml +++ b/models/fixtures/action_run.yml @@ -413,3 +413,23 @@ }, "total_commits": 0 } +- + id: 891 + title: "update actions" + repo_id: 1 + owner_id: 1 + workflow_id: "artifact.yaml" + index: 187 + trigger_user_id: 1 + ref: "refs/heads/branch2" + commit_sha: "985f0301dba5e7b34be866819cd15ad3d8f508ee" + event: "push" + is_fork_pull_request: 0 + status: 1 # success + started: 1683636528 + stopped: 1683636626 + created: 1683636108 + updated: 1683636626 + need_approval: 0 + approved_by: 0 + event_payload: '{"head_commit":{"id":"5f22f7d0d95d614d25a5b68592adb345a4b5c7fd"}}' diff --git a/models/fixtures/action_run_job.yml b/models/fixtures/action_run_job.yml index fd90f4fd5d..0b02d0e17e 100644 --- a/models/fixtures/action_run_job.yml +++ b/models/fixtures/action_run_job.yml @@ -26,3 +26,17 @@ status: 1 started: 1683636528 stopped: 1683636626 +- + id: 292 + run_id: 891 + repo_id: 1 + owner_id: 1 + commit_sha: 985f0301dba5e7b34be866819cd15ad3d8f508ee + is_fork_pull_request: 0 + name: job_2 + attempt: 1 + job_id: job_2 + task_id: 47 + status: 1 + started: 1683636528 + stopped: 1683636626 diff --git a/models/fixtures/lfs_meta_object.yml b/models/fixtures/lfs_meta_object.yml index 1c29e02d44..cef4824d64 100644 --- a/models/fixtures/lfs_meta_object.yml +++ b/models/fixtures/lfs_meta_object.yml @@ -9,7 +9,7 @@ - - id: 2 + id: 2 # this is an LFS orphan object oid: 2eccdb43825d2a49d99d542daa20075cff1d97d9d2349a8977efe9c03661737c size: 107 repository_id: 54 diff --git a/models/forgejo_migrations/migrate.go b/models/forgejo_migrations/migrate.go index c1a654cbde..3b6da74149 100644 --- a/models/forgejo_migrations/migrate.go +++ b/models/forgejo_migrations/migrate.go @@ -59,7 +59,13 @@ var migrations = []*Migration{ // v9 -> v10 NewMigration("Add pronouns to user", forgejo_v1_22.AddPronounsToUser), // v11 -> v12 + NewMigration("Add the `created` column to the `issue` table", forgejo_v1_22.AddCreatedToIssue), + // v12 -> v13 NewMigration("Add repo_archive_download_count table", forgejo_v1_22.AddRepoArchiveDownloadCount), + // v13 -> v14 + NewMigration("Add `hide_archive_links` column to `release` table", AddHideArchiveLinksToRelease), + // v14 -> v15 + NewMigration("Remove Gitea-specific columns from the repository and badge tables", RemoveGiteaSpecificColumnsFromRepositoryAndBadge), } // GetCurrentDBVersion returns the current Forgejo database version. diff --git a/models/forgejo_migrations/v13.go b/models/forgejo_migrations/v13.go new file mode 100644 index 0000000000..614f68249d --- /dev/null +++ b/models/forgejo_migrations/v13.go @@ -0,0 +1,15 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import "xorm.io/xorm" + +func AddHideArchiveLinksToRelease(x *xorm.Engine) error { + type Release struct { + ID int64 `xorm:"pk autoincr"` + HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` + } + + return x.Sync(&Release{}) +} diff --git a/models/forgejo_migrations/v14.go b/models/forgejo_migrations/v14.go new file mode 100644 index 0000000000..f6dd35ecf0 --- /dev/null +++ b/models/forgejo_migrations/v14.go @@ -0,0 +1,43 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package forgejo_migrations //nolint:revive + +import ( + "code.gitea.io/gitea/models/migrations/base" + + "xorm.io/xorm" +) + +func RemoveGiteaSpecificColumnsFromRepositoryAndBadge(x *xorm.Engine) error { + // Make sure the columns exist before dropping them + type Repository struct { + ID int64 + DefaultWikiBranch string + } + if err := x.Sync(&Repository{}); err != nil { + return err + } + + type Badge struct { + ID int64 `xorm:"pk autoincr"` + Slug string + } + err := x.Sync(new(Badge)) + if err != nil { + return err + } + + sess := x.NewSession() + defer sess.Close() + if err := sess.Begin(); err != nil { + return err + } + if err := base.DropTableColumns(sess, "repository", "default_wiki_branch"); err != nil { + return err + } + if err := base.DropTableColumns(sess, "badge", "slug"); err != nil { + return err + } + return sess.Commit() +} diff --git a/models/forgejo_migrations/v1_22/v11.go b/models/forgejo_migrations/v1_22/v11.go index 6822524705..c693993565 100644 --- a/models/forgejo_migrations/v1_22/v11.go +++ b/models/forgejo_migrations/v1_22/v11.go @@ -3,16 +3,17 @@ package v1_22 //nolint -import "xorm.io/xorm" +import ( + "code.gitea.io/gitea/modules/timeutil" -func AddRepoArchiveDownloadCount(x *xorm.Engine) error { - type RepoArchiveDownloadCount struct { - ID int64 `xorm:"pk autoincr"` - RepoID int64 `xorm:"index unique(s)"` - ReleaseID int64 `xorm:"index unique(s)"` - Type int `xorm:"unique(s)"` - Count int64 + "xorm.io/xorm" +) + +func AddCreatedToIssue(x *xorm.Engine) error { + type Issue struct { + ID int64 `xorm:"pk autoincr"` + Created timeutil.TimeStampNano } - return x.Sync(&RepoArchiveDownloadCount{}) + return x.Sync(&Issue{}) } diff --git a/models/forgejo_migrations/v1_22/v12.go b/models/forgejo_migrations/v1_22/v12.go new file mode 100644 index 0000000000..6822524705 --- /dev/null +++ b/models/forgejo_migrations/v1_22/v12.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_22 //nolint + +import "xorm.io/xorm" + +func AddRepoArchiveDownloadCount(x *xorm.Engine) error { + type RepoArchiveDownloadCount struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"index unique(s)"` + ReleaseID int64 `xorm:"index unique(s)"` + Type int `xorm:"unique(s)"` + Count int64 + } + + return x.Sync(&RepoArchiveDownloadCount{}) +} diff --git a/models/git/branch.go b/models/git/branch.go index 0e2febce3b..7e1c96d769 100644 --- a/models/git/branch.go +++ b/models/git/branch.go @@ -301,6 +301,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str sess := db.GetEngine(ctx) + // check whether from branch exist var branch Branch exist, err := db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, from).Get(&branch) if err != nil { @@ -312,6 +313,24 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str } } + // check whether to branch exist or is_deleted + var dstBranch Branch + exist, err = db.GetEngine(ctx).Where("repo_id=? AND name=?", repo.ID, to).Get(&dstBranch) + if err != nil { + return err + } + if exist { + if !dstBranch.IsDeleted { + return ErrBranchAlreadyExists{ + BranchName: to, + } + } + + if _, err := db.GetEngine(ctx).ID(dstBranch.ID).NoAutoCondition().Delete(&dstBranch); err != nil { + return err + } + } + // 1. update branch in database if n, err := sess.Where("repo_id=? AND name=?", repo.ID, from).Update(&Branch{ Name: to, @@ -366,12 +385,7 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str return err } - // 5. do git action - if err = gitAction(ctx, isDefault); err != nil { - return err - } - - // 6. insert renamed branch record + // 5. insert renamed branch record renamedBranch := &RenamedBranch{ RepoID: repo.ID, From: from, @@ -382,6 +396,11 @@ func RenameBranch(ctx context.Context, repo *repo_model.Repository, from, to str return err } + // 6. do git action + if err = gitAction(ctx, isDefault); err != nil { + return err + } + return committer.Commit() } diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 8319e5ecd0..980bd7b4c9 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -17,15 +17,12 @@ import ( type BranchList []*Branch func (branches BranchList) LoadDeletedBy(ctx context.Context) error { - ids := container.Set[int64]{} - for _, branch := range branches { - if !branch.IsDeleted { - continue - } - ids.Add(branch.DeletedByID) - } + ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) { + return branch.DeletedByID, branch.IsDeleted + }) + usersMap := make(map[int64]*user_model.User, len(ids)) - if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil { + if err := db.GetEngine(ctx).In("id", ids).Find(&usersMap); err != nil { return err } for _, branch := range branches { @@ -41,14 +38,13 @@ func (branches BranchList) LoadDeletedBy(ctx context.Context) error { } func (branches BranchList) LoadPusher(ctx context.Context) error { - ids := container.Set[int64]{} - for _, branch := range branches { - if branch.PusherID > 0 { // pusher_id maybe zero because some branches are sync by backend with no pusher - ids.Add(branch.PusherID) - } - } + ids := container.FilterSlice(branches, func(branch *Branch) (int64, bool) { + // pusher_id maybe zero because some branches are sync by backend with no pusher + return branch.PusherID, branch.PusherID > 0 + }) + usersMap := make(map[int64]*user_model.User, len(ids)) - if err := db.GetEngine(ctx).In("id", ids.Values()).Find(&usersMap); err != nil { + if err := db.GetEngine(ctx).In("id", ids).Find(&usersMap); err != nil { return err } for _, branch := range branches { diff --git a/models/git/commit_status.go b/models/git/commit_status.go index bec819348a..3f2172e50a 100644 --- a/models/git/commit_status.go +++ b/models/git/commit_status.go @@ -257,30 +257,27 @@ func GetLatestCommitStatus(ctx context.Context, repoID int64, sha string, listOp } // GetLatestCommitStatusForPairs returns all statuses with a unique context for a given list of repo-sha pairs -func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHAs map[int64]string, listOptions db.ListOptions) (map[int64][]*CommitStatus, error) { +func GetLatestCommitStatusForPairs(ctx context.Context, repoSHAs []RepoSHA) (map[int64][]*CommitStatus, error) { type result struct { Index int64 RepoID int64 + SHA string } - results := make([]result, 0, len(repoIDsToLatestCommitSHAs)) + results := make([]result, 0, len(repoSHAs)) getBase := func() *xorm.Session { return db.GetEngine(ctx).Table(&CommitStatus{}) } // Create a disjunction of conditions for each repoID and SHA pair - conds := make([]builder.Cond, 0, len(repoIDsToLatestCommitSHAs)) - for repoID, sha := range repoIDsToLatestCommitSHAs { - conds = append(conds, builder.Eq{"repo_id": repoID, "sha": sha}) + conds := make([]builder.Cond, 0, len(repoSHAs)) + for _, repoSHA := range repoSHAs { + conds = append(conds, builder.Eq{"repo_id": repoSHA.RepoID, "sha": repoSHA.SHA}) } sess := getBase().Where(builder.Or(conds...)). - Select("max( `index` ) as `index`, repo_id"). - GroupBy("context_hash, repo_id").OrderBy("max( `index` ) desc") - - if !listOptions.IsListAll() { - sess = db.SetSessionPagination(sess, &listOptions) - } + Select("max( `index` ) as `index`, repo_id, sha"). + GroupBy("context_hash, repo_id, sha").OrderBy("max( `index` ) desc") err := sess.Find(&results) if err != nil { @@ -297,7 +294,7 @@ func GetLatestCommitStatusForPairs(ctx context.Context, repoIDsToLatestCommitSHA cond := builder.Eq{ "`index`": result.Index, "repo_id": result.RepoID, - "sha": repoIDsToLatestCommitSHAs[result.RepoID], + "sha": result.SHA, } conds = append(conds, cond) } diff --git a/models/git/commit_status_summary.go b/models/git/commit_status_summary.go new file mode 100644 index 0000000000..7603e7aa65 --- /dev/null +++ b/models/git/commit_status_summary.go @@ -0,0 +1,88 @@ +// Copyright 2024 Gitea. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "context" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" + + "xorm.io/builder" +) + +// CommitStatusSummary holds the latest commit Status of a single Commit +type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` + SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` + State api.CommitStatusState `xorm:"VARCHAR(7) NOT NULL"` + TargetURL string `xorm:"TEXT"` +} + +func init() { + db.RegisterModel(new(CommitStatusSummary)) +} + +type RepoSHA struct { + RepoID int64 + SHA string +} + +func GetLatestCommitStatusForRepoAndSHAs(ctx context.Context, repoSHAs []RepoSHA) ([]*CommitStatus, error) { + cond := builder.NewCond() + for _, rs := range repoSHAs { + cond = cond.Or(builder.Eq{"repo_id": rs.RepoID, "sha": rs.SHA}) + } + + var summaries []CommitStatusSummary + if err := db.GetEngine(ctx).Where(cond).Find(&summaries); err != nil { + return nil, err + } + + commitStatuses := make([]*CommitStatus, 0, len(repoSHAs)) + for _, summary := range summaries { + commitStatuses = append(commitStatuses, &CommitStatus{ + RepoID: summary.RepoID, + SHA: summary.SHA, + State: summary.State, + TargetURL: summary.TargetURL, + }) + } + return commitStatuses, nil +} + +func UpdateCommitStatusSummary(ctx context.Context, repoID int64, sha string) error { + commitStatuses, _, err := GetLatestCommitStatus(ctx, repoID, sha, db.ListOptionsAll) + if err != nil { + return err + } + state := CalcCommitStatus(commitStatuses) + // mysql will return 0 when update a record which state hasn't been changed which behaviour is different from other database, + // so we need to use insert in on duplicate + if setting.Database.Type.IsMySQL() { + _, err := db.GetEngine(ctx).Exec("INSERT INTO commit_status_summary (repo_id,sha,state,target_url) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE state=?", + repoID, sha, state.State, state.TargetURL, state.State) + return err + } + + if cnt, err := db.GetEngine(ctx).Where("repo_id=? AND sha=?", repoID, sha). + Cols("state, target_url"). + Update(&CommitStatusSummary{ + State: state.State, + TargetURL: state.TargetURL, + }); err != nil { + return err + } else if cnt == 0 { + _, err = db.GetEngine(ctx).Insert(&CommitStatusSummary{ + RepoID: repoID, + SHA: sha, + State: state.State, + TargetURL: state.TargetURL, + }) + return err + } + return nil +} diff --git a/models/issues/comment.go b/models/issues/comment.go index 1e962b52f7..e4b5ed12cd 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -1289,10 +1289,9 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error { return nil } - issueIDs := make(container.Set[int64]) - for _, comment := range comments { - issueIDs.Add(comment.IssueID) - } + issueIDs := container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.IssueID, true + }) ctx, committer, err := db.TxContext(ctx) if err != nil { @@ -1315,7 +1314,7 @@ func InsertIssueComments(ctx context.Context, comments []*Comment) error { } } - for issueID := range issueIDs { + for _, issueID := range issueIDs { if _, err := db.Exec(ctx, "UPDATE issue set num_comments = (SELECT count(*) FROM comment WHERE issue_id = ? AND `type`=?) WHERE id = ?", issueID, CommentTypeComment, issueID); err != nil { return err diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 347dbe99e5..370b5396e0 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -17,13 +17,9 @@ import ( type CommentList []*Comment func (comments CommentList) getPosterIDs() []int64 { - posterIDs := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.PosterID > 0 { - posterIDs.Add(comment.PosterID) - } - } - return posterIDs.Values() + return container.FilterSlice(comments, func(c *Comment) (int64, bool) { + return c.PosterID, c.PosterID > 0 + }) } // LoadPosters loads posters @@ -44,13 +40,9 @@ func (comments CommentList) LoadPosters(ctx context.Context) error { } func (comments CommentList) getLabelIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.LabelID > 0 { - ids.Add(comment.LabelID) - } - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.LabelID, comment.LabelID > 0 + }) } func (comments CommentList) loadLabels(ctx context.Context) error { @@ -94,13 +86,9 @@ func (comments CommentList) loadLabels(ctx context.Context) error { } func (comments CommentList) getMilestoneIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.MilestoneID > 0 { - ids.Add(comment.MilestoneID) - } - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.MilestoneID, comment.MilestoneID > 0 + }) } func (comments CommentList) loadMilestones(ctx context.Context) error { @@ -137,13 +125,9 @@ func (comments CommentList) loadMilestones(ctx context.Context) error { } func (comments CommentList) getOldMilestoneIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.OldMilestoneID > 0 { - ids.Add(comment.OldMilestoneID) - } - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.OldMilestoneID, comment.OldMilestoneID > 0 + }) } func (comments CommentList) loadOldMilestones(ctx context.Context) error { @@ -180,13 +164,9 @@ func (comments CommentList) loadOldMilestones(ctx context.Context) error { } func (comments CommentList) getAssigneeIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.AssigneeID > 0 { - ids.Add(comment.AssigneeID) - } - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.AssigneeID, comment.AssigneeID > 0 + }) } func (comments CommentList) loadAssignees(ctx context.Context) error { @@ -237,14 +217,9 @@ func (comments CommentList) loadAssignees(ctx context.Context) error { // getIssueIDs returns all the issue ids on this comment list which issue hasn't been loaded func (comments CommentList) getIssueIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.Issue != nil { - continue - } - ids.Add(comment.IssueID) - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.IssueID, comment.Issue == nil + }) } // Issues returns all the issues of comments @@ -311,16 +286,12 @@ func (comments CommentList) LoadIssues(ctx context.Context) error { } func (comments CommentList) getDependentIssueIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { if comment.DependentIssue != nil { - continue + return 0, false } - if comment.DependentIssueID > 0 { - ids.Add(comment.DependentIssueID) - } - } - return ids.Values() + return comment.DependentIssueID, comment.DependentIssueID > 0 + }) } func (comments CommentList) loadDependentIssues(ctx context.Context) error { @@ -375,13 +346,9 @@ func (comments CommentList) loadDependentIssues(ctx context.Context) error { // getAttachmentCommentIDs only return the comment ids which possibly has attachments func (comments CommentList) getAttachmentCommentIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.Type.HasAttachmentSupport() { - ids.Add(comment.ID) - } - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.ID, comment.Type.HasAttachmentSupport() + }) } // LoadAttachmentsByIssue loads attachments by issue id @@ -449,13 +416,9 @@ func (comments CommentList) LoadAttachments(ctx context.Context) (err error) { } func (comments CommentList) getReviewIDs() []int64 { - ids := make(container.Set[int64], len(comments)) - for _, comment := range comments { - if comment.ReviewID > 0 { - ids.Add(comment.ReviewID) - } - } - return ids.Values() + return container.FilterSlice(comments, func(comment *Comment) (int64, bool) { + return comment.ReviewID, comment.ReviewID > 0 + }) } func (comments CommentList) loadReviews(ctx context.Context) error { diff --git a/models/issues/issue.go b/models/issues/issue.go index 11256f788a..affd581929 100644 --- a/models/issues/issue.go +++ b/models/issues/issue.go @@ -124,6 +124,8 @@ type Issue struct { DeadlineUnix timeutil.TimeStamp `xorm:"INDEX"` + Created timeutil.TimeStampNano + CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"` UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"` ClosedUnix timeutil.TimeStamp `xorm:"INDEX"` diff --git a/models/issues/issue_index.go b/models/issues/issue_index.go index 16274d0ef0..9386027f74 100644 --- a/models/issues/issue_index.go +++ b/models/issues/issue_index.go @@ -9,6 +9,14 @@ import ( "code.gitea.io/gitea/models/db" ) +func GetMaxIssueIndexForRepo(ctx context.Context, repoID int64) (int64, error) { + var max int64 + if _, err := db.GetEngine(ctx).Select("MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { + return 0, err + } + return max, nil +} + // RecalculateIssueIndexForRepo create issue_index for repo if not exist and // update it based on highest index of existing issues assigned to a repo func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error { @@ -18,8 +26,8 @@ func RecalculateIssueIndexForRepo(ctx context.Context, repoID int64) error { } defer committer.Close() - var max int64 - if _, err = db.GetEngine(ctx).Select(" MAX(`index`)").Table("issue").Where("repo_id=?", repoID).Get(&max); err != nil { + max, err := GetMaxIssueIndexForRepo(ctx, repoID) + if err != nil { return err } diff --git a/models/issues/issue_index_test.go b/models/issues/issue_index_test.go new file mode 100644 index 0000000000..9937aac70e --- /dev/null +++ b/models/issues/issue_index_test.go @@ -0,0 +1,38 @@ +// Copyright 2024 The Forgejo Authors +// SPDX-License-Identifier: MIT + +package issues_test + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + + "github.com/stretchr/testify/assert" +) + +func TestGetMaxIssueIndexForRepo(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + + maxPR, err := issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID) + assert.NoError(t, err) + + issue := testCreateIssue(t, repo.ID, repo.OwnerID, "title1", "content1", false) + assert.Greater(t, issue.Index, maxPR) + + maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID) + assert.NoError(t, err) + + pull := testCreateIssue(t, repo.ID, repo.OwnerID, "title2", "content2", true) + assert.Greater(t, pull.Index, maxPR) + + maxPR, err = issues_model.GetMaxIssueIndexForRepo(db.DefaultContext, repo.ID) + assert.NoError(t, err) + + assert.Equal(t, maxPR, pull.Index) +} diff --git a/models/issues/issue_list.go b/models/issues/issue_list.go index da55ff1b09..2235f3d3a8 100644 --- a/models/issues/issue_list.go +++ b/models/issues/issue_list.go @@ -74,11 +74,9 @@ func (issues IssueList) LoadRepositories(ctx context.Context) (repo_model.Reposi } func (issues IssueList) getPosterIDs() []int64 { - posterIDs := make(container.Set[int64], len(issues)) - for _, issue := range issues { - posterIDs.Add(issue.PosterID) - } - return posterIDs.Values() + return container.FilterSlice(issues, func(issue *Issue) (int64, bool) { + return issue.PosterID, true + }) } func (issues IssueList) loadPosters(ctx context.Context) error { @@ -193,11 +191,9 @@ func (issues IssueList) loadLabels(ctx context.Context) error { } func (issues IssueList) getMilestoneIDs() []int64 { - ids := make(container.Set[int64], len(issues)) - for _, issue := range issues { - ids.Add(issue.MilestoneID) - } - return ids.Values() + return container.FilterSlice(issues, func(issue *Issue) (int64, bool) { + return issue.MilestoneID, true + }) } func (issues IssueList) loadMilestones(ctx context.Context) error { diff --git a/models/issues/issue_update.go b/models/issues/issue_update.go index f20d552a1b..78e1f8e030 100644 --- a/models/issues/issue_update.go +++ b/models/issues/issue_update.go @@ -325,6 +325,8 @@ func NewIssueWithIndex(ctx context.Context, doer *user_model.User, opts NewIssue return fmt.Errorf("issue exist") } + opts.Issue.Created = timeutil.TimeStampNanoNow() + if _, err := e.Insert(opts.Issue); err != nil { return err } diff --git a/models/issues/pull_list.go b/models/issues/pull_list.go index de3eceed37..61b4168ea2 100644 --- a/models/issues/pull_list.go +++ b/models/issues/pull_list.go @@ -47,6 +47,14 @@ func listPullRequestStatement(ctx context.Context, baseRepoID int64, opts *PullR return sess, nil } +func GetUnmergedPullRequestsByHeadInfoMax(ctx context.Context, repoID, olderThan int64, branch string) ([]*PullRequest, error) { + prs := make([]*PullRequest, 0, 2) + sess := db.GetEngine(ctx). + Join("INNER", "issue", "issue.id = `pull_request`.issue_id"). + Where("`pull_request`.head_repo_id = ? AND `pull_request`.head_branch = ? AND `pull_request`.has_merged = ? AND `issue`.is_closed = ? AND `pull_request`.flow = ? AND (`issue`.`created` IS NULL OR `issue`.`created` <= ?)", repoID, branch, false, false, PullRequestFlowGithub, olderThan) + return prs, sess.Find(&prs) +} + // GetUnmergedPullRequestsByHeadInfo returns all pull requests that are open and has not been merged func GetUnmergedPullRequestsByHeadInfo(ctx context.Context, repoID int64, branch string) ([]*PullRequest, error) { prs := make([]*PullRequest, 0, 2) diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index 675c90527d..a9d4edc8a5 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -4,7 +4,9 @@ package issues_test import ( + "fmt" "testing" + "time" "code.gitea.io/gitea/models/db" issues_model "code.gitea.io/gitea/models/issues" @@ -12,6 +14,7 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) @@ -156,6 +159,100 @@ func TestGetUnmergedPullRequestsByHeadInfo(t *testing.T) { } } +func TestGetUnmergedPullRequestsByHeadInfoMax(t *testing.T) { + defer tests.AddFixtures("models/fixtures/TestGetUnmergedPullRequestsByHeadInfoMax/")() + assert.NoError(t, unittest.PrepareTestDatabase()) + + repoID := int64(1) + olderThan := int64(0) + + // for NULL created field the olderThan condition is ignored + prs, err := issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, "branch2") + assert.NoError(t, err) + assert.Equal(t, int64(1), prs[0].HeadRepoID) + + // test for when the created field is set + branch := "branchmax" + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) + assert.NoError(t, err) + assert.Len(t, prs, 0) + olderThan = time.Now().UnixNano() + assert.NoError(t, err) + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) + assert.NoError(t, err) + assert.Len(t, prs, 1) + for _, pr := range prs { + assert.Equal(t, int64(1), pr.HeadRepoID) + assert.Equal(t, branch, pr.HeadBranch) + } + pr := prs[0] + + for _, testCase := range []struct { + table string + field string + id int64 + match any + nomatch any + }{ + { + table: "issue", + field: "is_closed", + id: pr.IssueID, + match: false, + nomatch: true, + }, + { + table: "pull_request", + field: "flow", + id: pr.ID, + match: issues_model.PullRequestFlowGithub, + nomatch: issues_model.PullRequestFlowAGit, + }, + { + table: "pull_request", + field: "head_repo_id", + id: pr.ID, + match: pr.HeadRepoID, + nomatch: 0, + }, + { + table: "pull_request", + field: "head_branch", + id: pr.ID, + match: pr.HeadBranch, + nomatch: "something else", + }, + { + table: "pull_request", + field: "has_merged", + id: pr.ID, + match: false, + nomatch: true, + }, + } { + t.Run(testCase.field, func(t *testing.T) { + update := fmt.Sprintf("UPDATE `%s` SET `%s` = ? WHERE `id` = ?", testCase.table, testCase.field) + + // expect no match + _, err = db.GetEngine(db.DefaultContext).Exec(update, testCase.nomatch, testCase.id) + assert.NoError(t, err) + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) + assert.NoError(t, err) + assert.Len(t, prs, 0) + + // expect one match + _, err = db.GetEngine(db.DefaultContext).Exec(update, testCase.match, testCase.id) + assert.NoError(t, err) + prs, err = issues_model.GetUnmergedPullRequestsByHeadInfoMax(db.DefaultContext, repoID, olderThan, branch) + assert.NoError(t, err) + assert.Len(t, prs, 1) + + // identical to the known PR + assert.Equal(t, pr.ID, prs[0].ID) + }) + } +} + func TestGetUnmergedPullRequestsByBaseInfo(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) prs, err := issues_model.GetUnmergedPullRequestsByBaseInfo(db.DefaultContext, 1, "master") diff --git a/models/issues/reaction.go b/models/issues/reaction.go index d5448636fe..eb7faefc79 100644 --- a/models/issues/reaction.go +++ b/models/issues/reaction.go @@ -305,14 +305,12 @@ func (list ReactionList) GroupByType() map[string]ReactionList { } func (list ReactionList) getUserIDs() []int64 { - userIDs := make(container.Set[int64], len(list)) - for _, reaction := range list { + return container.FilterSlice(list, func(reaction *Reaction) (int64, bool) { if reaction.OriginalAuthor != "" { - continue + return 0, false } - userIDs.Add(reaction.UserID) - } - return userIDs.Values() + return reaction.UserID, true + }) } func valuesUser(m map[int64]*user_model.User) []*user_model.User { diff --git a/models/issues/review_list.go b/models/issues/review_list.go index ec6cb07988..7b8c3d319c 100644 --- a/models/issues/review_list.go +++ b/models/issues/review_list.go @@ -38,12 +38,11 @@ func (reviews ReviewList) LoadReviewers(ctx context.Context) error { } func (reviews ReviewList) LoadIssues(ctx context.Context) error { - issueIDs := container.Set[int64]{} - for i := 0; i < len(reviews); i++ { - issueIDs.Add(reviews[i].IssueID) - } + issueIDs := container.FilterSlice(reviews, func(review *Review) (int64, bool) { + return review.IssueID, true + }) - issues, err := GetIssuesByIDs(ctx, issueIDs.Values()) + issues, err := GetIssuesByIDs(ctx, issueIDs) if err != nil { return err } diff --git a/models/migrations/base/tests.go b/models/migrations/base/tests.go index 0726211ea8..0989902a65 100644 --- a/models/migrations/base/tests.go +++ b/models/migrations/base/tests.go @@ -161,8 +161,7 @@ func MainTest(m *testing.M) { exitStatus := m.Run() if err := testlogger.WriterCloser.Reset(); err != nil && exitStatus == 0 { - fmt.Printf("testlogger.WriterCloser.Reset: %v\n", err) - os.Exit(1) + fmt.Printf("testlogger.WriterCloser.Reset: error ignored: %v\n", err) } if err := removeAllWithRetry(setting.RepoRootPath); err != nil { fmt.Fprintf(os.Stderr, "os.RemoveAll: %v\n", err) diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 88a34bd4ae..7146c49676 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -578,7 +578,12 @@ var migrations = []Migration{ // Gitea 1.22.0 ends at 294 + // v294 -> v295 NewMigration("Add unique index for project issue table", v1_23.AddUniqueIndexForProjectIssue), + // v295 -> v296 + NewMigration("Add commit status summary table", v1_23.AddCommitStatusSummary), + // v296 -> v297 + NewMigration("Add missing field of commit status summary table", v1_23.AddCommitStatusSummary2), } // GetCurrentDBVersion returns the current db version @@ -655,15 +660,15 @@ func Migrate(x *xorm.Engine) error { v := currentVersion.Version if minDBVersion > v { - log.Fatal(`Gitea no longer supports auto-migration from your previously installed version. + log.Fatal(`Forgejo no longer supports auto-migration from your previously installed version. Please try upgrading to a lower version first (suggested v1.6.4), then upgrade to this version.`) return nil } - // Downgrading Gitea's database version not supported + // Downgrading Forgejo database version is not supported if int(v-minDBVersion) > len(migrations) { - msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Gitea, you can not use the newer database for this old Gitea release (%d).", v, minDBVersion+len(migrations)) - msg += "\nGitea will exit to keep your database safe and unchanged. Please use the correct Gitea release, do not change the migration version manually (incorrect manual operation may lose data)." + msg := fmt.Sprintf("Your database (migration version: %d) is for a newer Forgejo, you can not use the newer database for this old Forgejo release (%d).", v, minDBVersion+len(migrations)) + msg += "\nForgejo will exit to keep your database safe and unchanged. Please use the correct Forgejo release, do not change the migration version manually (incorrect manual operation may lose data)." if !setting.IsProd { msg += fmt.Sprintf("\nIf you are in development and really know what you're doing, you can force changing the migration version by executing: UPDATE version SET version=%d WHERE id=1;", minDBVersion+len(migrations)) } diff --git a/models/migrations/v1_23/v295.go b/models/migrations/v1_23/v295.go new file mode 100644 index 0000000000..9a2003cfc1 --- /dev/null +++ b/models/migrations/v1_23/v295.go @@ -0,0 +1,18 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import "xorm.io/xorm" + +func AddCommitStatusSummary(x *xorm.Engine) error { + type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + RepoID int64 `xorm:"INDEX UNIQUE(repo_id_sha)"` + SHA string `xorm:"VARCHAR(64) NOT NULL INDEX UNIQUE(repo_id_sha)"` + State string `xorm:"VARCHAR(7) NOT NULL"` + } + // there is no migrations because if there is no data on this table, it will fall back to get data + // from commit status + return x.Sync2(new(CommitStatusSummary)) +} diff --git a/models/migrations/v1_23/v296.go b/models/migrations/v1_23/v296.go new file mode 100644 index 0000000000..495ae2ab23 --- /dev/null +++ b/models/migrations/v1_23/v296.go @@ -0,0 +1,16 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import "xorm.io/xorm" + +func AddCommitStatusSummary2(x *xorm.Engine) error { + type CommitStatusSummary struct { + ID int64 `xorm:"pk autoincr"` + TargetURL string `xorm:"TEXT"` + } + // there is no migrations because if there is no data on this table, it will fall back to get data + // from commit status + return x.Sync(new(CommitStatusSummary)) +} diff --git a/models/organization/org.go b/models/organization/org.go index 47230bdc36..45f19c7696 100644 --- a/models/organization/org.go +++ b/models/organization/org.go @@ -9,6 +9,7 @@ import ( "fmt" "strings" + actions_model "code.gitea.io/gitea/models/actions" "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" @@ -401,6 +402,8 @@ func DeleteOrganization(ctx context.Context, org *Organization) error { &TeamUnit{OrgID: org.ID}, &TeamInvite{OrgID: org.ID}, &secret_model.Secret{OwnerID: org.ID}, + &actions_model.ActionRunner{OwnerID: org.ID}, + &actions_model.ActionRunnerToken{OwnerID: org.ID}, ); err != nil { return fmt.Errorf("DeleteBeans: %w", err) } diff --git a/models/packages/package_version.go b/models/packages/package_version.go index 505dbaa0a5..278e8e3a86 100644 --- a/models/packages/package_version.go +++ b/models/packages/package_version.go @@ -287,9 +287,10 @@ func (opts *PackageSearchOptions) configureOrderBy(e db.Engine) { // SearchVersions gets all versions of packages matching the search options func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) { sess := db.GetEngine(ctx). - Where(opts.ToConds()). + Select("package_version.*"). Table("package_version"). - Join("INNER", "package", "package.id = package_version.package_id") + Join("INNER", "package", "package.id = package_version.package_id"). + Where(opts.ToConds()) opts.configureOrderBy(sess) @@ -304,19 +305,18 @@ func SearchVersions(ctx context.Context, opts *PackageSearchOptions) ([]*Package // SearchLatestVersions gets the latest version of every package matching the search options func SearchLatestVersions(ctx context.Context, opts *PackageSearchOptions) ([]*PackageVersion, int64, error) { - cond := opts.ToConds(). - And(builder.Expr("pv2.id IS NULL")) - - joinCond := builder.Expr("package_version.package_id = pv2.package_id AND (package_version.created_unix < pv2.created_unix OR (package_version.created_unix = pv2.created_unix AND package_version.id < pv2.id))") - if opts.IsInternal.Has() { - joinCond = joinCond.And(builder.Eq{"pv2.is_internal": opts.IsInternal.Value()}) - } + in := builder. + Select("MAX(package_version.id)"). + From("package_version"). + InnerJoin("package", "package.id = package_version.package_id"). + Where(opts.ToConds()). + GroupBy("package_version.package_id") sess := db.GetEngine(ctx). + Select("package_version.*"). Table("package_version"). - Join("LEFT", "package_version pv2", joinCond). Join("INNER", "package", "package.id = package_version.package_id"). - Where(cond) + Where(builder.In("package_version.id", in)) opts.configureOrderBy(sess) diff --git a/models/repo/issue.go b/models/repo/issue.go index 6f6b565a00..0dd4fd5ed4 100644 --- a/models/repo/issue.go +++ b/models/repo/issue.go @@ -53,7 +53,7 @@ func (repo *Repository) IsDependenciesEnabled(ctx context.Context) bool { var u *RepoUnit var err error if u, err = repo.GetUnit(ctx, unit.TypeIssues); err != nil { - log.Trace("%s", err) + log.Trace("IsDependenciesEnabled: %v", err) return setting.Service.DefaultEnableDependencies } return u.IssuesConfig().EnableDependencies diff --git a/models/repo/release.go b/models/repo/release.go index 3168bdaaea..075e287174 100644 --- a/models/repo/release.go +++ b/models/repo/release.go @@ -78,6 +78,7 @@ type Release struct { TargetBehind string `xorm:"-"` // to handle non-existing or empty target Title string Sha1 string `xorm:"VARCHAR(64)"` + HideArchiveLinks bool `xorm:"NOT NULL DEFAULT false"` NumCommits int64 NumCommitsBehind int64 `xorm:"-"` Note string `xorm:"TEXT"` diff --git a/models/repo/repo_list.go b/models/repo/repo_list.go index cb7cd47a8d..987c7df9b0 100644 --- a/models/repo/repo_list.go +++ b/models/repo/repo_list.go @@ -104,18 +104,19 @@ func (repos RepositoryList) LoadAttributes(ctx context.Context) error { return nil } - set := make(container.Set[int64]) + userIDs := container.FilterSlice(repos, func(repo *Repository) (int64, bool) { + return repo.OwnerID, true + }) repoIDs := make([]int64, len(repos)) for i := range repos { - set.Add(repos[i].OwnerID) repoIDs[i] = repos[i].ID } // Load owners. - users := make(map[int64]*user_model.User, len(set)) + users := make(map[int64]*user_model.User, len(userIDs)) if err := db.GetEngine(ctx). Where("id > 0"). - In("id", set.Values()). + In("id", userIDs). Find(&users); err != nil { return fmt.Errorf("find users: %w", err) } diff --git a/models/user/fixtures/user.yml b/models/user/fixtures/user.yml new file mode 100644 index 0000000000..b1892f331b --- /dev/null +++ b/models/user/fixtures/user.yml @@ -0,0 +1,36 @@ +- + id: 1041 + lower_name: remote01 + name: remote01 + full_name: Remote01 + email: remote01@example.com + keep_email_private: false + email_notifications_preference: onmention + passwd: ZogKvWdyEx:password + passwd_hash_algo: dummy + must_change_password: false + login_source: 1001 + login_name: 123 + type: 5 + salt: ZogKvWdyEx + max_repo_creation: -1 + is_active: true + is_admin: false + is_restricted: false + allow_git_hook: false + allow_import_local: false + allow_create_organization: true + prohibit_login: true + avatar: avatarremote01 + avatar_email: avatarremote01@example.com + use_custom_avatar: false + num_followers: 0 + num_following: 0 + num_stars: 0 + num_repos: 0 + num_teams: 0 + num_members: 0 + visibility: 0 + repo_admin_change_team_access: false + theme: "" + keep_activity_private: false diff --git a/models/user/search.go b/models/user/search.go index 45b051187e..04c434e4fa 100644 --- a/models/user/search.go +++ b/models/user/search.go @@ -45,7 +45,11 @@ type SearchUserOptions struct { func (opts *SearchUserOptions) toSearchQueryBase(ctx context.Context) *xorm.Session { var cond builder.Cond - cond = builder.Eq{"type": opts.Type} + if opts.Type == UserTypeIndividual { + cond = builder.In("type", UserTypeIndividual, UserTypeRemoteUser) + } else { + cond = builder.Eq{"type": opts.Type} + } if opts.IncludeReserved { if opts.Type == UserTypeIndividual { cond = cond.Or(builder.Eq{"type": UserTypeUserReserved}).Or( @@ -140,7 +144,7 @@ func SearchUsers(ctx context.Context, opts *SearchUserOptions) (users []*User, _ sessQuery := opts.toSearchQueryBase(ctx).OrderBy(opts.OrderBy.String()) defer sessQuery.Close() - if opts.Page != 0 { + if opts.PageSize > 0 { sessQuery = db.SetSessionPagination(sessQuery, opts) } diff --git a/models/user/user.go b/models/user/user.go index 0a48f25176..bedd7b56fc 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -220,7 +220,7 @@ func (u *User) GetEmail() string { // GetAllUsers returns a slice of all individual users found in DB. func GetAllUsers(ctx context.Context) ([]*User, error) { users := make([]*User, 0) - return users, db.GetEngine(ctx).OrderBy("id").Where("type = ?", UserTypeIndividual).Find(&users) + return users, db.GetEngine(ctx).OrderBy("id").In("type", UserTypeIndividual, UserTypeRemoteUser).Find(&users) } // GetAllAdmins returns a slice of all adminusers found in DB. @@ -425,6 +425,10 @@ func (u *User) IsBot() bool { return u.Type == UserTypeBot } +func (u *User) IsRemote() bool { + return u.Type == UserTypeRemoteUser +} + // DisplayName returns full name if it's not empty, // returns username otherwise. func (u *User) DisplayName() string { @@ -938,7 +942,8 @@ func GetUserByName(ctx context.Context, name string) (*User, error) { if len(name) == 0 { return nil, ErrUserNotExist{Name: name} } - u := &User{LowerName: strings.ToLower(name), Type: UserTypeIndividual} + // adding Type: UserTypeIndividual is a noop because it is zero and discarded + u := &User{LowerName: strings.ToLower(name)} has, err := db.GetEngine(ctx).Get(u) if err != nil { return nil, err diff --git a/models/user/user_test.go b/models/user/user_test.go index 53b6bce6f3..b9d2f3ebb5 100644 --- a/models/user/user_test.go +++ b/models/user/user_test.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) @@ -34,6 +35,35 @@ func TestOAuth2Application_LoadUser(t *testing.T) { assert.NotNil(t, user) } +func TestGetUserByName(t *testing.T) { + defer tests.AddFixtures("models/user/fixtures/")() + assert.NoError(t, unittest.PrepareTestDatabase()) + + { + _, err := user_model.GetUserByName(db.DefaultContext, "") + assert.True(t, user_model.IsErrUserNotExist(err), err) + } + { + _, err := user_model.GetUserByName(db.DefaultContext, "UNKNOWN") + assert.True(t, user_model.IsErrUserNotExist(err), err) + } + { + user, err := user_model.GetUserByName(db.DefaultContext, "USER2") + assert.NoError(t, err) + assert.Equal(t, user.Name, "user2") + } + { + user, err := user_model.GetUserByName(db.DefaultContext, "org3") + assert.NoError(t, err) + assert.Equal(t, user.Name, "org3") + } + { + user, err := user_model.GetUserByName(db.DefaultContext, "remote01") + assert.NoError(t, err) + assert.Equal(t, user.Name, "remote01") + } +} + func TestGetUserEmailsByNames(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) @@ -62,6 +92,22 @@ func TestCanCreateOrganization(t *testing.T) { assert.False(t, user.CanCreateOrganization()) } +func TestGetAllUsers(t *testing.T) { + defer tests.AddFixtures("models/user/fixtures/")() + assert.NoError(t, unittest.PrepareTestDatabase()) + + users, err := user_model.GetAllUsers(db.DefaultContext) + assert.NoError(t, err) + + found := make(map[user_model.UserType]bool, 0) + for _, user := range users { + found[user.Type] = true + } + assert.True(t, found[user_model.UserTypeIndividual], users) + assert.True(t, found[user_model.UserTypeRemoteUser], users) + assert.False(t, found[user_model.UserTypeOrganization], users) +} + func TestAPAPIURL(t *testing.T) { user := user_model.User{ID: 1} url := user.APAPIURL() @@ -72,6 +118,7 @@ func TestAPAPIURL(t *testing.T) { } func TestSearchUsers(t *testing.T) { + defer tests.AddFixtures("models/user/fixtures/")() assert.NoError(t, unittest.PrepareTestDatabase()) testSuccess := func(opts *user_model.SearchUserOptions, expectedUserOrOrgIDs []int64) { users, _, err := user_model.SearchUsers(db.DefaultContext, opts) @@ -112,13 +159,13 @@ func TestSearchUsers(t *testing.T) { } testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}}, - []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) + []int64{1, 2, 4, 5, 8, 9, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(false)}, []int64{9}) testUserSuccess(&user_model.SearchUserOptions{OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, - []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40}) + []int64{1, 2, 4, 5, 8, 10, 11, 12, 13, 14, 15, 16, 18, 20, 21, 24, 27, 28, 29, 30, 32, 34, 37, 38, 39, 40, 1041}) testUserSuccess(&user_model.SearchUserOptions{Keyword: "user1", OrderBy: "id ASC", ListOptions: db.ListOptions{Page: 1}, IsActive: optional.Some(true)}, []int64{1, 10, 11, 12, 13, 14, 15, 16, 18}) @@ -134,7 +181,7 @@ func TestSearchUsers(t *testing.T) { []int64{29}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsProhibitLogin: optional.Some(true)}, - []int64{37}) + []int64{1041, 37}) testUserSuccess(&user_model.SearchUserOptions{ListOptions: db.ListOptions{Page: 1}, IsTwoFactorEnabled: optional.Some(true)}, []int64{24}) diff --git a/models/webhook/webhook.go b/models/webhook/webhook.go index 4ab806573b..f3370f3db5 100644 --- a/models/webhook/webhook.go +++ b/models/webhook/webhook.go @@ -361,6 +361,15 @@ func (w Webhook) HeaderAuthorization() (string, error) { return secret.DecryptSecret(setting.SecretKey, w.HeaderAuthorizationEncrypted) } +// HeaderAuthorizationTrimPrefix returns the decrypted Authorization with a specified prefix trimmed. +func (w Webhook) HeaderAuthorizationTrimPrefix(prefix string) (string, error) { + s, err := w.HeaderAuthorization() + if err != nil { + return "", err + } + return strings.TrimPrefix(s, prefix), nil +} + // SetHeaderAuthorization encrypts and sets the Authorization header. func (w *Webhook) SetHeaderAuthorization(cleartext string) error { if cleartext == "" { diff --git a/models/webhook/webhook_system.go b/models/webhook/webhook_system.go index a2a9ee321a..62e8286205 100644 --- a/models/webhook/webhook_system.go +++ b/models/webhook/webhook_system.go @@ -8,15 +8,11 @@ import ( "fmt" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/optional" ) // GetDefaultWebhooks returns all admin-default webhooks. func GetDefaultWebhooks(ctx context.Context) ([]*Webhook, error) { - webhooks := make([]*Webhook, 0, 5) - return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, false). - Find(&webhooks) + return getAdminWebhooks(ctx, false) } // GetSystemOrDefaultWebhook returns admin system or default webhook by given ID. @@ -34,15 +30,21 @@ func GetSystemOrDefaultWebhook(ctx context.Context, id int64) (*Webhook, error) } // GetSystemWebhooks returns all admin system webhooks. -func GetSystemWebhooks(ctx context.Context, isActive optional.Option[bool]) ([]*Webhook, error) { +func GetSystemWebhooks(ctx context.Context, onlyActive bool) ([]*Webhook, error) { + return getAdminWebhooks(ctx, true, onlyActive) +} + +func getAdminWebhooks(ctx context.Context, systemWebhooks bool, onlyActive ...bool) ([]*Webhook, error) { webhooks := make([]*Webhook, 0, 5) - if !isActive.Has() { + if len(onlyActive) > 0 && onlyActive[0] { return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, true). + Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, systemWebhooks, true). + OrderBy("id"). Find(&webhooks) } return webhooks, db.GetEngine(ctx). - Where("repo_id=? AND owner_id=? AND is_system_webhook=? AND is_active = ?", 0, 0, true, isActive.Value()). + Where("repo_id=? AND owner_id=? AND is_system_webhook=?", 0, 0, systemWebhooks). + OrderBy("id"). Find(&webhooks) } diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go index 7442a80d7f..83dda16c53 100644 --- a/modules/charset/escape_test.go +++ b/modules/charset/escape_test.go @@ -130,7 +130,7 @@ then resh (ר), and finally heh (ה) (which should appear leftmost).`, }, { // UTF-8/16/32 all use the same codepoint for BOM - // Gitea could read UTF-16/32 content and convert into UTF-8 internally then render it, so we only process UTF-8 internally + // Forgejo could read UTF-16/32 content and convert into UTF-8 internally then render it, so we only process UTF-8 internally name: "UTF BOM", text: "\xef\xbb\xbftest", result: "\xef\xbb\xbftest", diff --git a/modules/container/filter.go b/modules/container/filter.go new file mode 100644 index 0000000000..37ec7c3d56 --- /dev/null +++ b/modules/container/filter.go @@ -0,0 +1,21 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package container + +import "slices" + +// FilterSlice ranges over the slice and calls include() for each element. +// If the second returned value is true, the first returned value will be included in the resulting +// slice (after deduplication). +func FilterSlice[E any, T comparable](s []E, include func(E) (T, bool)) []T { + filtered := make([]T, 0, len(s)) // slice will be clipped before returning + seen := make(map[T]bool, len(s)) + for i := range s { + if v, ok := include(s[i]); ok && !seen[v] { + filtered = append(filtered, v) + seen[v] = true + } + } + return slices.Clip(filtered) +} diff --git a/modules/container/filter_test.go b/modules/container/filter_test.go new file mode 100644 index 0000000000..ad304e5abb --- /dev/null +++ b/modules/container/filter_test.go @@ -0,0 +1,28 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package container + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestFilterMapUnique(t *testing.T) { + result := FilterSlice([]int{ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + }, func(i int) (int, bool) { + switch i { + case 0: + return 0, true // included later + case 1: + return 0, true // duplicate of previous (should be ignored) + case 2: + return 2, false // not included + default: + return i, true + } + }) + assert.Equal(t, []int{0, 3, 4, 5, 6, 7, 8, 9}, result) +} diff --git a/modules/git/commit.go b/modules/git/commit.go index 00681e3b2f..b5ae2e0e52 100644 --- a/modules/git/commit.go +++ b/modules/git/commit.go @@ -462,7 +462,7 @@ func parseCommitFileStatus(fileStatus *CommitFileStatus, stdout io.Reader) { _, _ = rd.Discard(1) } for { - modifier, err := rd.ReadSlice('\x00') + modifier, err := rd.ReadString('\x00') if err != nil { if err != io.EOF { log.Error("Unexpected error whilst reading from git log --name-status. Error: %v", err) diff --git a/modules/git/diff_test.go b/modules/git/diff_test.go index 0f865c52a8..8fa47a943c 100644 --- a/modules/git/diff_test.go +++ b/modules/git/diff_test.go @@ -159,22 +159,6 @@ func BenchmarkCutDiffAroundLine(b *testing.B) { } } -func ExampleCutDiffAroundLine() { - const diff = `diff --git a/README.md b/README.md ---- a/README.md -+++ b/README.md -@@ -1,3 +1,6 @@ - # gitea-github-migrator -+ -+ Build Status -- Latest Release - Docker Pulls -+ cut off -+ cut off` - result, _ := CutDiffAroundLine(strings.NewReader(diff), 4, false, 3) - println(result) -} - func TestParseDiffHunkString(t *testing.T) { leftLine, leftHunk, rightLine, rightHunk := ParseDiffHunkString("@@ -19,3 +19,5 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER") assert.EqualValues(t, 19, leftLine) diff --git a/modules/git/grep.go b/modules/git/grep.go index ee6a858f74..1de739107a 100644 --- a/modules/git/grep.go +++ b/modules/git/grep.go @@ -10,6 +10,7 @@ import ( "context" "errors" "fmt" + "io" "os" "strconv" "strings" @@ -80,10 +81,21 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO defer stdoutReader.Close() isInBlock := false - scanner := bufio.NewScanner(stdoutReader) + scanner := bufio.NewReader(stdoutReader) var res *GrepResult - for scanner.Scan() { - line := scanner.Text() + for { + line, err := scanner.ReadString('\n') + if err != nil { + if err == io.EOF { + return nil + } + return err + } + // Remove delimiter. + if len(line) > 0 { + line = line[:len(line)-1] + } + if !isInBlock { if _ /* ref */, filename, ok := strings.Cut(line, ":"); ok { isInBlock = true @@ -109,7 +121,7 @@ func GrepSearch(ctx context.Context, repo *Repository, search string, opts GrepO res.LineCodes = append(res.LineCodes, lineCode) } } - return scanner.Err() + return nil }, }) // git grep exits by cancel (killed), usually it is caused by the limit of results diff --git a/modules/git/grep_test.go b/modules/git/grep_test.go index b5fa437c53..a321b11452 100644 --- a/modules/git/grep_test.go +++ b/modules/git/grep_test.go @@ -4,7 +4,10 @@ package git import ( + "bytes" "context" + "os" + "path" "path/filepath" "testing" @@ -49,3 +52,27 @@ func TestGrepSearch(t *testing.T) { assert.Error(t, err) assert.Len(t, res, 0) } + +func TestGrepLongFiles(t *testing.T) { + tmpDir := t.TempDir() + + err := InitRepository(DefaultContext, tmpDir, false, Sha1ObjectFormat.Name()) + assert.NoError(t, err) + + gitRepo, err := openRepositoryWithDefaultContext(tmpDir) + assert.NoError(t, err) + defer gitRepo.Close() + + assert.NoError(t, os.WriteFile(path.Join(tmpDir, "README.md"), bytes.Repeat([]byte{'a'}, 65*1024), 0o666)) + + err = AddChanges(tmpDir, true) + assert.NoError(t, err) + + err = CommitChanges(tmpDir, CommitChangesOptions{Message: "Long file"}) + assert.NoError(t, err) + + res, err := GrepSearch(context.Background(), gitRepo, "a", GrepOptions{}) + assert.NoError(t, err) + assert.Len(t, res, 1) + assert.Len(t, res[0].LineCodes[0], 65*1024) +} diff --git a/modules/git/object_format.go b/modules/git/object_format.go index a056b20e8a..c2fcf4c063 100644 --- a/modules/git/object_format.go +++ b/modules/git/object_format.go @@ -6,6 +6,7 @@ package git import ( "crypto/sha1" "crypto/sha256" + "hash" "regexp" "strconv" ) @@ -33,6 +34,15 @@ type ObjectFormat interface { ComputeHash(t ObjectType, content []byte) ObjectID } +func computeHash(dst []byte, hasher hash.Hash, t ObjectType, content []byte) []byte { + _, _ = hasher.Write(t.Bytes()) + _, _ = hasher.Write([]byte(" ")) + _, _ = hasher.Write([]byte(strconv.Itoa(len(content)))) + _, _ = hasher.Write([]byte{0}) + _, _ = hasher.Write(content) + return hasher.Sum(dst) +} + /* SHA1 Type */ type Sha1ObjectFormatImpl struct{} @@ -65,16 +75,9 @@ func (Sha1ObjectFormatImpl) MustID(b []byte) ObjectID { // ComputeHash compute the hash for a given ObjectType and content func (h Sha1ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) ObjectID { - hasher := sha1.New() - _, _ = hasher.Write(t.Bytes()) - _, _ = hasher.Write([]byte(" ")) - _, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10))) - _, _ = hasher.Write([]byte{0}) - - // HashSum generates a SHA1 for the provided hash - var sha1 Sha1Hash - copy(sha1[:], hasher.Sum(nil)) - return &sha1 + var obj Sha1Hash + computeHash(obj[:0], sha1.New(), t, content) + return &obj } /* SHA256 Type */ @@ -111,16 +114,9 @@ func (Sha256ObjectFormatImpl) MustID(b []byte) ObjectID { // ComputeHash compute the hash for a given ObjectType and content func (h Sha256ObjectFormatImpl) ComputeHash(t ObjectType, content []byte) ObjectID { - hasher := sha256.New() - _, _ = hasher.Write(t.Bytes()) - _, _ = hasher.Write([]byte(" ")) - _, _ = hasher.Write([]byte(strconv.FormatInt(int64(len(content)), 10))) - _, _ = hasher.Write([]byte{0}) - - // HashSum generates a SHA256 for the provided hash - var sha256 Sha1Hash - copy(sha256[:], hasher.Sum(nil)) - return &sha256 + var obj Sha256Hash + computeHash(obj[:0], sha256.New(), t, content) + return &obj } var ( diff --git a/modules/git/object_id_test.go b/modules/git/object_id_test.go index 1ad40096a0..6f365d6b19 100644 --- a/modules/git/object_id_test.go +++ b/modules/git/object_id_test.go @@ -18,4 +18,8 @@ func TestIsValidSHAPattern(t *testing.T) { assert.False(t, h.IsValid("abc")) assert.False(t, h.IsValid("123g")) assert.False(t, h.IsValid("some random text")) + + assert.Equal(t, "79ee38a6416c1ede423ec7ee0a8639ceea4aad22", ComputeBlobHash(Sha1ObjectFormat, []byte("some random blob")).String()) + assert.Equal(t, "d5c6407415d85df49592672aa421aed39b9db5e3", ComputeBlobHash(Sha1ObjectFormat, []byte("same length blob")).String()) + assert.Equal(t, "df0b5174ed06ae65aea40d43316bcbc21d82c9e3158ce2661df2ad28d7931dd6", ComputeBlobHash(Sha256ObjectFormat, []byte("some random blob")).String()) } diff --git a/modules/git/pipeline/lfs_common.go b/modules/git/pipeline/lfs_common.go new file mode 100644 index 0000000000..188e7d4d65 --- /dev/null +++ b/modules/git/pipeline/lfs_common.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pipeline + +import ( + "fmt" + "time" + + "code.gitea.io/gitea/modules/git" +) + +// LFSResult represents commits found using a provided pointer file hash +type LFSResult struct { + Name string + SHA string + Summary string + When time.Time + ParentHashes []git.ObjectID + BranchName string + FullCommitName string +} + +type lfsResultSlice []*LFSResult + +func (a lfsResultSlice) Len() int { return len(a) } +func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } +func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) } + +func lfsError(msg string, err error) error { + return fmt.Errorf("LFS error occurred, %s: err: %w", msg, err) +} diff --git a/modules/git/pipeline/lfs.go b/modules/git/pipeline/lfs_gogit.go similarity index 80% rename from modules/git/pipeline/lfs.go rename to modules/git/pipeline/lfs_gogit.go index 6dfca24f29..adcf8ed09c 100644 --- a/modules/git/pipeline/lfs.go +++ b/modules/git/pipeline/lfs_gogit.go @@ -7,12 +7,10 @@ package pipeline import ( "bufio" - "fmt" "io" "sort" "strings" "sync" - "time" "code.gitea.io/gitea/modules/git" @@ -21,23 +19,6 @@ import ( "github.com/go-git/go-git/v5/plumbing/object" ) -// LFSResult represents commits found using a provided pointer file hash -type LFSResult struct { - Name string - SHA string - Summary string - When time.Time - ParentHashes []git.ObjectID - BranchName string - FullCommitName string -} - -type lfsResultSlice []*LFSResult - -func (a lfsResultSlice) Len() int { return len(a) } -func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) } - // FindLFSFile finds commits that contain a provided pointer file hash func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) { resultsMap := map[string]*LFSResult{} @@ -51,7 +32,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err All: true, }) if err != nil { - return nil, fmt.Errorf("Failed to get GoGit CommitsIter. Error: %w", err) + return nil, lfsError("failed to get GoGit CommitsIter", err) } err = commitsIter.ForEach(func(gitCommit *object.Commit) error { @@ -85,7 +66,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err return nil }) if err != nil && err != io.EOF { - return nil, fmt.Errorf("Failure in CommitIter.ForEach: %w", err) + return nil, lfsError("failure in CommitIter.ForEach", err) } for _, result := range resultsMap { @@ -156,7 +137,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err select { case err, has := <-errChan: if has { - return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err) + return nil, lfsError("unable to obtain name for LFS files", err) } default: } diff --git a/modules/git/pipeline/lfs_nogogit.go b/modules/git/pipeline/lfs_nogogit.go index 4c65249089..a3ee883968 100644 --- a/modules/git/pipeline/lfs_nogogit.go +++ b/modules/git/pipeline/lfs_nogogit.go @@ -8,33 +8,14 @@ package pipeline import ( "bufio" "bytes" - "fmt" "io" "sort" "strings" "sync" - "time" "code.gitea.io/gitea/modules/git" ) -// LFSResult represents commits found using a provided pointer file hash -type LFSResult struct { - Name string - SHA string - Summary string - When time.Time - ParentIDs []git.ObjectID - BranchName string - FullCommitName string -} - -type lfsResultSlice []*LFSResult - -func (a lfsResultSlice) Len() int { return len(a) } -func (a lfsResultSlice) Swap(i, j int) { a[i], a[j] = a[j], a[i] } -func (a lfsResultSlice) Less(i, j int) bool { return a[j].When.After(a[i].When) } - // FindLFSFile finds commits that contain a provided pointer file hash func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, error) { resultsMap := map[string]*LFSResult{} @@ -137,11 +118,11 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err n += int64(count) if bytes.Equal(binObjectID, objectID.RawValue()) { result := LFSResult{ - Name: curPath + string(fname), - SHA: curCommit.ID.String(), - Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0], - When: curCommit.Author.When, - ParentIDs: curCommit.Parents, + Name: curPath + string(fname), + SHA: curCommit.ID.String(), + Summary: strings.Split(strings.TrimSpace(curCommit.CommitMessage), "\n")[0], + When: curCommit.Author.When, + ParentHashes: curCommit.Parents, } resultsMap[curCommit.ID.String()+":"+curPath+string(fname)] = &result } else if string(mode) == git.EntryModeTree.String() { @@ -183,7 +164,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err for _, result := range resultsMap { hasParent := false - for _, parentID := range result.ParentIDs { + for _, parentID := range result.ParentHashes { if _, hasParent = resultsMap[parentID.String()+":"+result.Name]; hasParent { break } @@ -241,7 +222,7 @@ func FindLFSFile(repo *git.Repository, objectID git.ObjectID) ([]*LFSResult, err select { case err, has := <-errChan: if has { - return nil, fmt.Errorf("Unable to obtain name for LFS files. Error: %w", err) + return nil, lfsError("unable to obtain name for LFS files", err) } default: } diff --git a/modules/git/repo_attribute_test.go b/modules/git/repo_attribute_test.go index 9b7b43d510..e9f7454413 100644 --- a/modules/git/repo_attribute_test.go +++ b/modules/git/repo_attribute_test.go @@ -225,13 +225,13 @@ func TestGitAttributeCheckerError(t *testing.T) { ac, err := gitRepo.GitAttributeChecker("", "linguist-language") require.NoError(t, err) - // calling CheckPath before would allow git to cache part of it and succesfully return later + // calling CheckPath before would allow git to cache part of it and successfully return later assert.NoError(t, os.RemoveAll(gitRepo.Path)) _, err = ac.CheckPath("i-am-a-python.p") if err == nil { t.Skip( - "git check-attr started too fast and CheckPath was succesful (and likely cached)", + "git check-attr started too fast and CheckPath was successful (and likely cached)", "https://codeberg.org/forgejo/forgejo/issues/2948", ) } diff --git a/modules/httplib/url_test.go b/modules/httplib/url_test.go index 72033b1208..2842edd514 100644 --- a/modules/httplib/url_test.go +++ b/modules/httplib/url_test.go @@ -7,20 +7,39 @@ import ( "testing" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) func TestIsRiskyRedirectURL(t *testing.T) { - setting.AppURL = "http://localhost:3000/" + defer test.MockVariableValue(&setting.AppURL, "http://localhost:3000/sub/")() + defer test.MockVariableValue(&setting.AppSubURL, "/sub")() + tests := []struct { input string want bool }{ {"", false}, {"foo", false}, - {"/", false}, + {"./", false}, + {"?key=val", false}, + {"/sub/", false}, + {"http://localhost:3000/sub/", false}, + {"/sub/foo", false}, + {"http://localhost:3000/sub/foo", false}, + {"http://localhost:3000/sub/test?param=false", false}, + // FIXME: should probably be true (would requires resolving references using setting.appURL.ResolveReference(u)) + {"/sub/../", false}, + {"http://localhost:3000/sub/../", false}, + {"/sUb/", false}, + {"http://localhost:3000/sUb/foo", false}, + {"/sub", false}, {"/foo?k=%20#abc", false}, + {"/", false}, + {"a/", false}, + {"test?param=false", false}, + {"/hey/hey/hey#3244", false}, {"//", true}, {"\\\\", true}, @@ -28,7 +47,73 @@ func TestIsRiskyRedirectURL(t *testing.T) { {"\\/", true}, {"mail:a@b.com", true}, {"https://test.com", true}, - {setting.AppURL + "/foo", false}, + {"http://localhost:3000/foo", true}, + {"http://localhost:3000/sub", true}, + {"http://localhost:3000/sub?key=val", true}, + {"https://example.com/", true}, + {"//example.com", true}, + {"http://example.com", true}, + {"http://localhost:3000/test?param=false", true}, + {"//localhost:3000/test?param=false", true}, + {"://missing protocol scheme", true}, + // FIXME: should probably be false + {"//localhost:3000/sub/test?param=false", true}, + } + for _, tt := range tests { + t.Run(tt.input, func(t *testing.T) { + assert.Equal(t, tt.want, IsRiskyRedirectURL(tt.input)) + }) + } +} + +func TestIsRiskyRedirectURLWithoutSubURL(t *testing.T) { + defer test.MockVariableValue(&setting.AppURL, "https://next.forgejo.org/")() + defer test.MockVariableValue(&setting.AppSubURL, "")() + + tests := []struct { + input string + want bool + }{ + {"", false}, + {"foo", false}, + {"./", false}, + {"?key=val", false}, + {"/sub/", false}, + {"https://next.forgejo.org/sub/", false}, + {"/sub/foo", false}, + {"https://next.forgejo.org/sub/foo", false}, + {"https://next.forgejo.org/sub/test?param=false", false}, + {"https://next.forgejo.org/sub/../", false}, + {"/sub/../", false}, + {"/sUb/", false}, + {"https://next.forgejo.org/sUb/foo", false}, + {"/sub", false}, + {"/foo?k=%20#abc", false}, + {"/", false}, + {"a/", false}, + {"test?param=false", false}, + {"/hey/hey/hey#3244", false}, + {"https://next.forgejo.org/test?param=false", false}, + {"https://next.forgejo.org/foo", false}, + {"https://next.forgejo.org/sub", false}, + {"https://next.forgejo.org/sub?key=val", false}, + + {"//", true}, + {"\\\\", true}, + {"/\\", true}, + {"\\/", true}, + {"mail:a@b.com", true}, + {"https://test.com", true}, + {"https://example.com/", true}, + {"//example.com", true}, + {"http://example.com", true}, + {"://missing protocol scheme", true}, + {"https://forgejo.org", true}, + {"https://example.org?url=https://next.forgejo.org", true}, + // FIXME: should probably be false + {"https://next.forgejo.org", true}, + {"//next.forgejo.org/test?param=false", true}, + {"//next.forgejo.org/sub/test?param=false", true}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { diff --git a/modules/indexer/code/bleve/bleve.go b/modules/indexer/code/bleve/bleve.go index c607d780ef..ff0e37ca29 100644 --- a/modules/indexer/code/bleve/bleve.go +++ b/modules/indexer/code/bleve/bleve.go @@ -41,6 +41,8 @@ const ( 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 ) func addUnicodeNormalizeTokenFilter(m *mapping.IndexMappingImpl) error { @@ -246,7 +248,7 @@ func (b *Indexer) Search(ctx context.Context, opts *internal.SearchOptions) (int phraseQuery.Analyzer = repoIndexerAnalyzer keywordQuery = phraseQuery if opts.IsKeywordFuzzy { - phraseQuery.Fuzziness = len(opts.Keyword) / fuzzyDenominator + phraseQuery.Fuzziness = min(maxFuzziness, len(opts.Keyword)/fuzzyDenominator) } if len(opts.RepoIDs) > 0 { diff --git a/modules/indexer/code/indexer.go b/modules/indexer/code/indexer.go index ebebf6ba8a..0a8ce27907 100644 --- a/modules/indexer/code/indexer.go +++ b/modules/indexer/code/indexer.go @@ -120,9 +120,12 @@ func Init() { case "bleve", "elasticsearch": handler := func(items ...*internal.IndexerData) (unhandled []*internal.IndexerData) { indexer := *globalIndexer.Load() + // make it a process to allow for cancellation (especially during integration tests where no global shutdown happens) + batchCtx, _, finished := process.GetManager().AddContext(ctx, "CodeIndexer batch") + defer finished() for _, indexerData := range items { log.Trace("IndexerData Process Repo: %d", indexerData.RepoID) - if err := index(ctx, indexer, indexerData.RepoID); err != nil { + if err := index(batchCtx, indexer, indexerData.RepoID); err != nil { unhandled = append(unhandled, indexerData) if !setting.IsInTesting { log.Error("Codes indexer handler: index error for repo %v: %v", indexerData.RepoID, err) @@ -155,7 +158,7 @@ func Init() { if err := recover(); err != nil { log.Error("PANIC whilst initializing repository indexer: %v\nStacktrace: %s", err, log.Stack(2)) log.Error("The indexer files are likely corrupted and may need to be deleted") - log.Error("You can completely remove the \"%s\" directory to make Gitea recreate the indexes", setting.Indexer.RepoPath) + log.Error("You can completely remove the \"%s\" directory to make Forgejo recreate the indexes", setting.Indexer.RepoPath) } }() @@ -173,17 +176,11 @@ func Init() { if err := recover(); err != nil { log.Error("PANIC whilst initializing repository indexer: %v\nStacktrace: %s", err, log.Stack(2)) log.Error("The indexer files are likely corrupted and may need to be deleted") - log.Error("You can completely remove the \"%s\" index to make Gitea recreate the indexes", setting.Indexer.RepoConnStr) + log.Error("You can completely remove the \"%s\" index to make Forgejo recreate the indexes", setting.Indexer.RepoConnStr) } }() rIndexer = elasticsearch.NewIndexer(setting.Indexer.RepoConnStr, setting.Indexer.RepoIndexerName) - if err != nil { - cancel() - (*globalIndexer.Load()).Close() - close(waitChannel) - log.Fatal("PID: %d Unable to create the elasticsearch Repository Indexer connstr: %s Error: %v", os.Getpid(), setting.Indexer.RepoConnStr, err) - } existed, err = rIndexer.Init(ctx) if err != nil { cancel() diff --git a/modules/indexer/code/indexer_test.go b/modules/indexer/code/indexer_test.go index 8975c5ce40..2d013e08ed 100644 --- a/modules/indexer/code/indexer_test.go +++ b/modules/indexer/code/indexer_test.go @@ -49,6 +49,12 @@ func testIndexer(name string, t *testing.T, indexer internal.Indexer) { IDs: []int64{}, Langs: 0, }, + { + RepoIDs: nil, + Keyword: "Description for", + IDs: []int64{repoID}, + Langs: 1, + }, { RepoIDs: nil, Keyword: "repo1", diff --git a/modules/indexer/internal/bleve/indexer.go b/modules/indexer/internal/bleve/indexer.go index a168e0cb9b..1435d2f519 100644 --- a/modules/indexer/internal/bleve/indexer.go +++ b/modules/indexer/internal/bleve/indexer.go @@ -55,7 +55,7 @@ func (i *Indexer) Init(_ context.Context) (bool, error) { } if version != 0 { - log.Warn("Found older bleve index with version %d, Gitea will remove it and rebuild", version) + log.Warn("Found older bleve index with version %d, Forgejo will remove it and rebuild", version) } indexMapping, err := i.mappingGetter() diff --git a/modules/indexer/internal/elasticsearch/util.go b/modules/indexer/internal/elasticsearch/util.go index 9e034bd553..18cb152d3c 100644 --- a/modules/indexer/internal/elasticsearch/util.go +++ b/modules/indexer/internal/elasticsearch/util.go @@ -62,7 +62,7 @@ func (i *Indexer) checkOldIndexes(ctx context.Context) { indexName := versionedIndexName(i.indexName, v) exists, err := i.Client.IndexExists(indexName).Do(ctx) if err == nil && exists { - log.Warn("Found older elasticsearch index named %q, Gitea will keep the old NOT DELETED. You can delete the old version after the upgrade succeed.", indexName) + log.Warn("Found older elasticsearch index named %q, Forgejo will keep the old NOT DELETED. You can delete the old version after the upgrade succeed.", indexName) } } } diff --git a/modules/indexer/internal/meilisearch/util.go b/modules/indexer/internal/meilisearch/util.go index e6d8fefade..845bdb6e7f 100644 --- a/modules/indexer/internal/meilisearch/util.go +++ b/modules/indexer/internal/meilisearch/util.go @@ -32,7 +32,7 @@ func (i *Indexer) checkOldIndexes() { indexName := versionedIndexName(i.indexName, v) _, err := i.Client.GetIndex(indexName) if err == nil { - log.Warn("Found older meilisearch index named %q, Gitea will keep the old NOT DELETED. You can delete the old version after the upgrade succeed.", indexName) + log.Warn("Found older meilisearch index named %q, Forgejo will keep the old NOT DELETED. You can delete the old version after the upgrade succeed.", indexName) } } } diff --git a/modules/indexer/issues/bleve/bleve.go b/modules/indexer/issues/bleve/bleve.go index 1f54be721b..e3193dbc02 100644 --- a/modules/indexer/issues/bleve/bleve.go +++ b/modules/indexer/issues/bleve/bleve.go @@ -39,6 +39,8 @@ const ( 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 @@ -162,7 +164,7 @@ func (b *Indexer) Search(ctx context.Context, options *internal.SearchOptions) ( if options.Keyword != "" { fuzziness := 0 if options.IsFuzzyKeyword { - fuzziness = len(options.Keyword) / fuzzyDenominator + fuzziness = min(maxFuzziness, len(options.Keyword)/fuzzyDenominator) } queries = append(queries, bleve.NewDisjunctionQuery([]query.Query{ diff --git a/modules/indexer/issues/indexer.go b/modules/indexer/issues/indexer.go index 1cb86feb82..9f56cb00f2 100644 --- a/modules/indexer/issues/indexer.go +++ b/modules/indexer/issues/indexer.go @@ -85,7 +85,7 @@ func InitIssueIndexer(syncReindex bool) { if err := recover(); err != nil { log.Error("PANIC whilst initializing issue indexer: %v\nStacktrace: %s", err, log.Stack(2)) log.Error("The indexer files are likely corrupted and may need to be deleted") - log.Error("You can completely remove the %q directory to make Gitea recreate the indexes", setting.Indexer.IssuePath) + log.Error("You can completely remove the %q directory to make Forgejo recreate the indexes", setting.Indexer.IssuePath) globalIndexer.Store(dummyIndexer) log.Fatal("PID: %d Unable to initialize the Bleve Issue Indexer at path: %s Error: %v", os.Getpid(), setting.Indexer.IssuePath, err) } diff --git a/modules/indexer/issues/internal/tests/tests.go b/modules/indexer/issues/internal/tests/tests.go index 7f32876d80..7144174087 100644 --- a/modules/indexer/issues/internal/tests/tests.go +++ b/modules/indexer/issues/internal/tests/tests.go @@ -130,6 +130,20 @@ var cases = []*testIndexerCase{ ExpectedIDs: []int64{1002, 1001, 1000}, ExpectedTotal: 3, }, + { + Name: "Keyword Fuzzy", + ExtraData: []*internal.IndexerData{ + {ID: 1000, Title: "hi hello world"}, + {ID: 1001, Content: "hi hello world"}, + {ID: 1002, Comments: []string{"hi", "hello world"}}, + }, + SearchOptions: &internal.SearchOptions{ + Keyword: "hello wrold", + IsFuzzyKeyword: true, + }, + ExpectedIDs: []int64{1002, 1001, 1000}, + ExpectedTotal: 3, + }, { Name: "RepoIDs", ExtraData: []*internal.IndexerData{ diff --git a/modules/markup/file_preview.go b/modules/markup/file_preview.go index 95c94e0c14..993df717e1 100644 --- a/modules/markup/file_preview.go +++ b/modules/markup/file_preview.go @@ -27,66 +27,107 @@ var filePreviewPattern = regexp.MustCompile(`https?://((?:\S+/){3})src/commit/([ type FilePreview struct { fileContent []template.HTML + title template.HTML subTitle template.HTML lineOffset int - urlFull string - filePath string start int end int isTruncated bool } -func NewFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale) *FilePreview { +func NewFilePreviews(ctx *RenderContext, node *html.Node, locale translation.Locale) []*FilePreview { if setting.FilePreviewMaxLines == 0 { // Feature is disabled return nil } + mAll := filePreviewPattern.FindAllStringSubmatchIndex(node.Data, -1) + if mAll == nil { + return nil + } + + result := make([]*FilePreview, 0) + + for _, m := range mAll { + if slices.Contains(m, -1) { + continue + } + + preview := newFilePreview(ctx, node, locale, m) + if preview != nil { + result = append(result, preview) + } + } + + return result +} + +func newFilePreview(ctx *RenderContext, node *html.Node, locale translation.Locale, m []int) *FilePreview { preview := &FilePreview{} - m := filePreviewPattern.FindStringSubmatchIndex(node.Data) - if m == nil { - return nil - } - - // Ensure that every group has a match - if slices.Contains(m, -1) { - return nil - } - - preview.urlFull = node.Data[m[0]:m[1]] + urlFull := node.Data[m[0]:m[1]] // Ensure that we only use links to local repositories - if !strings.HasPrefix(preview.urlFull, setting.AppURL+setting.AppSubURL) { + if !strings.HasPrefix(urlFull, setting.AppURL) { return nil } - projPath := strings.TrimSuffix(node.Data[m[2]:m[3]], "/") + projPath := strings.TrimPrefix(strings.TrimSuffix(node.Data[m[0]:m[3]], "/"), setting.AppURL) commitSha := node.Data[m[4]:m[5]] - preview.filePath = node.Data[m[6]:m[7]] + filePath := node.Data[m[6]:m[7]] hash := node.Data[m[8]:m[9]] preview.start = m[0] preview.end = m[1] projPathSegments := strings.Split(projPath, "/") + if len(projPathSegments) != 2 { + return nil + } + + ownerName := projPathSegments[len(projPathSegments)-2] + repoName := projPathSegments[len(projPathSegments)-1] + var language string fileBlob, err := DefaultProcessorHelper.GetRepoFileBlob( ctx.Ctx, - projPathSegments[len(projPathSegments)-2], - projPathSegments[len(projPathSegments)-1], - commitSha, preview.filePath, + ownerName, + repoName, + commitSha, filePath, &language, ) if err != nil { return nil } + titleBuffer := new(bytes.Buffer) + + isExternRef := ownerName != ctx.Metas["user"] || repoName != ctx.Metas["repo"] + if isExternRef { + err = html.Render(titleBuffer, createLink(node.Data[m[0]:m[3]], ownerName+"/"+repoName, "")) + if err != nil { + log.Error("failed to render repoLink: %v", err) + } + titleBuffer.WriteString(" – ") + } + + err = html.Render(titleBuffer, createLink(urlFull, filePath, "muted")) + if err != nil { + log.Error("failed to render filepathLink: %v", err) + } + + preview.title = template.HTML(titleBuffer.String()) + lineSpecs := strings.Split(hash, "-") commitLinkBuffer := new(bytes.Buffer) - err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitSha[0:7], "text black")) + commitLinkText := commitSha[0:7] + if isExternRef { + commitLinkText = ownerName + "/" + repoName + "@" + commitLinkText + } + + err = html.Render(commitLinkBuffer, createLink(node.Data[m[0]:m[5]], commitLinkText, "text black")) if err != nil { log.Error("failed to render commitLink: %v", err) } @@ -272,19 +313,16 @@ func (p *FilePreview) CreateHTML(locale translation.Locale) *html.Node { Data: atom.Div.String(), Attr: []html.Attribute{{Key: "class", Val: "header"}}, } - afilepath := &html.Node{ + + ptitle := &html.Node{ Type: html.ElementNode, - Data: atom.A.String(), - Attr: []html.Attribute{ - {Key: "href", Val: p.urlFull}, - {Key: "class", Val: "muted"}, - }, + Data: atom.Div.String(), } - afilepath.AppendChild(&html.Node{ - Type: html.TextNode, - Data: p.filePath, + ptitle.AppendChild(&html.Node{ + Type: html.RawNode, + Data: string(p.title), }) - header.AppendChild(afilepath) + header.AppendChild(ptitle) psubtitle := &html.Node{ Type: html.ElementNode, diff --git a/modules/markup/html.go b/modules/markup/html.go index 020ba9c631..7a5ca14aeb 100644 --- a/modules/markup/html.go +++ b/modules/markup/html.go @@ -893,7 +893,7 @@ func issueIndexPatternProcessor(ctx *RenderContext, node *html.Node) { } else { // Path determines the type of link that will be rendered. It's unknown at this point whether // the linked item is actually a PR or an issue. Luckily it's of no real consequence because - // Gitea will redirect on click as appropriate. + // Forgejo will redirect on click as appropriate. path := "issues" if ref.IsPull { path = "pulls" @@ -1056,45 +1056,53 @@ func comparePatternProcessor(ctx *RenderContext, node *html.Node) { } func filePreviewPatternProcessor(ctx *RenderContext, node *html.Node) { - if ctx.Metas == nil { + if ctx.Metas == nil || ctx.Metas["user"] == "" || ctx.Metas["repo"] == "" { return } if DefaultProcessorHelper.GetRepoFileBlob == nil { return } + locale := translation.NewLocale("en-US") + if ctx.Ctx != nil { + ctxLocale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale) + if ok { + locale = ctxLocale + } + } + next := node.NextSibling for node != nil && node != next { - locale := translation.NewLocale("en-US") - if ctx.Ctx != nil { - ctxLocale, ok := ctx.Ctx.Value(translation.ContextKey).(translation.Locale) - if ok { - locale = ctxLocale + previews := NewFilePreviews(ctx, node, locale) + if previews == nil { + node = node.NextSibling + continue + } + + offset := 0 + for _, preview := range previews { + previewNode := preview.CreateHTML(locale) + + // Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div + before := node.Data[:(preview.start - offset)] + after := node.Data[(preview.end - offset):] + afterPrefix := "

" + offset = preview.end - len(afterPrefix) + node.Data = before + nextSibling := node.NextSibling + node.Parent.InsertBefore(&html.Node{ + Type: html.RawNode, + Data: "

", + }, nextSibling) + node.Parent.InsertBefore(previewNode, nextSibling) + afterNode := &html.Node{ + Type: html.RawNode, + Data: afterPrefix + after, } + node.Parent.InsertBefore(afterNode, nextSibling) + node = afterNode } - preview := NewFilePreview(ctx, node, locale) - if preview == nil { - return - } - - previewNode := preview.CreateHTML(locale) - - // Specialized version of replaceContent, so the parent paragraph element is not destroyed from our div - before := node.Data[:preview.start] - after := node.Data[preview.end:] - node.Data = before - nextSibling := node.NextSibling - node.Parent.InsertBefore(&html.Node{ - Type: html.RawNode, - Data: "

", - }, nextSibling) - node.Parent.InsertBefore(previewNode, nextSibling) - node.Parent.InsertBefore(&html.Node{ - Type: html.RawNode, - Data: "

" + after, - }, nextSibling) - node = node.NextSibling } } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 61a3edd6b3..a1a99c1a7f 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -17,6 +17,7 @@ import ( "code.gitea.io/gitea/modules/markup" "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/util" @@ -680,9 +681,9 @@ func TestIssue18471(t *testing.T) { } func TestRender_FilePreview(t *testing.T) { - setting.StaticRootPath = "../../" - setting.Names = []string{"english"} - setting.Langs = []string{"en-US"} + defer test.MockVariableValue(&setting.StaticRootPath, "../../")() + defer test.MockVariableValue(&setting.Names, []string{"english"})() + defer test.MockVariableValue(&setting.Langs, []string{"en-US"})() translation.InitLocales(context.Background()) setting.AppURL = markup.TestAppURL @@ -705,41 +706,297 @@ func TestRender_FilePreview(t *testing.T) { sha := "190d9492934af498c3f669d6a2431dc5459e5b20" commitFilePreview := util.URLJoin(markup.TestRepoURL, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3" - test := func(input, expected string) { + testRender := func(input, expected string, metas map[string]string) { buffer, err := markup.RenderString(&markup.RenderContext{ Ctx: git.DefaultContext, RelativePath: ".md", - Metas: localMetas, + Metas: metas, }, input) assert.NoError(t, err) assert.Equal(t, strings.TrimSpace(expected), strings.TrimSpace(buffer)) } - test( - commitFilePreview, - `

`+ - `
`+ - `
`+ - `path/to/file.go`+ - ``+ - `Lines 2 to 3 in 190d949`+ - ``+ - `
`+ - `
`+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - ``+ - `
B`+"\n"+`
C`+"\n"+`
`+ - `
`+ - `
`+ - `

`, - ) + t.Run("single", func(t *testing.T) { + testRender( + commitFilePreview, + `

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) + + t.Run("cross-repo", func(t *testing.T) { + testRender( + commitFilePreview, + `

`+ + `
`+ + `
`+ + `
`+ + `gogits/gogs – `+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in gogits/gogs@190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + map[string]string{ + "user": "gogits", + "repo": "gogs2", + }, + ) + }) + + t.Run("AppSubURL", func(t *testing.T) { + urlWithSub := util.URLJoin(markup.TestAppURL, "sub", markup.TestOrgRepo, "src", "commit", sha, "path", "to", "file.go") + "#L2-L3" + + testRender( + urlWithSub, + `

190d949293/path/to/file.go (L2-L3)

`, + localMetas, + ) + + defer test.MockVariableValue(&setting.AppURL, markup.TestAppURL+"sub/")() + defer test.MockVariableValue(&setting.AppSubURL, "/sub")() + + testRender( + urlWithSub, + `

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + + testRender( + "first without sub "+commitFilePreview+" second "+urlWithSub, + `

first without sub 190d949293/path/to/file.go (L2-L3) second

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) + + t.Run("multiples", func(t *testing.T) { + testRender( + "first "+commitFilePreview+" second "+commitFilePreview, + `

first

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

second

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + + testRender( + "first "+commitFilePreview+" second "+commitFilePreview+" third "+commitFilePreview, + `

first

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

second

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

third

`+ + `
`+ + `
`+ + `
`+ + `path/to/file.go`+ + `
`+ + ``+ + `Lines 2 to 3 in 190d949`+ + ``+ + `
`+ + `
`+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + ``+ + `
B`+"\n"+`
C`+"\n"+`
`+ + `
`+ + `
`+ + `

`, + localMetas, + ) + }) } diff --git a/modules/markup/markdown/goldmark.go b/modules/markup/markdown/goldmark.go index 5a481a31fd..8289e28677 100644 --- a/modules/markup/markdown/goldmark.go +++ b/modules/markup/markdown/goldmark.go @@ -4,17 +4,12 @@ package markdown import ( - "bytes" "fmt" "regexp" - "slices" "strings" - "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/common" "code.gitea.io/gitea/modules/setting" - giteautil "code.gitea.io/gitea/modules/util" "github.com/yuin/goldmark/ast" east "github.com/yuin/goldmark/extension/ast" @@ -30,6 +25,12 @@ var byteMailto = []byte("mailto:") // ASTTransformer is a default transformer of the goldmark tree. type ASTTransformer struct{} +func (g *ASTTransformer) applyElementDir(n ast.Node) { + if markup.DefaultProcessorHelper.ElementDir != "" { + n.SetAttributeString("dir", []byte(markup.DefaultProcessorHelper.ElementDir)) + } +} + // Transform transforms the given AST tree. func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc parser.Context) { firstChild := node.FirstChild() @@ -46,12 +47,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa tocMode = rc.TOC } - applyElementDir := func(n ast.Node) { - if markup.DefaultProcessorHelper.ElementDir != "" { - n.SetAttributeString("dir", []byte(markup.DefaultProcessorHelper.ElementDir)) - } - } - _ = ast.Walk(node, func(n ast.Node, entering bool) (ast.WalkStatus, error) { if !entering { return ast.WalkContinue, nil @@ -59,135 +54,15 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa switch v := n.(type) { case *ast.Heading: - for _, attr := range v.Attributes() { - if _, ok := attr.Value.([]byte); !ok { - v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) - } - } - txt := n.Text(reader.Source()) - header := markup.Header{ - Text: util.BytesToReadOnlyString(txt), - Level: v.Level, - } - if id, found := v.AttributeString("id"); found { - header.ID = util.BytesToReadOnlyString(id.([]byte)) - } - tocList = append(tocList, header) - applyElementDir(v) + g.transformHeading(ctx, v, reader, &tocList) case *ast.Paragraph: - applyElementDir(v) + g.applyElementDir(v) case *ast.Image: - // Images need two things: - // - // 1. Their src needs to munged to be a real value - // 2. If they're not wrapped with a link they need a link wrapper - - // Check if the destination is a real link - if len(v.Destination) > 0 && !markup.IsLink(v.Destination) { - v.Destination = []byte(giteautil.URLJoin( - ctx.Links.ResolveMediaLink(ctx.IsWiki), - strings.TrimLeft(string(v.Destination), "/"), - )) - } - - parent := n.Parent() - // Create a link around image only if parent is not already a link - if _, ok := parent.(*ast.Link); !ok && parent != nil { - next := n.NextSibling() - - // Create a link wrapper - wrap := ast.NewLink() - wrap.Destination = v.Destination - wrap.Title = v.Title - wrap.SetAttributeString("target", []byte("_blank")) - - // Duplicate the current image node - image := ast.NewImage(ast.NewLink()) - image.Destination = v.Destination - image.Title = v.Title - for _, attr := range v.Attributes() { - image.SetAttribute(attr.Name, attr.Value) - } - for child := v.FirstChild(); child != nil; { - next := child.NextSibling() - image.AppendChild(image, child) - child = next - } - - // Append our duplicate image to the wrapper link - wrap.AppendChild(wrap, image) - - // Wire in the next sibling - wrap.SetNextSibling(next) - - // Replace the current node with the wrapper link - parent.ReplaceChild(parent, n, wrap) - - // But most importantly ensure the next sibling is still on the old image too - v.SetNextSibling(next) - } + g.transformImage(ctx, v, reader) case *ast.Link: - // Links need their href to munged to be a real value - link := v.Destination - - // Do not process the link if it's not a link, starts with an hashtag - // (indicating it's an anchor link), starts with `mailto:` or any of the - // custom markdown URLs. - processLink := len(link) > 0 && !markup.IsLink(link) && - link[0] != '#' && !bytes.HasPrefix(link, byteMailto) && - !slices.ContainsFunc(setting.Markdown.CustomURLSchemes, func(s string) bool { - return bytes.HasPrefix(link, []byte(s+":")) - }) - - if processLink { - var base string - if ctx.IsWiki { - base = ctx.Links.WikiLink() - } else if ctx.Links.HasBranchInfo() { - base = ctx.Links.SrcLink() - } else { - base = ctx.Links.Base - } - - link = []byte(giteautil.URLJoin(base, string(link))) - } - if len(link) > 0 && link[0] == '#' { - link = []byte("#user-content-" + string(link)[1:]) - } - v.Destination = link + g.transformLink(ctx, v, reader) case *ast.List: - if v.HasChildren() { - children := make([]ast.Node, 0, v.ChildCount()) - child := v.FirstChild() - for child != nil { - children = append(children, child) - child = child.NextSibling() - } - v.RemoveChildren(v) - - for _, child := range children { - listItem := child.(*ast.ListItem) - if !child.HasChildren() || !child.FirstChild().HasChildren() { - v.AppendChild(v, child) - continue - } - taskCheckBox, ok := child.FirstChild().FirstChild().(*east.TaskCheckBox) - if !ok { - v.AppendChild(v, child) - continue - } - newChild := NewTaskCheckBoxListItem(listItem) - newChild.IsChecked = taskCheckBox.IsChecked - newChild.SetAttributeString("class", []byte("task-list-item")) - segments := newChild.FirstChild().Lines() - if segments.Len() > 0 { - segment := segments.At(0) - newChild.SourcePosition = rc.metaLength + segment.Start - } - v.AppendChild(v, newChild) - } - } - applyElementDir(v) + g.transformList(ctx, v, reader, rc) case *ast.Text: if v.SoftLineBreak() && !v.HardLineBreak() { if ctx.Metas["mode"] != "document" { @@ -197,10 +72,7 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } } case *ast.CodeSpan: - colorContent := n.Text(reader.Source()) - if matchColor(strings.ToLower(string(colorContent))) { - v.AppendChild(v, NewColorPreview(colorContent)) - } + g.transformCodeSpan(ctx, v, reader) } return ast.WalkContinue, nil }) @@ -222,50 +94,6 @@ func (g *ASTTransformer) Transform(node *ast.Document, reader text.Reader, pc pa } } -type prefixedIDs struct { - values container.Set[string] -} - -// Generate generates a new element id. -func (p *prefixedIDs) Generate(value []byte, kind ast.NodeKind) []byte { - dft := []byte("id") - if kind == ast.KindHeading { - dft = []byte("heading") - } - return p.GenerateWithDefault(value, dft) -} - -// Generate generates a new element id. -func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte { - result := common.CleanValue(value) - if len(result) == 0 { - result = dft - } - if !bytes.HasPrefix(result, []byte("user-content-")) { - result = append([]byte("user-content-"), result...) - } - if p.values.Add(util.BytesToReadOnlyString(result)) { - return result - } - for i := 1; ; i++ { - newResult := fmt.Sprintf("%s-%d", result, i) - if p.values.Add(newResult) { - return []byte(newResult) - } - } -} - -// Put puts a given element id to the used ids table. -func (p *prefixedIDs) Put(value []byte) { - p.values.Add(util.BytesToReadOnlyString(value)) -} - -func newPrefixedIDs() *prefixedIDs { - return &prefixedIDs{ - values: make(container.Set[string]), - } -} - // NewHTMLRenderer creates a HTMLRenderer to render // in the gitea form. func NewHTMLRenderer(opts ...html.Option) renderer.NodeRenderer { @@ -295,38 +123,6 @@ func (r *HTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) { reg.Register(east.KindTaskCheckBox, r.renderTaskCheckBox) } -// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements. -// See #21474 for reference -func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { - if entering { - if n.Attributes() != nil { - _, _ = w.WriteString("') - } else { - _, _ = w.WriteString("") - } - for c := n.FirstChild(); c != nil; c = c.NextSibling() { - switch v := c.(type) { - case *ast.Text: - segment := v.Segment - value := segment.Value(source) - if bytes.HasSuffix(value, []byte("\n")) { - r.Writer.RawWrite(w, value[:len(value)-1]) - r.Writer.RawWrite(w, []byte(" ")) - } else { - r.Writer.RawWrite(w, value) - } - case *ColorPreview: - _, _ = w.WriteString(fmt.Sprintf(``, string(v.Color))) - } - } - return ast.WalkSkipChildren, nil - } - _, _ = w.WriteString("") - return ast.WalkContinue, nil -} - func (r *HTMLRenderer) renderDocument(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { n := node.(*ast.Document) @@ -415,38 +211,3 @@ func (r *HTMLRenderer) renderIcon(w util.BufWriter, source []byte, node ast.Node return ast.WalkContinue, nil } - -func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - n := node.(*TaskCheckBoxListItem) - if entering { - if n.Attributes() != nil { - _, _ = w.WriteString("') - } else { - _, _ = w.WriteString("
  • ") - } - fmt.Fprintf(w, ``) - } else { - _ = w.WriteByte('>') - } - fc := n.FirstChild() - if fc != nil { - if _, ok := fc.(*ast.TextBlock); !ok { - _ = w.WriteByte('\n') - } - } - } else { - _, _ = w.WriteString("
  • \n") - } - return ast.WalkContinue, nil -} - -func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { - return ast.WalkContinue, nil -} diff --git a/modules/markup/markdown/prefixed_id.go b/modules/markup/markdown/prefixed_id.go new file mode 100644 index 0000000000..9c60949202 --- /dev/null +++ b/modules/markup/markdown/prefixed_id.go @@ -0,0 +1,59 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package markdown + +import ( + "bytes" + "fmt" + + "code.gitea.io/gitea/modules/container" + "code.gitea.io/gitea/modules/markup/common" + + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/util" +) + +type prefixedIDs struct { + values container.Set[string] +} + +// Generate generates a new element id. +func (p *prefixedIDs) Generate(value []byte, kind ast.NodeKind) []byte { + dft := []byte("id") + if kind == ast.KindHeading { + dft = []byte("heading") + } + return p.GenerateWithDefault(value, dft) +} + +// GenerateWithDefault generates a new element id. +func (p *prefixedIDs) GenerateWithDefault(value, dft []byte) []byte { + result := common.CleanValue(value) + if len(result) == 0 { + result = dft + } + if !bytes.HasPrefix(result, []byte("user-content-")) { + result = append([]byte("user-content-"), result...) + } + if p.values.Add(util.BytesToReadOnlyString(result)) { + return result + } + for i := 1; ; i++ { + newResult := fmt.Sprintf("%s-%d", result, i) + if p.values.Add(newResult) { + return []byte(newResult) + } + } +} + +// Put puts a given element id to the used ids table. +func (p *prefixedIDs) Put(value []byte) { + p.values.Add(util.BytesToReadOnlyString(value)) +} + +func newPrefixedIDs() *prefixedIDs { + return &prefixedIDs{ + values: make(container.Set[string]), + } +} diff --git a/modules/markup/markdown/transform_codespan.go b/modules/markup/markdown/transform_codespan.go new file mode 100644 index 0000000000..0cf1169dee --- /dev/null +++ b/modules/markup/markdown/transform_codespan.go @@ -0,0 +1,56 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package markdown + +import ( + "bytes" + "fmt" + "strings" + + "code.gitea.io/gitea/modules/markup" + + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/renderer/html" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +// renderCodeSpan renders CodeSpan elements (like goldmark upstream does) but also renders ColorPreview elements. +// See #21474 for reference +func (r *HTMLRenderer) renderCodeSpan(w util.BufWriter, source []byte, n ast.Node, entering bool) (ast.WalkStatus, error) { + if entering { + if n.Attributes() != nil { + _, _ = w.WriteString("') + } else { + _, _ = w.WriteString("") + } + for c := n.FirstChild(); c != nil; c = c.NextSibling() { + switch v := c.(type) { + case *ast.Text: + segment := v.Segment + value := segment.Value(source) + if bytes.HasSuffix(value, []byte("\n")) { + r.Writer.RawWrite(w, value[:len(value)-1]) + r.Writer.RawWrite(w, []byte(" ")) + } else { + r.Writer.RawWrite(w, value) + } + case *ColorPreview: + _, _ = w.WriteString(fmt.Sprintf(``, string(v.Color))) + } + } + return ast.WalkSkipChildren, nil + } + _, _ = w.WriteString("") + return ast.WalkContinue, nil +} + +func (g *ASTTransformer) transformCodeSpan(ctx *markup.RenderContext, v *ast.CodeSpan, reader text.Reader) { + colorContent := v.Text(reader.Source()) + if matchColor(strings.ToLower(string(colorContent))) { + v.AppendChild(v, NewColorPreview(colorContent)) + } +} diff --git a/modules/markup/markdown/transform_heading.go b/modules/markup/markdown/transform_heading.go new file mode 100644 index 0000000000..ce585a37de --- /dev/null +++ b/modules/markup/markdown/transform_heading.go @@ -0,0 +1,32 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package markdown + +import ( + "fmt" + + "code.gitea.io/gitea/modules/markup" + + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +func (g *ASTTransformer) transformHeading(ctx *markup.RenderContext, v *ast.Heading, reader text.Reader, tocList *[]markup.Header) { + for _, attr := range v.Attributes() { + if _, ok := attr.Value.([]byte); !ok { + v.SetAttribute(attr.Name, []byte(fmt.Sprintf("%v", attr.Value))) + } + } + txt := v.Text(reader.Source()) + header := markup.Header{ + Text: util.BytesToReadOnlyString(txt), + Level: v.Level, + } + if id, found := v.AttributeString("id"); found { + header.ID = util.BytesToReadOnlyString(id.([]byte)) + } + *tocList = append(*tocList, header) + g.applyElementDir(v) +} diff --git a/modules/markup/markdown/transform_image.go b/modules/markup/markdown/transform_image.go new file mode 100644 index 0000000000..db449e9dd9 --- /dev/null +++ b/modules/markup/markdown/transform_image.go @@ -0,0 +1,66 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package markdown + +import ( + "strings" + + "code.gitea.io/gitea/modules/markup" + giteautil "code.gitea.io/gitea/modules/util" + + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/text" +) + +func (g *ASTTransformer) transformImage(ctx *markup.RenderContext, v *ast.Image, reader text.Reader) { + // Images need two things: + // + // 1. Their src needs to munged to be a real value + // 2. If they're not wrapped with a link they need a link wrapper + + // Check if the destination is a real link + if len(v.Destination) > 0 && !markup.IsLink(v.Destination) { + v.Destination = []byte(giteautil.URLJoin( + ctx.Links.ResolveMediaLink(ctx.IsWiki), + strings.TrimLeft(string(v.Destination), "/"), + )) + } + + parent := v.Parent() + // Create a link around image only if parent is not already a link + if _, ok := parent.(*ast.Link); !ok && parent != nil { + next := v.NextSibling() + + // Create a link wrapper + wrap := ast.NewLink() + wrap.Destination = v.Destination + wrap.Title = v.Title + wrap.SetAttributeString("target", []byte("_blank")) + + // Duplicate the current image node + image := ast.NewImage(ast.NewLink()) + image.Destination = v.Destination + image.Title = v.Title + for _, attr := range v.Attributes() { + image.SetAttribute(attr.Name, attr.Value) + } + for child := v.FirstChild(); child != nil; { + next := child.NextSibling() + image.AppendChild(image, child) + child = next + } + + // Append our duplicate image to the wrapper link + wrap.AppendChild(wrap, image) + + // Wire in the next sibling + wrap.SetNextSibling(next) + + // Replace the current node with the wrapper link + parent.ReplaceChild(parent, v, wrap) + + // But most importantly ensure the next sibling is still on the old image too + v.SetNextSibling(next) + } +} diff --git a/modules/markup/markdown/transform_link.go b/modules/markup/markdown/transform_link.go new file mode 100644 index 0000000000..aceae5b74f --- /dev/null +++ b/modules/markup/markdown/transform_link.go @@ -0,0 +1,47 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package markdown + +import ( + "bytes" + "slices" + + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/setting" + giteautil "code.gitea.io/gitea/modules/util" + + "github.com/yuin/goldmark/ast" + "github.com/yuin/goldmark/text" +) + +func (g *ASTTransformer) transformLink(ctx *markup.RenderContext, v *ast.Link, reader text.Reader) { + // Links need their href to munged to be a real value + link := v.Destination + + // Do not process the link if it's not a link, starts with an hashtag + // (indicating it's an anchor link), starts with `mailto:` or any of the + // custom markdown URLs. + processLink := len(link) > 0 && !markup.IsLink(link) && + link[0] != '#' && !bytes.HasPrefix(link, byteMailto) && + !slices.ContainsFunc(setting.Markdown.CustomURLSchemes, func(s string) bool { + return bytes.HasPrefix(link, []byte(s+":")) + }) + + if processLink { + var base string + if ctx.IsWiki { + base = ctx.Links.WikiLink() + } else if ctx.Links.HasBranchInfo() { + base = ctx.Links.SrcLink() + } else { + base = ctx.Links.Base + } + + link = []byte(giteautil.URLJoin(base, string(link))) + } + if len(link) > 0 && link[0] == '#' { + link = []byte("#user-content-" + string(link)[1:]) + } + v.Destination = link +} diff --git a/modules/markup/markdown/transform_list.go b/modules/markup/markdown/transform_list.go new file mode 100644 index 0000000000..6563e2dd64 --- /dev/null +++ b/modules/markup/markdown/transform_list.go @@ -0,0 +1,86 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package markdown + +import ( + "fmt" + + "code.gitea.io/gitea/modules/markup" + + "github.com/yuin/goldmark/ast" + east "github.com/yuin/goldmark/extension/ast" + "github.com/yuin/goldmark/renderer/html" + "github.com/yuin/goldmark/text" + "github.com/yuin/goldmark/util" +) + +func (r *HTMLRenderer) renderTaskCheckBoxListItem(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + n := node.(*TaskCheckBoxListItem) + if entering { + if n.Attributes() != nil { + _, _ = w.WriteString("') + } else { + _, _ = w.WriteString("
  • ") + } + fmt.Fprintf(w, ``) + } else { + _ = w.WriteByte('>') + } + fc := n.FirstChild() + if fc != nil { + if _, ok := fc.(*ast.TextBlock); !ok { + _ = w.WriteByte('\n') + } + } + } else { + _, _ = w.WriteString("
  • \n") + } + return ast.WalkContinue, nil +} + +func (r *HTMLRenderer) renderTaskCheckBox(w util.BufWriter, source []byte, node ast.Node, entering bool) (ast.WalkStatus, error) { + return ast.WalkContinue, nil +} + +func (g *ASTTransformer) transformList(ctx *markup.RenderContext, v *ast.List, reader text.Reader, rc *RenderConfig) { + if v.HasChildren() { + children := make([]ast.Node, 0, v.ChildCount()) + child := v.FirstChild() + for child != nil { + children = append(children, child) + child = child.NextSibling() + } + v.RemoveChildren(v) + + for _, child := range children { + listItem := child.(*ast.ListItem) + if !child.HasChildren() || !child.FirstChild().HasChildren() { + v.AppendChild(v, child) + continue + } + taskCheckBox, ok := child.FirstChild().FirstChild().(*east.TaskCheckBox) + if !ok { + v.AppendChild(v, child) + continue + } + newChild := NewTaskCheckBoxListItem(listItem) + newChild.IsChecked = taskCheckBox.IsChecked + newChild.SetAttributeString("class", []byte("task-list-item")) + segments := newChild.FirstChild().Lines() + if segments.Len() > 0 { + segment := segments.At(0) + newChild.SourcePosition = rc.metaLength + segment.Start + } + v.AppendChild(v, newChild) + } + } + g.applyElementDir(v) +} diff --git a/modules/nosql/manager_redis.go b/modules/nosql/manager_redis.go index 3c82651541..3c5502f979 100644 --- a/modules/nosql/manager_redis.go +++ b/modules/nosql/manager_redis.go @@ -224,7 +224,7 @@ func getRedisOptions(uri *url.URL) *redis.UniversalOptions { if db, err := strconv.Atoi(uri.Path[1:]); err == nil { opts.DB = db } else { - log.Error("Provided database identifier '%s' is not a valid integer. Gitea will ignore this option.", uri.Path) + log.Error("Provided database identifier '%s' is not a valid integer. Forgejo will ignore this option.", uri.Path) } } diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go index 6688e78cd1..b120a0edf6 100644 --- a/modules/optional/serialization.go +++ b/modules/optional/serialization.go @@ -35,7 +35,7 @@ func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { return nil } -func (o Option[T]) MarshalYAML() (interface{}, error) { +func (o Option[T]) MarshalYAML() (any, error) { if !o.Has() { return nil, nil } diff --git a/modules/packages/nuget/metadata.go b/modules/packages/nuget/metadata.go index 264b4612fb..1e98ddffde 100644 --- a/modules/packages/nuget/metadata.go +++ b/modules/packages/nuget/metadata.go @@ -59,6 +59,7 @@ type Package struct { type Metadata struct { Description string `json:"description,omitempty"` ReleaseNotes string `json:"release_notes,omitempty"` + Readme string `json:"readme,omitempty"` Authors string `json:"authors,omitempty"` ProjectURL string `json:"project_url,omitempty"` RepositoryURL string `json:"repository_url,omitempty"` @@ -72,6 +73,7 @@ type Dependency struct { Version string `json:"version"` } +// https://learn.microsoft.com/en-us/nuget/reference/nuspec type nuspecPackage struct { Metadata struct { ID string `xml:"id"` @@ -81,6 +83,7 @@ type nuspecPackage struct { ProjectURL string `xml:"projectUrl"` Description string `xml:"description"` ReleaseNotes string `xml:"releaseNotes"` + Readme string `xml:"readme"` PackageTypes struct { PackageType []struct { Name string `xml:"name,attr"` @@ -90,6 +93,11 @@ type nuspecPackage struct { URL string `xml:"url,attr"` } `xml:"repository"` Dependencies struct { + Dependency []struct { + ID string `xml:"id,attr"` + Version string `xml:"version,attr"` + Exclude string `xml:"exclude,attr"` + } `xml:"dependency"` Group []struct { TargetFramework string `xml:"targetFramework,attr"` Dependency []struct { @@ -123,14 +131,14 @@ func ParsePackageMetaData(r io.ReaderAt, size int64) (*Package, error) { } defer f.Close() - return ParseNuspecMetaData(f) + return ParseNuspecMetaData(archive, f) } } return nil, ErrMissingNuspecFile } // ParseNuspecMetaData parses a Nuspec file to retrieve the metadata of a Nuget package -func ParseNuspecMetaData(r io.Reader) (*Package, error) { +func ParseNuspecMetaData(archive *zip.Reader, r io.Reader) (*Package, error) { var nuspecBuf bytes.Buffer var p nuspecPackage if err := xml.NewDecoder(io.TeeReader(r, &nuspecBuf)).Decode(&p); err != nil { @@ -168,6 +176,28 @@ func ParseNuspecMetaData(r io.Reader) (*Package, error) { Dependencies: make(map[string][]Dependency), } + if p.Metadata.Readme != "" { + f, err := archive.Open(p.Metadata.Readme) + if err == nil { + buf, _ := io.ReadAll(f) + m.Readme = string(buf) + _ = f.Close() + } + } + + if len(p.Metadata.Dependencies.Dependency) > 0 { + deps := make([]Dependency, 0, len(p.Metadata.Dependencies.Dependency)) + for _, dep := range p.Metadata.Dependencies.Dependency { + if dep.ID == "" || dep.Version == "" { + continue + } + deps = append(deps, Dependency{ + ID: dep.ID, + Version: dep.Version, + }) + } + m.Dependencies[""] = deps + } for _, group := range p.Metadata.Dependencies.Group { deps := make([]Dependency, 0, len(group.Dependency)) for _, dep := range group.Dependency { diff --git a/modules/packages/nuget/metadata_test.go b/modules/packages/nuget/metadata_test.go index bba2bff4a5..f466492f8a 100644 --- a/modules/packages/nuget/metadata_test.go +++ b/modules/packages/nuget/metadata_test.go @@ -6,7 +6,6 @@ package nuget import ( "archive/zip" "bytes" - "strings" "testing" "github.com/stretchr/testify/assert" @@ -19,6 +18,7 @@ const ( projectURL = "https://gitea.io" description = "Package Description" releaseNotes = "Package Release Notes" + readme = "Readme" repositoryURL = "https://gitea.io/gitea/gitea" targetFramework = ".NETStandard2.1" dependencyID = "System.Text.Json" @@ -36,6 +36,7 @@ const nuspecContent = ` ` + description + ` ` + releaseNotes + ` + README.md @@ -60,17 +61,19 @@ const symbolsNuspecContent = ` ` func TestParsePackageMetaData(t *testing.T) { - createArchive := func(name, content string) []byte { + createArchive := func(files map[string]string) []byte { var buf bytes.Buffer archive := zip.NewWriter(&buf) - w, _ := archive.Create(name) - w.Write([]byte(content)) + for name, content := range files { + w, _ := archive.Create(name) + w.Write([]byte(content)) + } archive.Close() return buf.Bytes() } t.Run("MissingNuspecFile", func(t *testing.T) { - data := createArchive("dummy.txt", "") + data := createArchive(map[string]string{"dummy.txt": ""}) np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.Nil(t, np) @@ -78,7 +81,7 @@ func TestParsePackageMetaData(t *testing.T) { }) t.Run("MissingNuspecFileInRoot", func(t *testing.T) { - data := createArchive("sub/package.nuspec", "") + data := createArchive(map[string]string{"sub/package.nuspec": ""}) np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.Nil(t, np) @@ -86,7 +89,7 @@ func TestParsePackageMetaData(t *testing.T) { }) t.Run("InvalidNuspecFile", func(t *testing.T) { - data := createArchive("package.nuspec", "") + data := createArchive(map[string]string{"package.nuspec": ""}) np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.Nil(t, np) @@ -94,10 +97,10 @@ func TestParsePackageMetaData(t *testing.T) { }) t.Run("InvalidPackageId", func(t *testing.T) { - data := createArchive("package.nuspec", ` + data := createArchive(map[string]string{"package.nuspec": ` - `) + `}) np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.Nil(t, np) @@ -105,30 +108,34 @@ func TestParsePackageMetaData(t *testing.T) { }) t.Run("InvalidPackageVersion", func(t *testing.T) { - data := createArchive("package.nuspec", ` + data := createArchive(map[string]string{"package.nuspec": ` - `+id+` + ` + id + ` - `) + `}) np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.Nil(t, np) assert.ErrorIs(t, err, ErrNuspecInvalidVersion) }) - t.Run("Valid", func(t *testing.T) { - data := createArchive("package.nuspec", nuspecContent) + t.Run("MissingReadme", func(t *testing.T) { + data := createArchive(map[string]string{"package.nuspec": nuspecContent}) np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.NoError(t, err) assert.NotNil(t, np) + assert.Empty(t, np.Metadata.Readme) }) -} -func TestParseNuspecMetaData(t *testing.T) { t.Run("Dependency Package", func(t *testing.T) { - np, err := ParseNuspecMetaData(strings.NewReader(nuspecContent)) + data := createArchive(map[string]string{ + "package.nuspec": nuspecContent, + "README.md": readme, + }) + + np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.NoError(t, err) assert.NotNil(t, np) assert.Equal(t, DependencyPackage, np.PackageType) @@ -139,6 +146,7 @@ func TestParseNuspecMetaData(t *testing.T) { assert.Equal(t, projectURL, np.Metadata.ProjectURL) assert.Equal(t, description, np.Metadata.Description) assert.Equal(t, releaseNotes, np.Metadata.ReleaseNotes) + assert.Equal(t, readme, np.Metadata.Readme) assert.Equal(t, repositoryURL, np.Metadata.RepositoryURL) assert.Len(t, np.Metadata.Dependencies, 1) assert.Contains(t, np.Metadata.Dependencies, targetFramework) @@ -148,13 +156,15 @@ func TestParseNuspecMetaData(t *testing.T) { assert.Equal(t, dependencyVersion, deps[0].Version) t.Run("NormalizedVersion", func(t *testing.T) { - np, err := ParseNuspecMetaData(strings.NewReader(` - - - test - 1.04.5.2.5-rc.1+metadata - -`)) + data := createArchive(map[string]string{"package.nuspec": ` + + + test + 1.04.5.2.5-rc.1+metadata + + `}) + + np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.NoError(t, err) assert.NotNil(t, np) assert.Equal(t, "1.4.5.2-rc.1", np.Version) @@ -162,7 +172,9 @@ func TestParseNuspecMetaData(t *testing.T) { }) t.Run("Symbols Package", func(t *testing.T) { - np, err := ParseNuspecMetaData(strings.NewReader(symbolsNuspecContent)) + data := createArchive(map[string]string{"package.nuspec": symbolsNuspecContent}) + + np, err := ParsePackageMetaData(bytes.NewReader(data), int64(len(data))) assert.NoError(t, err) assert.NotNil(t, np) assert.Equal(t, SymbolsPackage, np.PackageType) diff --git a/modules/repository/push.go b/modules/repository/push.go index cf047847b6..751ee83a09 100644 --- a/modules/repository/push.go +++ b/modules/repository/push.go @@ -16,6 +16,7 @@ type PushUpdateOptions struct { RefFullName git.RefName // branch, tag or other name to push OldCommitID string NewCommitID string + TimeNano int64 } // IsNewRef return true if it's a first-time push to a branch, tag or etc. diff --git a/modules/session/store.go b/modules/session/store.go index 4fa4d2848f..70988fcdc5 100644 --- a/modules/session/store.go +++ b/modules/session/store.go @@ -18,6 +18,12 @@ type Store interface { // RegenerateSession regenerates the underlying session and returns the new store func RegenerateSession(resp http.ResponseWriter, req *http.Request) (Store, error) { + for _, f := range BeforeRegenerateSession { + f(resp, req) + } s, err := session.RegenerateSession(resp, req) return s, err } + +// BeforeRegenerateSession is a list of functions that are called before a session is regenerated. +var BeforeRegenerateSession []func(http.ResponseWriter, *http.Request) diff --git a/modules/setting/database.go b/modules/setting/database.go index 6abef42cba..76fae27164 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -83,7 +83,7 @@ func loadDBSetting(rootCfg ConfigProvider) { Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0) } Database.ConnMaxIdleTime = sec.Key("CONN_MAX_IDLETIME").MustDuration(0) - Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0) + Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(100) Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) Database.LogSQL = sec.Key("LOG_SQL").MustBool(false) diff --git a/modules/setting/incoming_email.go b/modules/setting/incoming_email.go index 75337a312f..314fdea137 100644 --- a/modules/setting/incoming_email.go +++ b/modules/setting/incoming_email.go @@ -38,6 +38,24 @@ func loadIncomingEmailFrom(rootCfg ConfigProvider) { return } + // Handle aliases + sec := rootCfg.Section("email.incoming") + if sec.HasKey("USER") && !sec.HasKey("USERNAME") { + IncomingEmail.Username = sec.Key("USER").String() + } + if sec.HasKey("PASSWD") && !sec.HasKey("PASSWORD") { + IncomingEmail.Password = sec.Key("PASSWD").String() + } + + // Infer Port if not set + if IncomingEmail.Port == 0 { + if IncomingEmail.UseTLS { + IncomingEmail.Port = 993 + } else { + IncomingEmail.Port = 143 + } + } + if err := checkReplyToAddress(IncomingEmail.ReplyToAddress); err != nil { log.Fatal("Invalid incoming_mail.REPLY_TO_ADDRESS (%s): %v", IncomingEmail.ReplyToAddress, err) } diff --git a/modules/setting/incoming_email_test.go b/modules/setting/incoming_email_test.go new file mode 100644 index 0000000000..0fdd44d333 --- /dev/null +++ b/modules/setting/incoming_email_test.go @@ -0,0 +1,74 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package setting + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func Test_loadIncomingEmailFrom(t *testing.T) { + makeBaseConfig := func() (ConfigProvider, ConfigSection) { + cfg, _ := NewConfigProviderFromData("") + sec := cfg.Section("email.incoming") + sec.NewKey("ENABLED", "true") + sec.NewKey("REPLY_TO_ADDRESS", "forge+%{token}@example.com") + + return cfg, sec + } + resetIncomingEmailPort := func() func() { + return func() { + IncomingEmail.Port = 0 + } + } + + t.Run("aliases", func(t *testing.T) { + cfg, sec := makeBaseConfig() + sec.NewKey("USER", "jane.doe@example.com") + sec.NewKey("PASSWD", "y0u'll n3v3r gUess th1S!!1") + + loadIncomingEmailFrom(cfg) + + assert.EqualValues(t, "jane.doe@example.com", IncomingEmail.Username) + assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", IncomingEmail.Password) + }) + + t.Run("Port settings", func(t *testing.T) { + t.Run("no port, no tls", func(t *testing.T) { + defer resetIncomingEmailPort()() + cfg, sec := makeBaseConfig() + + // False is the default, but we test it explicitly. + sec.NewKey("USE_TLS", "false") + + loadIncomingEmailFrom(cfg) + + assert.EqualValues(t, 143, IncomingEmail.Port) + }) + + t.Run("no port, with tls", func(t *testing.T) { + defer resetIncomingEmailPort()() + cfg, sec := makeBaseConfig() + + sec.NewKey("USE_TLS", "true") + + loadIncomingEmailFrom(cfg) + + assert.EqualValues(t, 993, IncomingEmail.Port) + }) + + t.Run("port overrides tls", func(t *testing.T) { + defer resetIncomingEmailPort()() + cfg, sec := makeBaseConfig() + + sec.NewKey("PORT", "1993") + sec.NewKey("USE_TLS", "true") + + loadIncomingEmailFrom(cfg) + + assert.EqualValues(t, 1993, IncomingEmail.Port) + }) + }) +} diff --git a/modules/setting/mailer.go b/modules/setting/mailer.go index a2bc2df444..e9ce640c7f 100644 --- a/modules/setting/mailer.go +++ b/modules/setting/mailer.go @@ -134,6 +134,14 @@ func loadMailerFrom(rootCfg ConfigProvider) { sec.Key("PROTOCOL").SetValue("smtp+starttls") } + // Handle aliases + if sec.HasKey("USERNAME") && !sec.HasKey("USER") { + sec.Key("USER").SetValue(sec.Key("USERNAME").String()) + } + if sec.HasKey("PASSWORD") && !sec.HasKey("PASSWD") { + sec.Key("PASSWD").SetValue(sec.Key("PASSWORD").String()) + } + // Set default values & validate sec.Key("NAME").MustString(AppName) sec.Key("PROTOCOL").In("", []string{"smtp", "smtps", "smtp+starttls", "smtp+unix", "sendmail", "dummy"}) diff --git a/modules/setting/mailer_test.go b/modules/setting/mailer_test.go index fbabf11378..f8af4a78c1 100644 --- a/modules/setting/mailer_test.go +++ b/modules/setting/mailer_test.go @@ -38,4 +38,17 @@ func Test_loadMailerFrom(t *testing.T) { assert.EqualValues(t, kase.SMTPPort, MailService.SMTPPort) }) } + + t.Run("property aliases", func(t *testing.T) { + cfg, _ := NewConfigProviderFromData("") + sec := cfg.Section("mailer") + sec.NewKey("ENABLED", "true") + sec.NewKey("USERNAME", "jane.doe@example.com") + sec.NewKey("PASSWORD", "y0u'll n3v3r gUess th1S!!1") + + loadMailerFrom(cfg) + + assert.EqualValues(t, "jane.doe@example.com", MailService.User) + assert.EqualValues(t, "y0u'll n3v3r gUess th1S!!1", MailService.Passwd) + }) } diff --git a/modules/setting/repository.go b/modules/setting/repository.go index 50f0fd704c..6086dd1d57 100644 --- a/modules/setting/repository.go +++ b/modules/setting/repository.go @@ -162,7 +162,7 @@ var ( PreferredLicenses: []string{"Apache-2.0", "MIT"}, DisableHTTPGit: false, AccessControlAllowOrigin: "", - UseCompatSSHURI: false, + UseCompatSSHURI: true, DefaultCloseIssuesViaCommitsInAnyBranch: false, EnablePushCreateUser: false, EnablePushCreateOrg: false, diff --git a/modules/setting/setting.go b/modules/setting/setting.go index 781e794d5d..c906429a71 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -67,8 +67,8 @@ func IsRunUserMatchCurrentUser(runUser string) (string, bool) { // PrepareAppDataPath creates app data directory if necessary func PrepareAppDataPath() error { // FIXME: There are too many calls to MkdirAll in old code. It is incorrect. - // For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Gitea runs, - // then gitea will make new empty directories in /mnt/vol1, all are stored in the root filesystem. + // For example, if someDir=/mnt/vol1/gitea-home/data, if the mount point /mnt/vol1 is not mounted when Forgejo runs, + // then Forgejo will make new empty directories in /mnt/vol1, all are stored in the root filesystem. // The correct behavior should be: creating parent directories is end users' duty. We only create sub-directories in existing parent directories. // For quickstart, the parent directories should be created automatically for first startup (eg: a flag or a check of INSTALL_LOCK). // Now we can take the first step to do correctly (using Mkdir) in other packages, and prepare the AppDataPath here, then make a refactor in future. @@ -103,7 +103,7 @@ func InitCfgProvider(file string) { func MustInstalled() { if !InstallLock { - log.Fatal(`Unable to load config file for a installed Gitea instance, you should either use "--config" to set your config file (app.ini), or run "gitea web" command to install Gitea.`) + log.Fatal(`Unable to load config file for a installed Forgejo instance, you should either use "--config" to set your config file (app.ini), or run "forgejo web" command to install Forgejo.`) } } @@ -163,7 +163,7 @@ func loadRunModeFrom(rootCfg ConfigProvider) { rootSec := rootCfg.Section("") RunUser = rootSec.Key("RUN_USER").MustString(user.CurrentUsername()) - // The following is a purposefully undocumented option. Please do not run Gitea as root. It will only cause future headaches. + // The following is a purposefully undocumented option. Please do not run Forgejo as root. It will only cause future headaches. // Please don't use root as a bandaid to "fix" something that is broken, instead the broken thing should instead be fixed properly. unsafeAllowRunAsRoot := ConfigSectionKeyBool(rootSec, "I_AM_BEING_UNSAFE_RUNNING_AS_ROOT") unsafeAllowRunAsRoot = unsafeAllowRunAsRoot || util.OptionalBoolParse(os.Getenv("GITEA_I_AM_BEING_UNSAFE_RUNNING_AS_ROOT")).Value() @@ -183,9 +183,9 @@ func loadRunModeFrom(rootCfg ConfigProvider) { if os.Getuid() == 0 { if !unsafeAllowRunAsRoot { // Special thanks to VLC which inspired the wording of this messaging. - log.Fatal("Gitea is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the `cap_net_bind_service` permission") + log.Fatal("Forgejo is not supposed to be run as root. Sorry. If you need to use privileged TCP ports please instead use setcap and the `cap_net_bind_service` permission") } - log.Critical("You are running Gitea using the root user, and have purposely chosen to skip built-in protections around this. You have been warned against this.") + log.Critical("You are running Forgejo using the root user, and have purposely chosen to skip built-in protections around this. You have been warned against this.") } } diff --git a/modules/structs/admin_user.go b/modules/structs/admin_user.go index ad86f4ca03..5b7df127b4 100644 --- a/modules/structs/admin_user.go +++ b/modules/structs/admin_user.go @@ -30,10 +30,8 @@ type CreateUserOption struct { // EditUserOption edit user options type EditUserOption struct { - // required: true - SourceID int64 `json:"source_id"` - // required: true - LoginName string `json:"login_name" binding:"Required"` + SourceID *int64 `json:"source_id"` + LoginName *string `json:"login_name"` // swagger:strfmt email Email *string `json:"email" binding:"MaxSize(254)"` FullName *string `json:"full_name" binding:"MaxSize(100)"` diff --git a/modules/structs/release.go b/modules/structs/release.go index 02c68af188..d8da924f54 100644 --- a/modules/structs/release.go +++ b/modules/structs/release.go @@ -9,18 +9,19 @@ import ( // Release represents a repository release type Release struct { - ID int64 `json:"id"` - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - TarURL string `json:"tarball_url"` - ZipURL string `json:"zipball_url"` - UploadURL string `json:"upload_url"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` + ID int64 `json:"id"` + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + URL string `json:"url"` + HTMLURL string `json:"html_url"` + TarURL string `json:"tarball_url"` + ZipURL string `json:"zipball_url"` + HideArchiveLinks bool `json:"hide_archive_links"` + UploadURL string `json:"upload_url"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` // swagger:strfmt date-time CreatedAt time.Time `json:"created_at"` // swagger:strfmt date-time @@ -33,20 +34,22 @@ type Release struct { // CreateReleaseOption options when creating a release type CreateReleaseOption struct { // required: true - TagName string `json:"tag_name" binding:"Required"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - IsDraft bool `json:"draft"` - IsPrerelease bool `json:"prerelease"` + TagName string `json:"tag_name" binding:"Required"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + IsDraft bool `json:"draft"` + IsPrerelease bool `json:"prerelease"` + HideArchiveLinks bool `json:"hide_archive_links"` } // EditReleaseOption options when editing a release type EditReleaseOption struct { - TagName string `json:"tag_name"` - Target string `json:"target_commitish"` - Title string `json:"name"` - Note string `json:"body"` - IsDraft *bool `json:"draft"` - IsPrerelease *bool `json:"prerelease"` + TagName string `json:"tag_name"` + Target string `json:"target_commitish"` + Title string `json:"name"` + Note string `json:"body"` + IsDraft *bool `json:"draft"` + IsPrerelease *bool `json:"prerelease"` + HideArchiveLinks *bool `json:"hide_archive_links"` } diff --git a/modules/structs/repo.go b/modules/structs/repo.go index f6cc9803a4..1158160b53 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -89,6 +89,7 @@ type Repository struct { HasWiki bool `json:"has_wiki"` ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` WikiBranch string `json:"wiki_branch,omitempty"` + GloballyEditableWiki bool `json:"globally_editable_wiki"` HasPullRequests bool `json:"has_pull_requests"` HasProjects bool `json:"has_projects"` HasReleases bool `json:"has_releases"` @@ -185,6 +186,8 @@ type EditRepoOption struct { HasWiki *bool `json:"has_wiki,omitempty"` // set this structure to use external wiki instead of internal ExternalWiki *ExternalWiki `json:"external_wiki,omitempty"` + // set the globally editable state of the wiki + GloballyEditableWiki *bool `json:"globally_editable_wiki,omitempty"` // sets the default branch for this repository. DefaultBranch *string `json:"default_branch,omitempty"` // sets the branch used for this repository's wiki. diff --git a/modules/structs/repo_compare.go b/modules/structs/repo_compare.go new file mode 100644 index 0000000000..8a12498705 --- /dev/null +++ b/modules/structs/repo_compare.go @@ -0,0 +1,10 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package structs + +// Compare represents a comparison between two commits. +type Compare struct { + TotalCommits int `json:"total_commits"` // Total number of commits in the comparison. + Commits []*Commit `json:"commits"` // List of commits in the comparison. +} diff --git a/modules/structs/user.go b/modules/structs/user.go index 82b565e5e7..ad529c966e 100644 --- a/modules/structs/user.go +++ b/modules/structs/user.go @@ -19,6 +19,8 @@ type User struct { // the user's authentication sign-in name. // default: empty LoginName string `json:"login_name"` + // The ID of the user's Authentication Source + SourceID int64 `json:"source_id"` // the user's full name FullName string `json:"full_name"` // swagger:strfmt email diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 9e6b5baf17..4dc1f1938c 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -54,13 +54,13 @@ func NewFuncMap() template.FuncMap { "JsonUtils": NewJsonUtils, // ----------------------------------------------------------------- - // svg / avatar / icon + // svg / avatar / icon / color "svg": svg.RenderHTML, "EntryIcon": base.EntryIcon, "MigrationIcon": MigrationIcon, "ActionIcon": ActionIcon, - - "SortArrow": SortArrow, + "SortArrow": SortArrow, + "ContrastColor": util.ContrastColor, // ----------------------------------------------------------------- // time / number / format diff --git a/modules/templates/htmlrenderer.go b/modules/templates/htmlrenderer.go index 40941285aa..8661653adf 100644 --- a/modules/templates/htmlrenderer.go +++ b/modules/templates/htmlrenderer.go @@ -136,11 +136,11 @@ func wrapTmplErrMsg(msg string) { return } if setting.IsProd { - // in prod mode, Gitea must have correct templates to run - log.Fatal("Gitea can't run with template errors: %s", msg) + // in prod mode, Forgejo must have correct templates to run + log.Fatal("Forgejo can't run with template errors: %s", msg) } else { // in dev mode, do not need to really exit, because the template errors could be fixed by developer soon and the templates get reloaded - log.Error("There are template errors but Gitea continues to run in dev mode: %s", msg) + log.Error("There are template errors but Forgejo continues to run in dev mode: %s", msg) } } diff --git a/modules/templates/main_test.go b/modules/templates/main_test.go new file mode 100644 index 0000000000..bbdf5d2f99 --- /dev/null +++ b/modules/templates/main_test.go @@ -0,0 +1,24 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package templates_test + +import ( + "context" + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/markup" + + _ "code.gitea.io/gitea/models" + _ "code.gitea.io/gitea/models/issues" +) + +func TestMain(m *testing.M) { + markup.Init(&markup.ProcessorHelper{ + IsUsernameMentionable: func(ctx context.Context, username string) bool { + return username == "mention-user" + }, + }) + unittest.MainTest(m) +} diff --git a/modules/templates/util_misc.go b/modules/templates/util_misc.go index 6c1b4ab240..774385483b 100644 --- a/modules/templates/util_misc.go +++ b/modules/templates/util_misc.go @@ -142,35 +142,39 @@ type remoteAddress struct { Password string } -func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string, ignoreOriginalURL bool) remoteAddress { - a := remoteAddress{} - - remoteURL := m.OriginalURL - if ignoreOriginalURL || remoteURL == "" { - var err error - remoteURL, err = git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) - if err != nil { - log.Error("GetRemoteURL %v", err) - return a - } +func mirrorRemoteAddress(ctx context.Context, m *repo_model.Repository, remoteName string) remoteAddress { + ret := remoteAddress{} + remoteURL, err := git.GetRemoteAddress(ctx, m.RepoPath(), remoteName) + if err != nil { + log.Error("GetRemoteURL %v", err) + return ret } u, err := giturl.Parse(remoteURL) if err != nil { log.Error("giturl.Parse %v", err) - return a + return ret } if u.Scheme != "ssh" && u.Scheme != "file" { if u.User != nil { - a.Username = u.User.Username() - a.Password, _ = u.User.Password() + ret.Username = u.User.Username() + ret.Password, _ = u.User.Password() } - u.User = nil } - a.Address = u.String() - return a + // The URL stored in the git repo could contain authentication, + // erase it, or it will be shown in the UI. + u.User = nil + ret.Address = u.String() + // Why not use m.OriginalURL to set ret.Address? + // It should be OK to use it, since m.OriginalURL should be the same as the authentication-erased URL from the Git repository. + // However, the old code has already stored authentication in m.OriginalURL when updating mirror settings. + // That means we need to use "giturl.Parse" for m.OriginalURL again to ensure authentication is erased. + // Instead of doing this, why not directly use the authentication-erased URL from the Git repository? + // It should be the same as long as there are no bugs. + + return ret } func FilenameIsImage(filename string) bool { diff --git a/modules/templates/util_render.go b/modules/templates/util_render.go index f593e1b897..c4c5376afd 100644 --- a/modules/templates/util_render.go +++ b/modules/templates/util_render.go @@ -135,16 +135,9 @@ func RenderIssueTitle(ctx context.Context, text string, metas map[string]string) func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_model.Label) template.HTML { var ( archivedCSSClass string - textColor = "#111" + textColor = util.ContrastColor(label.Color) labelScope = label.ExclusiveScope() ) - r, g, b := util.HexToRBGColor(label.Color) - - // Determine if label text should be light or dark to be readable on background color - // this doesn't account for saturation or transparency - if util.UseLightTextOnBackground(r, g, b) { - textColor = "#eee" - } description := emoji.ReplaceAliases(template.HTMLEscapeString(label.Description)) @@ -168,7 +161,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m // Make scope and item background colors slightly darker and lighter respectively. // More contrast needed with higher luminance, empirically tweaked. - luminance := util.GetLuminance(r, g, b) + luminance := util.GetRelativeLuminance(label.Color) contrast := 0.01 + luminance*0.03 // Ensure we add the same amount of contrast also near 0 and 1. darken := contrast + math.Max(luminance+contrast-1.0, 0.0) @@ -178,6 +171,7 @@ func RenderLabel(ctx context.Context, locale translation.Locale, label *issues_m lightenFactor := math.Min(luminance+lighten, 1.0) / math.Max(luminance, 1.0/255.0) opacity := GetLabelOpacityByte(label.IsArchived()) + r, g, b := util.HexToRBGColor(label.Color) scopeBytes := []byte{ uint8(math.Min(math.Round(r*darkenFactor), 255)), uint8(math.Min(math.Round(g*darkenFactor), 255)), @@ -239,15 +233,20 @@ func RenderMarkdownToHtml(ctx context.Context, input string) template.HTML { //n return output } -func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string) template.HTML { +func RenderLabels(ctx context.Context, locale translation.Locale, labels []*issues_model.Label, repoLink string, isPull bool) template.HTML { htmlCode := `` for _, label := range labels { // Protect against nil value in labels - shouldn't happen but would cause a panic if so if label == nil { continue } - htmlCode += fmt.Sprintf("%s ", - repoLink, label.ID, RenderLabel(ctx, locale, label)) + + issuesOrPull := "issues" + if isPull { + issuesOrPull = "pulls" + } + htmlCode += fmt.Sprintf("%s ", + repoLink, issuesOrPull, label.ID, RenderLabel(ctx, locale, label)) } htmlCode += "" return template.HTML(htmlCode) diff --git a/modules/templates/util_render_test.go b/modules/templates/util_render_test.go index 5fea4d9a16..c1d5e26f62 100644 --- a/modules/templates/util_render_test.go +++ b/modules/templates/util_render_test.go @@ -6,13 +6,12 @@ package templates import ( "context" "html/template" - "os" "testing" + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" - "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/translation" "github.com/stretchr/testify/assert" ) @@ -46,19 +45,6 @@ var testMetas = map[string]string{ "mode": "comment", } -func TestMain(m *testing.M) { - unittest.InitSettings() - if err := git.InitSimple(context.Background()); err != nil { - log.Fatal("git init failed, err: %v", err) - } - markup.Init(&markup.ProcessorHelper{ - IsUsernameMentionable: func(ctx context.Context, username string) bool { - return username == "mention-user" - }, - }) - os.Exit(m.Run()) -} - func TestApostrophesInMentions(t *testing.T) { rendered := RenderMarkdownToHtml(context.Background(), "@mention-user's comment") assert.EqualValues(t, template.HTML("

    @mention-user's comment

    \n"), rendered) @@ -190,3 +176,15 @@ space

    ` assert.EqualValues(t, expected, RenderMarkdownToHtml(context.Background(), testInput)) } + +func TestRenderLabels(t *testing.T) { + unittest.PrepareTestEnv(t) + + tr := &translation.MockLocale{} + label := unittest.AssertExistsAndLoadBean(t, &issues_model.Label{ID: 1}) + + assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", false), + "user2/repo1/issues?labels=1") + assert.Contains(t, RenderLabels(db.DefaultContext, tr, []*issues_model.Label{label}, "user2/repo1", true), + "user2/repo1/pulls?labels=1") +} diff --git a/modules/util/color.go b/modules/util/color.go index 240b045c28..9c520dce78 100644 --- a/modules/util/color.go +++ b/modules/util/color.go @@ -4,22 +4,10 @@ package util import ( "fmt" - "math" "strconv" "strings" ) -// Check similar implementation in web_src/js/utils/color.js and keep synchronization - -// Return R, G, B values defined in reletive luminance -func getLuminanceRGB(channel float64) float64 { - sRGB := channel / 255 - if sRGB <= 0.03928 { - return sRGB / 12.92 - } - return math.Pow((sRGB+0.055)/1.055, 2.4) -} - // Get color as RGB values in 0..255 range from the hex color string (with or without #) func HexToRBGColor(colorString string) (float64, float64, float64) { hexString := colorString @@ -47,19 +35,23 @@ func HexToRBGColor(colorString string) (float64, float64, float64) { return r, g, b } -// return luminance given RGB channels -// Reference from: https://www.w3.org/WAI/GL/wiki/Relative_luminance -func GetLuminance(r, g, b float64) float64 { - R := getLuminanceRGB(r) - G := getLuminanceRGB(g) - B := getLuminanceRGB(b) - luminance := 0.2126*R + 0.7152*G + 0.0722*B - return luminance +// Returns relative luminance for a SRGB color - https://en.wikipedia.org/wiki/Relative_luminance +// Keep this in sync with web_src/js/utils/color.js +func GetRelativeLuminance(color string) float64 { + r, g, b := HexToRBGColor(color) + return (0.2126729*r + 0.7151522*g + 0.0721750*b) / 255 } -// Reference from: https://firsching.ch/github_labels.html -// In the future WCAG 3 APCA may be a better solution. -// Check if text should use light color based on RGB of background -func UseLightTextOnBackground(r, g, b float64) bool { - return GetLuminance(r, g, b) < 0.453 +func UseLightText(backgroundColor string) bool { + return GetRelativeLuminance(backgroundColor) < 0.453 +} + +// Given a background color, returns a black or white foreground color that the highest +// contrast ratio. In the future, the APCA contrast function, or CSS `contrast-color` will be better. +// https://github.com/color-js/color.js/blob/eb7b53f7a13bb716ec8b28c7a56f052cd599acd9/src/contrast/APCA.js#L42 +func ContrastColor(backgroundColor string) string { + if UseLightText(backgroundColor) { + return "#fff" + } + return "#000" } diff --git a/modules/util/color_test.go b/modules/util/color_test.go index d96ac36730..be6e6b122a 100644 --- a/modules/util/color_test.go +++ b/modules/util/color_test.go @@ -33,33 +33,31 @@ func Test_HexToRBGColor(t *testing.T) { } } -func Test_UseLightTextOnBackground(t *testing.T) { +func Test_UseLightText(t *testing.T) { cases := []struct { - r float64 - g float64 - b float64 - expected bool + color string + expected string }{ - {215, 58, 74, true}, - {0, 117, 202, true}, - {207, 211, 215, false}, - {162, 238, 239, false}, - {112, 87, 255, true}, - {0, 134, 114, true}, - {228, 230, 105, false}, - {216, 118, 227, true}, - {255, 255, 255, false}, - {43, 134, 133, true}, - {43, 135, 134, true}, - {44, 135, 134, true}, - {59, 182, 179, true}, - {124, 114, 104, true}, - {126, 113, 108, true}, - {129, 112, 109, true}, - {128, 112, 112, true}, + {"#d73a4a", "#fff"}, + {"#0075ca", "#fff"}, + {"#cfd3d7", "#000"}, + {"#a2eeef", "#000"}, + {"#7057ff", "#fff"}, + {"#008672", "#fff"}, + {"#e4e669", "#000"}, + {"#d876e3", "#000"}, + {"#ffffff", "#000"}, + {"#2b8684", "#fff"}, + {"#2b8786", "#fff"}, + {"#2c8786", "#000"}, + {"#3bb6b3", "#000"}, + {"#7c7268", "#fff"}, + {"#7e716c", "#fff"}, + {"#81706d", "#fff"}, + {"#807070", "#fff"}, + {"#84b6eb", "#000"}, } for n, c := range cases { - result := UseLightTextOnBackground(c.r, c.g, c.b) - assert.Equal(t, c.expected, result, "case %d: error should match", n) + assert.Equal(t, c.expected, ContrastColor(c.color), "case %d: error should match", n) } } diff --git a/modules/util/util.go b/modules/util/util.go index 7d1a755373..b6ea283551 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -213,6 +213,14 @@ func ToPointer[T any](val T) *T { return &val } +// Iif is an "inline-if", it returns "trueVal" if "condition" is true, otherwise "falseVal" +func Iif[T any](condition bool, trueVal, falseVal T) T { + if condition { + return trueVal + } + return falseVal +} + func ReserveLineBreakForTextarea(input string) string { // Since the content is from a form which is a textarea, the line endings are \r\n. // It's a standard behavior of HTML. diff --git a/modules/web/middleware/cookie.go b/modules/web/middleware/cookie.go index 621640895b..ec6b06f993 100644 --- a/modules/web/middleware/cookie.go +++ b/modules/web/middleware/cookie.go @@ -9,6 +9,7 @@ import ( "net/url" "strings" + "code.gitea.io/gitea/modules/session" "code.gitea.io/gitea/modules/setting" ) @@ -45,10 +46,40 @@ func SetSiteCookie(resp http.ResponseWriter, name, value string, maxAge int) { SameSite: setting.SessionConfig.SameSite, } resp.Header().Add("Set-Cookie", cookie.String()) - if maxAge < 0 { - // There was a bug in "setting.SessionConfig.CookiePath" code, the old default value of it was empty "". - // So we have to delete the cookie on path="" again, because some old code leaves cookies on path="". - cookie.Path = strings.TrimSuffix(setting.SessionConfig.CookiePath, "/") - resp.Header().Add("Set-Cookie", cookie.String()) - } + // Previous versions would use a cookie path with a trailing /. + // These are more specific than cookies without a trailing /, so + // we need to delete these if they exist. + deleteLegacySiteCookie(resp, name) +} + +// deleteLegacySiteCookie deletes the cookie with the given name at the cookie +// path with a trailing /, which would unintentionally override the cookie. +func deleteLegacySiteCookie(resp http.ResponseWriter, name string) { + if setting.SessionConfig.CookiePath == "" || strings.HasSuffix(setting.SessionConfig.CookiePath, "/") { + // If the cookie path ends with /, no legacy cookies will take + // precedence, so do nothing. The exception is that cookies with no + // path could override other cookies, but it's complicated and we don't + // currently handle that. + return + } + + cookie := &http.Cookie{ + Name: name, + Value: "", + MaxAge: -1, + Path: setting.SessionConfig.CookiePath + "/", + Domain: setting.SessionConfig.Domain, + Secure: setting.SessionConfig.Secure, + HttpOnly: true, + SameSite: setting.SessionConfig.SameSite, + } + resp.Header().Add("Set-Cookie", cookie.String()) +} + +func init() { + session.BeforeRegenerateSession = append(session.BeforeRegenerateSession, func(resp http.ResponseWriter, _ *http.Request) { + // Ensure that a cookie with a trailing slash does not take precedence over + // the cookie written by the middleware. + deleteLegacySiteCookie(resp, setting.SessionConfig.CookieName) + }) } diff --git a/options/license/APL-1.0 b/options/license/APL-1.0 index 261f2d687c..0748f90cd9 100644 --- a/options/license/APL-1.0 +++ b/options/license/APL-1.0 @@ -210,21 +210,21 @@ PART 1: INITIAL CONTRIBUTOR AND DESIGNATED WEB SITE The Initial Contributor is: ____________________________________________________ -  + [Enter full name of Initial Contributor] Address of Initial Contributor: ________________________________________________ -  + ________________________________________________ -  + ________________________________________________ -  + [Enter address above] The Designated Web Site is: __________________________________________________ -  + [Enter URL for Designated Web Site of Initial Contributor] NOTE: The Initial Contributor is to complete this Part 1, along with Parts 2, 3, and 5, and, if applicable, Parts 4 and 6. @@ -237,27 +237,27 @@ The date on which the Initial Work was first available under this License: _____ PART 3: GOVERNING JURISDICTION -For the purposes of this License, the Governing Jurisdiction is _________________________________________________. 
[Initial Contributor to Enter Governing Jurisdiction here] +For the purposes of this License, the Governing Jurisdiction is _________________________________________________. [Initial Contributor to Enter Governing Jurisdiction here] PART 4: THIRD PARTIES For the purposes of this License, "Third Party" has the definition set forth below in the ONE paragraph selected by the Initial Contributor from paragraphs A, B, C, D and E when the Initial Work is distributed or otherwise made available by the Initial Contributor. To select one of the following paragraphs, the Initial Contributor must place an "X" or "x" in the selection box alongside the one respective paragraph selected. SELECTION -  + BOX PARAGRAPH -[  ] A. "THIRD PARTY" means any third party. -  -  -[  ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b). -  -  -[  ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest. -  -  -[  ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. -  -  -[  ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. +[ ] A. "THIRD PARTY" means any third party. + + +[ ] B. "THIRD PARTY" means any third party except for any of the following: (a) a wholly owned subsidiary of the Subsequent Contributor in question; (b) a legal entity (the "PARENT") that wholly owns the Subsequent Contributor in question; or (c) a wholly owned subsidiary of the wholly owned subsidiary in (a) or of the Parent in (b). + + +[ ] C. "THIRD PARTY" means any third party except for any of the following: (a) any Person directly or indirectly owning a majority of the voting interest in the Subsequent Contributor or (b) any Person in which the Subsequent Contributor directly or indirectly owns a majority voting interest. + + +[ ] D. "THIRD PARTY" means any third party except for any Person directly or indirectly controlled by the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. + + +[ ] E. "THIRD PARTY" means any third party except for any Person directly or indirectly controlling, controlled by, or under common control with the Subsequent Contributor. For purposes of this definition, "control" shall mean the power to direct or cause the direction of, the management and policies of such Person whether through the ownership of voting interests, by contract, or otherwise. The default definition of "THIRD PARTY" is the definition set forth in paragraph A, if NONE OR MORE THAN ONE of paragraphs A, B, C, D or E in this Part 4 are selected by the Initial Contributor. PART 5: NOTICE @@ -271,8 +271,8 @@ PART 6: PATENT LICENSING TERMS For the purposes of this License, paragraphs A, B, C, D and E of this Part 6 of Exhibit A are only incorporated and form part of the terms of the License if the Initial Contributor places an "X" or "x" in the selection box alongside the YES answer to the question immediately below. Is this a Patents-Included License pursuant to Section 2.2 of the License? -YES [      ] -NO [      ] +YES [ ] +NO [ ] By default, if YES is not selected by the Initial Contributor, the answer is NO. diff --git a/options/license/BSD-2-clause-first-lines b/options/license/BSD-2-clause-first-lines new file mode 100644 index 0000000000..3774caf24a --- /dev/null +++ b/options/license/BSD-2-clause-first-lines @@ -0,0 +1,27 @@ +Copyright (C) 2006,2007,2009 NTT (Nippon Telegraph and Telephone +Corporation). All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer as the first lines of this file unmodified. + +2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY NTT "AS IS" AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL NTT BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/options/license/IBM-pibs b/options/license/IBM-pibs index 49454b8b1e..ee9c7be36d 100644 --- a/options/license/IBM-pibs +++ b/options/license/IBM-pibs @@ -4,5 +4,5 @@ Any user of this software should understand that IBM cannot provide technical su Any person who transfers this source code or any derivative work must include the IBM copyright notice, this paragraph, and the preceding two paragraphs in the transferred software. -COPYRIGHT   I B M   CORPORATION 2002 -LICENSED MATERIAL  -  PROGRAM PROPERTY OF I B M +COPYRIGHT I B M CORPORATION 2002 +LICENSED MATERIAL - PROGRAM PROPERTY OF I B M diff --git a/options/license/NCGL-UK-2.0 b/options/license/NCGL-UK-2.0 index 31fbad6f83..15c4f63c22 100644 --- a/options/license/NCGL-UK-2.0 +++ b/options/license/NCGL-UK-2.0 @@ -12,15 +12,15 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations. You are free to: - copy, publish, distribute and transmit the Information; + copy, publish, distribute and transmit the Information; adapt the Information; exploit the Information for Non-Commercial purposes for example, by combining it with other information in your own product or application. You are not permitted to: - exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. + exercise any of the rights granted to you by this licence in any manner that is primarily intended for or directed toward commercial advantage or private monetary compensation. You must, where you do any of the above: - acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; + acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; If the Information Provider does not provide a specific attribution statement, you must use the following: Contains information licensed under the Non-Commercial Government Licence v2.0. diff --git a/options/license/NPL-1.1 b/options/license/NPL-1.1 index 62c5296400..0d5457ff04 100644 --- a/options/license/NPL-1.1 +++ b/options/license/NPL-1.1 @@ -2,7 +2,7 @@ Netscape Public LIcense version 1.1 AMENDMENTS -The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License.  Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. +The Netscape Public License Version 1.1 ("NPL") consists of the Mozilla Public License Version 1.1 with the following Amendments, including Exhibit A-Netscape Public License. Files identified with "Exhibit A-Netscape Public License" are governed by the Netscape Public License Version 1.1. Additional Terms applicable to the Netscape Public License. @@ -28,7 +28,7 @@ Additional Terms applicable to the Netscape Public License. Notwithstanding the limitations of Section 11 above, the provisions regarding litigation in Section 11(a), (b) and (c) of the License shall apply to all disputes relating to this License. EXHIBIT A-Netscape Public License. -   + "The contents of this file are subject to the Netscape Public License Version 1.1 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.mozilla.org/NPL/ Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. @@ -37,8 +37,8 @@ The Original Code is Mozilla Communicator client code, released March 31, 1998. The Initial Developer of the Original Code is Netscape Communications Corporation. Portions created by Netscape are Copyright (C) 1998-1999 Netscape Communications Corporation. All Rights Reserved. Contributor(s): ______________________________________. -   -Alternatively, the contents of this file may be used under the terms of the _____ license (the  "[___] License"), in which case the provisions of [______] License are applicable  instead of those above.  If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting  the provisions above and replace  them with the notice and other provisions required by the [___] License.  If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." + +Alternatively, the contents of this file may be used under the terms of the _____ license (the "[___] License"), in which case the provisions of [______] License are applicable instead of those above. If you wish to allow use of your version of this file only under the terms of the [____] License and not to allow others to use your version of this file under the NPL, indicate your decision by deleting the provisions above and replace them with the notice and other provisions required by the [___] License. If you do not delete the provisions above, a recipient may use your version of this file under either the NPL or the [___] License." Mozilla Public License Version 1.1 diff --git a/options/license/OCCT-PL b/options/license/OCCT-PL index 85df3c73c5..9b6fccc1c9 100644 --- a/options/license/OCCT-PL +++ b/options/license/OCCT-PL @@ -6,7 +6,7 @@ OPEN CASCADE releases and makes publicly available the source code of the softwa It is not the purpose of this license to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this license has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. Please read this license carefully and completely before downloading this software. By downloading, using, modifying, distributing and sublicensing this software, you indicate your acceptance to be bound by the terms and conditions of this license. If you do not want to accept or cannot accept for any reasons the terms and conditions of this license, please do not download or use in any manner this software. -  + 1. Definitions Unless there is something in the subject matter or in the context inconsistent therewith, the capitalized terms used in this License shall have the following meaning. @@ -26,13 +26,13 @@ Unless there is something in the subject matter or in the context inconsistent t "Software": means the Original Code, the Modifications, the combination of Original Code and any Modifications or any respective portions thereof. "You" or "Your": means an individual or a legal entity exercising rights under this License -  + 2. Acceptance of license By using, reproducing, modifying, distributing or sublicensing the Software or any portion thereof, You expressly indicate Your acceptance of the terms and conditions of this License and undertake to act in accordance with all the provisions of this License applicable to You. -  + 3. Scope and purpose This License applies to the Software and You may not use, reproduce, modify, distribute, sublicense or circulate the Software, or any portion thereof, except as expressly provided under this License. Any attempt to otherwise use, reproduce, modify, distribute or sublicense the Software is void and will automatically terminate Your rights under this License. -  + 4. Contributor license Subject to the terms and conditions of this License, the Initial Developer and each of the Contributors hereby grant You a world-wide, royalty-free, irrevocable and non-exclusive license under the Applicable Intellectual Property Rights they own or control, to use, reproduce, modify, distribute and sublicense the Software provided that: diff --git a/options/license/OGL-UK-1.0 b/options/license/OGL-UK-1.0 index a761c9916f..867c0e353b 100644 --- a/options/license/OGL-UK-1.0 +++ b/options/license/OGL-UK-1.0 @@ -10,20 +10,20 @@ The Licensor grants you a worldwide, royalty-free, perpetual, non-exclusive lice This licence does not affect your freedom under fair dealing or fair use or any other copyright or database right exceptions and limitations. You are free to: - copy, publish, distribute and transmit the Information; + copy, publish, distribute and transmit the Information; adapt the Information; exploit the Information commercially for example, by combining it with other Information, or by including it in your own product or application. You must, where you do any of the above: - acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; -  If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following:
 Contains public sector information licensed under the Open Government Licence v1.0. + acknowledge the source of the Information by including any attribution statement specified by the Information Provider(s) and, where possible, provide a link to this licence; + If the Information Provider does not provide a specific attribution statement, or if you are using Information from several Information Providers and multiple attributions are not practical in your product or application, you may consider using the following: Contains public sector information licensed under the Open Government Licence v1.0. ensure that you do not use the Information in a way that suggests any official status or that the Information Provider endorses you or your use of the Information; ensure that you do not mislead others or misrepresent the Information or its source; ensure that your use of the Information does not breach the Data Protection Act 1998 or the Privacy and Electronic Communications (EC Directive) Regulations 2003. These are important conditions of this licence and if you fail to comply with them the rights granted to you under this licence, or any similar licence granted by the Licensor, will end automatically. - Exemptions + Exemptions This licence does not cover the use of: - personal data in the Information; @@ -48,22 +48,22 @@ Definitions In this licence, the terms below have the following meanings: -‘Information’
means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence. +‘Information’ means information protected by copyright or by database right (for example, literary and artistic works, content, data and source code) offered for use under the terms of this licence. -‘Information Provider’
means the person or organisation providing the Information under this licence. +‘Information Provider’ means the person or organisation providing the Information under this licence. -‘Licensor’
means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence. +‘Licensor’ means any Information Provider which has the authority to offer Information under the terms of this licence or the Controller of Her Majesty’s Stationery Office, who has the authority to offer Information subject to Crown copyright and Crown database rights and Information subject to copyright and database right that has been assigned to or acquired by the Crown, under the terms of this licence. -‘Use’
as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format. +‘Use’ as a verb, means doing any act which is restricted by copyright or database right, whether in the original medium or in any other medium, and includes without limitation distributing, copying, adapting, modifying as may be technically necessary to use it in a different mode or format. -‘You’
means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence. +‘You’ means the natural or legal person, or body of persons corporate or incorporate, acquiring rights under this licence. About the Open Government Licence The Controller of Her Majesty’s Stationery Office (HMSO) has developed this licence as a tool to enable Information Providers in the public sector to license the use and re-use of their Information under a common open licence. The Controller invites public sector bodies owning their own copyright and database rights to permit the use of their Information under this licence. -The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework. +The Controller of HMSO has authority to license Information subject to copyright and database right owned by the Crown. The extent of the Controller’s offer to license this Information under the terms of this licence is set out in the UK Government Licensing Framework. This is version 1.0 of the Open Government Licence. The Controller of HMSO may, from time to time, issue new versions of the Open Government Licence. However, you may continue to use Information licensed under this version should you wish to do so. These terms have been aligned to be interoperable with any Creative Commons Attribution Licence, which covers copyright, and Open Data Commons Attribution License, which covers database rights and applicable copyrights. -Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website. +Further context, best practice and guidance can be found in the UK Government Licensing Framework section on The National Archives website. diff --git a/options/license/OSET-PL-2.1 b/options/license/OSET-PL-2.1 index 15f0c7758c..e0ed2e1398 100644 --- a/options/license/OSET-PL-2.1 +++ b/options/license/OSET-PL-2.1 @@ -100,7 +100,8 @@ If it is impossible for You to comply with any of the terms of this License with 5.1 Failure to Comply The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60-days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30-days after Your receipt of the notice. - 5.2 Patent Infringement Claims
 If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. + 5.2 Patent Infringement Claims + If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3 Additional Compliance Terms Notwithstanding the foregoing in this Section 5, for purposes of this Section, if You breach Section 3.1 (Distribution of Source Form), Section 3.2 (Distribution of Executable Form), Section 3.3 (Distribution of a Larger Work), or Section 3.4 (Notices), then becoming compliant as described in Section 5.1 must also include, no later than 30 days after receipt by You of notice of such violation by a Contributor, making the Covered Software available in Source Code Form as required by this License on a publicly available computer network for a period of no less than three (3) years. diff --git a/options/license/SHL-2.0 b/options/license/SHL-2.0 index e522a396fe..9218b47a72 100644 --- a/options/license/SHL-2.0 +++ b/options/license/SHL-2.0 @@ -1,22 +1,22 @@ # Solderpad Hardware Licence Version 2.0 -This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation. +This licence (the “Licence”) operates as a wraparound licence to the Apache License Version 2.0 (the “Apache License”) and grants to You the rights, and imposes the obligations, set out in the Apache License (which can be found here: http://apache.org/licenses/LICENSE-2.0), with the following extensions. It must be read in conjunction with the Apache License. Section 1 below modifies definitions in the Apache License, and section 2 below replaces sections 2 of the Apache License. You may, at your option, choose to treat any Work released under this License as released under the Apache License (thus ignoring all sections written below entirely). Words in italics indicate changes rom the Apache License, but are indicative and not to be taken into account in interpretation. 1. The definitions set out in the Apache License are modified as follows: -Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below). +Copyright any reference to ‘copyright’ (whether capitalised or not) includes ‘Rights’ (as defined below). -Contribution also includes any design, as well as any work of authorship. +Contribution also includes any design, as well as any work of authorship. -Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof. +Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the interfaces of the Work and Derivative Works thereof. -Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). +Object form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). -Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). +Rights means copyright and any similar right including design right (whether registered or unregistered), semiconductor topography (mask) rights and database rights (but excluding Patents and Trademarks). -Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. -Work also includes a design or work of authorship, whether in Source form or other Object form. +Source form shall mean the preferred form for making modifications, including but not limited to source code, net lists, board layouts, CAD files, documentation source, and configuration files. +Work also includes a design or work of authorship, whether in Source form or other Object form. 2. Grant of Licence -2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist. +2.1 Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license under the Rights to reproduce, prepare Derivative Works of, make, adapt, repair, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form and do anything in relation to the Work as if the Rights did not exist. diff --git a/options/license/SHL-2.1 b/options/license/SHL-2.1 index 4815a9e5ed..c9ae53741f 100644 --- a/options/license/SHL-2.1 +++ b/options/license/SHL-2.1 @@ -19,7 +19,7 @@ The following definitions shall replace the corresponding definitions in the Apa "License" shall mean this Solderpad Hardware License version 2.1, being the terms and conditions for use, manufacture, instantiation, adaptation, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the Rights owner or entity authorized by the Rights owner that is granting the License. -  + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship or design. For the purposes of this License, Derivative Works shall not include works that remain reversibly separable from, or merely link (or bind by name) or physically connect to or interoperate with the Work and Derivative Works thereof. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form or the application of a Source form to physical material, including but not limited to compiled object code, generated documentation, the instantiation of a hardware design or physical object or material and conversions to other media types, including intermediate forms such as bytecodes, FPGA bitstreams, moulds, artwork and semiconductor topographies (mask works). diff --git a/options/license/SISSL b/options/license/SISSL index 7d6ad9d66c..af38d02d92 100644 --- a/options/license/SISSL +++ b/options/license/SISSL @@ -36,13 +36,13 @@ Sun Industry Standards Source License - Version 1.1 2.0 SOURCE CODE LICENSE - 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims:  + 2.1 The Initial Developer Grant The Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license, subject to third party intellectual property claims: (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer to use, reproduce, modify, display, perform, sublicense and distribute the Original Code (or portions thereof) with or without Modifications, and/or as part of a Larger Work; and (b) under Patents Claims infringed by the making, using or selling of Original Code, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Code (or portions thereof). (c) the licenses granted in this Section 2.1(a) and (b) are effective on the date Initial Developer first distributes Original Code under the terms of this License. - (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications.  + (d) Notwithstanding Section 2.1(b) above, no patent license is granted: 1) for code that You delete from the Original Code; 2) separate from the Original Code; or 3) for infringements caused by: i) the modification of the Original Code or ii) the combination of the Original Code with other software or devices, including but not limited to Modifications. 3.0 DISTRIBUTION OBLIGATIONS @@ -92,14 +92,14 @@ This License represents the complete agreement concerning subject matter hereof. EXHIBIT A - Sun Standards License -"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________. +"The contents of this file are subject to the Sun Standards License Version 1.1 (the "License"); You may not use this file except in compliance with the License. You may obtain a copy of the License at _______________________________. -Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either  +Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. The Original Code is ______________________________________. -The Initial Developer of the Original Code is:  +The Initial Developer of the Original Code is: Sun Microsystems, Inc.. Portions created by: _______________________________________ diff --git a/options/license/Sun-PPP-2000 b/options/license/Sun-PPP-2000 new file mode 100644 index 0000000000..914c19544a --- /dev/null +++ b/options/license/Sun-PPP-2000 @@ -0,0 +1,13 @@ +Copyright (c) 2000 by Sun Microsystems, Inc. +All rights reserved. + +Permission to use, copy, modify, and distribute this software and its +documentation is hereby granted, provided that the above copyright +notice appears in all copies. + +SUN MAKES NO REPRESENTATION OR WARRANTIES ABOUT THE SUITABILITY OF +THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR +ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR +DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES diff --git a/options/license/W3C-19980720 b/options/license/W3C-19980720 index a8554039ef..134879044d 100644 --- a/options/license/W3C-19980720 +++ b/options/license/W3C-19980720 @@ -4,7 +4,7 @@ Copyright (c) 1994-2002 World Wide Web Consortium, (Massachusetts Institute of T This W3C work (including software, documents, or other related items) is being provided by the copyright holders under the following license. By obtaining, using and/or copying this work, you (the licensee) agree that you have read, understood, and will comply with the following terms and conditions: -Permission to use, copy, modify, and distribute this software and its documentation, with or without modification,  for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: +Permission to use, copy, modify, and distribute this software and its documentation, with or without modification, for any purpose and without fee or royalty is hereby granted, provided that you include the following on ALL copies of the software and documentation or portions thereof, including modifications, that you make: 1. The full text of this NOTICE in a location viewable to users of the redistributed or derivative work. diff --git a/options/license/pkgconf b/options/license/pkgconf new file mode 100644 index 0000000000..b8b2ffd996 --- /dev/null +++ b/options/license/pkgconf @@ -0,0 +1,7 @@ +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +This software is provided 'as is' and without any warranty, express or +implied. In no event shall the authors be liable for any damages arising +from the use of this software. diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index ca1598fb1b..3e47d9e628 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -135,6 +135,9 @@ email_notifications.disable = Изключване на известията п delete_account = Изтриване на акаунта ви confirm_delete_account = Потвърждаване на изтриването email_notifications.onmention = Ел. поща само при споменаване +pronouns_unspecified = Непосочени +pronouns = Местоимения +gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig [packages] container.labels.value = Стойност @@ -157,6 +160,7 @@ settings.delete.success = Пакетът бе изтрит. settings.delete = Изтриване на пакета container.details.platform = Платформа settings.delete.error = Неуспешно изтриване на пакет. +installation = Инсталация [tool] hours = %d часа @@ -276,6 +280,7 @@ filter.private = Частни filter.is_mirror = Огледални filter.not_mirror = Не огледални copy_hash = Копиране на контролната сума +artifacts = Артефакти [repo] issues.context.edit = Редактиране @@ -289,7 +294,7 @@ settings.discord_username = Потребителско име issues.filter_sort.mostforks = Най-много разклонения activity = Дейност issues = Задачи -settings.update_settings = Обновяване на настройките +settings.update_settings = Запазване на настройките visibility = Видимост settings.site = Уебсайт watchers = Наблюдаващи @@ -410,8 +415,8 @@ repo_gitignore_helper = Изберете .gitignore шаблони. auto_init = Да се инициализира хранилище (Добавя .gitignore, License и README) template.issue_labels = Етикети за задачите migrate_items_labels = Етикети -issues.label_templates.title = Зареждане на предварително зададен набор от етикети -issues.label_templates.helper = Изберете набор от етикети +issues.label_templates.title = Зареждане на предв. зададен набор от етикети +issues.label_templates.helper = Изберете предв. зададен набор от етикети projects.template.desc = Шаблон projects.card_type.text_only = Само текст projects.card_type.images_and_text = Изображения и текст @@ -504,7 +509,7 @@ wiki.filter_page = Филтриране на страница wiki.back_to_wiki = Обратно към уики страницата wiki.wiki_page_revisions = Ревизии на уики страницата wiki.file_revision = Ревизия на страницата -activity.title.issues_created_by = %s създадена от %s +activity.title.issues_created_by = %s създадени от %s wiki.delete_page_notice_1 = Изтриването на уики страницата "%s" не може да бъде отменено. Продължаване? wiki.page_name_desc = Въведете име за тази уики страница. Някои специални имена са: "Home", "_Sidebar" и "_Footer". wiki.page_already_exists = Вече съществува уики страница със същото име. @@ -643,16 +648,16 @@ issues.filter_assginee_no_select = Всички изпълнители issues.filter_assginee_no_assignee = Без изпълнител activity.opened_prs_count_1 = Предложена заявка за сливане activity.opened_prs_count_n = Предложени заявки за сливане -activity.title.prs_merged_by = %s слята от %s +activity.title.prs_merged_by = %s слети от %s activity.merged_prs_label = Слята activity.opened_prs_label = Предложена -activity.title.issues_closed_from = %s затворена от %s +activity.title.issues_closed_from = %s затворени от %s activity.closed_issue_label = Затворена activity.new_issue_label = Отворена activity.title.releases_1 = %d издание activity.title.releases_n = %d издания milestones.completeness = %d%% Завършен -activity.title.prs_opened_by = %s предложена от %s +activity.title.prs_opened_by = %s предложени от %s issues.action_milestone_no_select = Без етап issues.action_assignee_no_select = Без изпълнител milestones.edit = Редактиране на етапа @@ -671,7 +676,7 @@ issues.action_assignee = Изпълнител milestones.closed = Затворен %s milestones.open = Отваряне milestones.close = Затваряне -issues.label_templates.use = Използване на набор от етикети +issues.label_templates.use = Използване на предв. зададен набор от етикети issues.add_milestone_at = `добави това към етапа %s %s` issues.add_label = добави етикета %s %s issues.add_labels = добави етикети %s %s @@ -740,7 +745,7 @@ issues.due_date_form_edit = Редактиране issues.due_date_form_remove = Премахване issues.due_date_modified = промени крайния срок от %[2]s на %[1]s %[3]s pulls.compare_changes = Нова заявка за сливане -activity.title.releases_published_by = %s публикувано от %s +activity.title.releases_published_by = %s публикувани от %s topic.manage_topics = Управление на темите topic.done = Готово find_file.go_to_file = Отиване към файл @@ -1105,6 +1110,24 @@ issues.filter_type.reviewed_by_you = Рецензирани от вас issues.filter_type.review_requested = Поискани рецензии issues.review.review = Рецензия issues.review.comment = рецензира %s +branch.deleted_by = Изтрит от %s +branch.restore = Възстановяване на клона "%s" +archive.title_date = Това хранилище е архивирано на %s. Можете да преглеждате файлове и да го клонирате, но не можете да изтласквате или отваряте задачи или заявки за сливане. +release.download_count_one = %s изтегляне +release.download_count_few = %s изтегляния +branch.restore_success = Клонът "%s" е възстановен. +tag.create_tag_from = Създаване на нов маркер от "%s" +branch.create_new_branch = Създаване на клон от клон: +pulls.status_checks_show_all = Показване на всички проверки +size_format = %[1]s: %[2]s; %[3]s: %[4]s +pulls.filter_changes_by_commit = Филтриране по подаване +issues.ref_closing_from = `спомена заявка за сливане %[4]s, която ще затвори тази задача %[2]s` +issues.ref_from = `от %[1]s` +issues.ref_reopening_from = `спомена заявка за сливане %[4]s, която ще отвори наново тази задача %[2]s` +issues.draft_title = Чернова +pulls.reopen_to_merge = Моля, отворете наново тази заявка за сливане, за да извършите сливане. +pulls.cant_reopen_deleted_branch = Тази заявка за сливане не може да бъде отворена наново, защото клонът бе изтрит. +pulls.status_checks_hide_all = Скриване на всички проверки [modal] confirm = Потвърждаване @@ -1262,6 +1285,7 @@ issue.action.review = @%[1]s коментира в тази заявка issue.action.reopen = @%[1]s отвори наново #%[2]d. issue.action.approve = @%[1]s одобри тази заявка за сливане. issue.action.reject = @%[1]s поиска промени в тази заявка за сливане. +register_notify.title = %[1]s, добре дошли в %[2]s [user] joined_on = Присъединени на %s @@ -1286,6 +1310,8 @@ email_visibility.limited = Вашият адрес на ел. поща е вид disabled_public_activity = Този потребител е изключил публичната видимост на дейността. email_visibility.private = Вашият адрес на ел. поща е видим само за вас и администраторите show_on_map = Показване на това място на картата +followers_one = %d последовател +following_one = %d следван [home] filter = Други филтри @@ -1381,6 +1407,7 @@ packages.type = Тип orgs.teams = Екипи orgs.members = Участници config_settings = Настройки +users.details = Потребителски данни [error] not_found = Целта не може да бъде намерена. @@ -1416,6 +1443,12 @@ team_name_been_taken = Името на екипа вече е заето. org_name_been_taken = Името на организацията вече е заето. still_own_packages = Вашият акаунт притежава един или повече пакети, първо ги изтрийте. still_own_repo = Вашият акаунт притежава едно или повече хранилища, първо ги изтрийте или прехвърлете. +FullName = Пълно име +Description = Описание +Pronouns = Местоимения +Biography = Биография +Website = Уебсайт +Location = Местоположение [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -1541,6 +1574,9 @@ less = По-малко number_of_contributions_in_the_last_12_months = %s приноса през последните 12 месеца contributions_zero = Няма приноси more = Повече +contributions_one = принос +contributions_few = приноса +contributions_format = {contributions} на {day} {month}, {year} [git.filemode] directory = Директория @@ -1579,6 +1615,7 @@ commit_kind = Търсене на подавания... project_kind = Търсене на проекти... package_kind = Търсене на пакети... search = Търсене... +branch_kind = Търсене на клонове... [markup] filepreview.lines = Редове от %[1]d до %[2]d в %[3]s diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index fd7d8b03fb..2cdec52aef 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -4,13 +4,13 @@ dashboard=Přehled explore=Procházet help=Nápověda logo=Logo -sign_in=Přihlásit se +sign_in=Přihlášení sign_in_with_provider=Přihlásit se pomocí %s sign_in_or=nebo sign_out=Odhlásit se -sign_up=Registrovat se +sign_up=Registrace link_account=Propojit účet -register=Registrovat se +register=Registrace version=Verze powered_by=Běží na %s page=Strana @@ -21,7 +21,7 @@ active_stopwatch=Aktivní sledování času tracked_time_summary=Shrnutí sledovaného času na základě filtrů v seznamu problémů create_new=Vytvořit… user_profile_and_more=Profil a nastavení… -signed_in_as=Přihlášen jako +signed_in_as=Přihlášen/a jako enable_javascript=Tato stránka vyžaduje JavaScript. toc=Obsah licenses=Licence @@ -35,18 +35,18 @@ re_type=Potvrzení hesla captcha=CAPTCHA twofa=Dvoufaktorové ověřování twofa_scratch=Dvoufaktorový kód -passcode=Přístupový kód +passcode=Passcode webauthn_insert_key=Vložte svůj bezpečnostní klíč webauthn_sign_in=Stiskněte tlačítko na svém bezpečnostním klíči. Pokud bezpečnostní klíč nemá žádné tlačítko, vložte jej znovu. -webauthn_press_button=Stiskněte prosím tlačítko na zabezpečovacím klíči… +webauthn_press_button=Stiskněte prosím tlačítko na bezpečnostním klíči… webauthn_use_twofa=Použít dvoufaktorový kód z vašeho telefonu -webauthn_error=Nepodařilo se přečíst váš zabezpečovací klíč. +webauthn_error=Nepodařilo se přečíst váš bezpečnostní klíč. webauthn_unsupported_browser=Váš prohlížeč momentálně nepodporuje WebAuthn. webauthn_error_unknown=Došlo k neznámé chybě. Opakujte akci. -webauthn_error_insecure=`WebAuthn podporuje pouze zabezpečená připojení. Pro testování přes HTTP můžete použít výchozí "localhost" nebo "127.0.0.1"` +webauthn_error_insecure=WebAuthn podporuje pouze zabezpečená připojení. Pro testování přes HTTP můžete použít výchozí „localhost“ nebo „127.0.0.1“ webauthn_error_unable_to_process=Server nemohl zpracovat váš požadavek. -webauthn_error_duplicated=Zabezpečovací klíč není pro tento požadavek povolen. Prosím ujistěte se, zda klíč není již registrován. +webauthn_error_duplicated=Bezpečnostní klíč není pro tento požadavek povolen. Ujistěte se prosím, zda již klíč není registrován. webauthn_error_empty=Musíte nastavit název tohoto klíče. webauthn_error_timeout=Požadavek vypršel dříve, než se podařilo přečíst váš klíč. Znovu načtěte tuto stránku a akci opakujte. webauthn_reload=Znovu načíst @@ -94,22 +94,22 @@ remove_label_str=Odstranit položku „%s“ edit=Upravit view=Zobrazit -enabled=Povolený -disabled=Zakázané +enabled=Povoleno +disabled=Zakázáno locked=Uzamčeno copy=Kopírovat copy_url=Kopírovat URL copy_hash=Kopírovat hash copy_content=Kopírovat obsah -copy_branch=Kopírovat jméno větve +copy_branch=Kopírovat název větve copy_success=Zkopírováno! copy_error=Kopírování se nezdařilo copy_type_unsupported=Tento typ souboru nelze zkopírovat -write=Psaní +write=Zápis preview=Náhled -loading=Načítá se… +loading=Načítání… error=Chyba error404=Stránka, kterou se snažíte zobrazit, buď neexistuje, nebo nemáte oprávnění ji zobrazit. @@ -135,7 +135,7 @@ concept_user_organization=Organizace show_timestamps=Zobrazit časové značky show_log_seconds=Zobrazit sekundy -show_full_screen=Zobrazit celou obrazovku +show_full_screen=Zobrazit na celé obrazovce download_logs=Stáhnout protokoly confirm_delete_selected=Potvrdit odstranění všech vybraných položek? @@ -162,7 +162,7 @@ invalid_data = Neplatná data: %v [aria] navbar=Navigační lišta -footer=Patička +footer=Zápatí footer.software=O softwaru footer.links=Odkazy @@ -179,17 +179,17 @@ contributions_few = příspěvků buttons.heading.tooltip=Přidat nadpis buttons.bold.tooltip=Přidat tučný text buttons.italic.tooltip=Přidat kurzívu -buttons.quote.tooltip=Do uvozovek +buttons.quote.tooltip=Citace buttons.code.tooltip=Přidat kód buttons.link.tooltip=Přidat odkaz -buttons.list.unordered.tooltip=Přidat seznam odrážek +buttons.list.unordered.tooltip=Přidat odrážkový seznam buttons.list.ordered.tooltip=Přidat číslovaný seznam buttons.list.task.tooltip=Přidat seznam úkolů -buttons.mention.tooltip=Uveďte uživatele nebo tým -buttons.ref.tooltip=Odkaz na issue nebo pull request -buttons.switch_to_legacy.tooltip=Místo toho použít starší editor -buttons.enable_monospace_font=Zapnout monospace font -buttons.disable_monospace_font=Vypnout monospace font +buttons.mention.tooltip=Zmínit uživatele nebo tým +buttons.ref.tooltip=Odkaz na problém nebo žádost o sloučení +buttons.switch_to_legacy.tooltip=Použít starší editor +buttons.enable_monospace_font=Zapnout neproporcionální písmo +buttons.disable_monospace_font=Vypnout neproporcionální písmo [filter] string.asc=A – Z @@ -197,28 +197,28 @@ string.desc=Z – A [error] occurred=Došlo k chybě -report_message=Pokud jste si jisti, že se jedná o chybu služby Forgejo, vyhledejte prosím problémy na Codebergu a v případě potřeby založte nový problém. -missing_csrf=Špatný požadavek: Neexistuje CSRF token -invalid_csrf=Špatný požadavek: Neplatný CSRF token +report_message=Pokud jste si jisti, že se jedná o chybu software Forgejo, vyhledejte prosím problémy ve službě Codeberg a v případě potřeby založte nový problém. +missing_csrf=Nesprávný požadavek: nenalezen token CSRF +invalid_csrf=Nesprávný požadavek: neplatný token CSRF not_found=Cíl nebyl nalezen. network_error=Chyba sítě server_internal = Interní chyba serveru [startpage] -app_desc=Snadno přístupný vlastní Git -install=Jednoduchá na instalaci +app_desc=Snadno přístupná vlastní Git služba +install=Jednoduché na instalaci install_desc=Jednoduše spusťte binární soubor pro vaši platformu, nasaďte jej pomocí Dockeru nebo si jej stáhněte jako balíček. platform=Multiplatformní platform_desc=Forgejo běží na všech platformách, na které dokáže kompilovat jazyk Go: Windows, macOS, Linux, ARM, atd. Výběr je opravdu velký! -lightweight=Lehká -lightweight_desc=Forgejo má minimální požadavky a může běžet na Raspberry Pi. Šetřete energii vašeho stroje! +lightweight=Lehké +lightweight_desc=Forgejo má nízké minimální požadavky a dokáže běžet i na levném Raspberry Pi. Šetřete energii vašeho stroje! license=Open Source -license_desc=Vše je na Forgejo! Připojte se tím, že přispějete a uděláte tento projekt ještě lepší. Nestyďte se být přispěvatel! +license_desc=Vyzkoušejte Forgejo! Připojte se k nám, přispějte a vylepšete tento projekt. Nebojte se přispět! [install] install=Instalace title=Počáteční konfigurace -docker_helper=Pokud spouštíte Forgejo v Dockeru, přečtěte si dokumentaci, než budete měnit jakákoliv nastavení. +docker_helper=Pokud provozujete Forgejo v Dockeru, před změnou nastavení si přečtěte dokumentaci. require_db_desc=Forgejo vyžaduje MySQL, PostgreSQL, SQLite3 nebo TiDB (protokol MySQL). db_title=Nastavení databáze db_type=Typ databáze @@ -230,71 +230,71 @@ db_schema=Schéma db_schema_helper=Ponechte prázdné pro výchozí nastavení databáze („public“). ssl_mode=SSL path=Cesta -sqlite_helper=Cesta k souboru SQLite3 databáze.
    Pokud spouštíte Forgejo jako službu, zadejte absolutní cestu. -reinstall_error=Pokoušíte se nainstalovat do existující databáze Forgejo -reinstall_confirm_message=Přeinstalování s existující databází Forgejo může způsobit více problémů. Ve většině případů byste měli použít existující „app.ini“ pro spuštění Forgejo. Pokud víte, co děláte, potvrďte následující: +sqlite_helper=Cesta k souboru databáze SQLite3.
    Pokud provozujete Forgejo jako službu, zadejte absolutní cestu. +reinstall_error=Pokoušíte se provést instalaci do existující databáze Forgejo +reinstall_confirm_message=Přeinstalování s existující databází Forgejo může způsobit spoustu problémů. Ve většině případů byste měli ke spouštění Forgejo použít existující soubor „app.ini“. Pokud víte, co děláte, potvrďte následující: reinstall_confirm_check_1=Data šifrovaná pomocí SECRET_KEY v souboru api.ini mohou být ztracena: uživatelé nemusí být schopni se přihlásit s 2FA/OTP a zrcadla nemusí fungovat správně. Zaškrtnutím tohoto políčka potvrdíte, že aktuální soubor app.ini obsahuje správný SECRET_KEY. -reinstall_confirm_check_2=Může být nutné znovu synchronizovat repozitáře a nastavení. Zaškrtnutím tohoto políčka potvrzujete, že budete háčky pro repozitáře a soubor authorized_keys znovu synchronizovat ručně. Potvrzujete, že zajistíte správnost nastavení repozitáře a zrcadla. -reinstall_confirm_check_3=Potvrzujete, že jste si naprosto jisti, že tato Forgejo je spuštěna se správným umístěním souboru app.ini a že jste si jisti, že musíte provést novou instalaci. Potvrzujete, že berete na vědomí výše uvedená rizika. -err_empty_db_path=Cesta k SQLite3 databázi nemůže být prázdná. -no_admin_and_disable_registration=Nemůžete vypnout registraci účtů bez vytvoření účtu správce. +reinstall_confirm_check_2=Může být nutné znovu synchronizovat repozitáře a nastavení. Zaškrtnutím tohoto pole potvrzujete, že ručně znovu synchronizujete hooky repozitářů a soubor authorized_keys. Potvrzujete, že zajistíte správnost nastavení repozitářů a zrcadel. +reinstall_confirm_check_3=Potvrzujete, že jste si naprosto jisti, že tato instance Forgejo je spuštěna se správným umístěním souboru app.ini a že jste si vědomi toho, že musíte provést novou instalaci. Potvrzujete, že berete na vědomí výše uvedená rizika. +err_empty_db_path=Cesta k databázi SQLite3 nemůže být prázdná. +no_admin_and_disable_registration=Nelze vypnout registraci účtů bez vytvoření účtu administrátora. err_empty_admin_password=Heslo administrátora nemůže být prázdné. -err_empty_admin_email=Email administrátora nemůže být prázdný. +err_empty_admin_email=E-mail administrátora nemůže být prázdný. err_admin_name_is_reserved=Uživatelské jméno administrátora není platné, uživatelské jméno je rezervované err_admin_name_pattern_not_allowed=Uživatelské jméno administrátora je neplatné, uživatelské jméno odpovídá vyhrazenému vzoru err_admin_name_is_invalid=Uživatelské jméno administrátora není platné general_title=Obecná nastavení app_name=Název instance -app_name_helper=Zde můžete zadat název vaší společnosti. +app_name_helper=Sem můžete zadat název vaší společnosti. repo_path=Kořenový adresář repozitářů repo_path_helper=Všechny vzdálené repozitáře Gitu budou uloženy do tohoto adresáře. lfs_path=Kořenový adresář Git LFS -lfs_path_helper=V tomto adresáři budou uloženy soubory, které jsou sledovány Git LFS. Pokud ponecháte prázdné, LFS zakážete. +lfs_path_helper=V tomto adresáři budou uloženy soubory, které jsou sledovány službou Git LFS. Ponechte prázdné pro zakázání. run_user=Spustit jako uživatel -run_user_helper=Uživatelské jméno, pod kterým běží služba Forgejo v operačním systému. Pozor: tento uživatel musí mít přístup ke kořenovému adresáři repozitářů. +run_user_helper=Uživatelské jméno, pod kterým běží software Forgejo v operačním systému. Pozor: tento uživatel musí mít přístup ke kořenovému adresáři repozitářů. domain=Doména serveru -domain_helper=Adresa domény, nebo hostitele serveru. +domain_helper=Doména nebo adresa hostitele serveru. ssh_port=Port serveru SSH ssh_port_helper=Číslo portu, který bude použit pro server SSH. Ponechte prázdné pro zakázání serveru SSH. http_port=Port pro naslouchání HTTP http_port_helper=Číslo portu, který bude použit webovým serverem Forgejo. app_url=Základní URL -app_url_helper=Základní adresa pro HTTP(S) URL adresy pro klonování a e-mailová oznámení. +app_url_helper=Základní adresa pro HTTP(S) adresy URL pro klonování a e-mailová oznámení. log_root_path=Adresář protokolů -log_root_path_helper=Soubory protokolu budou zapsány do tohoto adresáře. +log_root_path_helper=Protokoly budou zapisovány do tohoto adresáře. optional_title=Volitelná nastavení email_title=Nastavení e-mailů smtp_addr=Hostitel SMTP smtp_port=Port SMTP smtp_from=Poslat e-mail jako -smtp_from_helper=E-mailová adresa, kterou bude Forgejo používat. Zadejte běžnou e-mailovou adresu, nebo použijte formát "Jméno". +smtp_from_helper=E-mailová adresa, kterou bude používat software Forgejo. Zadejte běžnou e-mailovou adresu, nebo použijte formát "Jméno" . mailer_user=Uživatelské jméno SMTP mailer_password=Heslo SMTP register_confirm=Pro registraci vyžadovat potvrzení e-mailu mail_notify=Zapnout e-mailová oznámení server_service_title=Nastavení serveru a služeb třetích stran offline_mode=Povolit místní režim -offline_mode_popup=Zakázat sítě pro doručování obsahu a poskytovat veškerý obsah lokálně. +offline_mode_popup=Zakázat sítě třetích stran pro doručování obsahu a poskytovat veškerý obsah lokálně. disable_gravatar=Zakázat Gravatar -disable_gravatar_popup=Zakážete Gravatar a jiné cizí zdroje avatarů. Pokud uživatel nenahraje avatar, bude použit výchozí. +disable_gravatar_popup=Zakázat Gravatar a jiné zdroje avatarů třetích stran. Pokud uživatel nenahraje avatar, bude použit výchozí. federated_avatar_lookup=Povolit federované avatary -federated_avatar_lookup_popup=Povolte vyhledání avatarů z veřejných zdrojů pro využití služeb založených na libravatar. +federated_avatar_lookup_popup=Povolit federované vyhledání avatarů pomocí služby Libravatar. disable_registration=Zakázat uživatelské registrace -disable_registration_popup=Vypnout možnost registrace. Pouze správci budou moci vytvářet účty. +disable_registration_popup=Zakázat možnost registrace. Pouze administrátoři budou moci vytvářet nové uživatelské účty. allow_only_external_registration_popup=Povolit registraci pouze prostřednictvím externích služeb openid_signin=Povolit přihlášení pomocí OpenID -openid_signin_popup=Umožňuje uživateli přihlásit se pomocí OpenID. +openid_signin_popup=Povolit přihlášení pomocí služby OpenID. openid_signup=Povolit uživatelskou registraci pomocí OpenID -openid_signup_popup=Umožňuje uživateli automaticky se registrovat pomocí OpenID. +openid_signup_popup=Povolit uživatelům registrovat se pomocí OpenID. enable_captcha=Povolit CAPTCHA při registraci -enable_captcha_popup=Vyžadovat správně zadaný text CAPTCHA při registraci. +enable_captcha_popup=Vyžadovat CAPTCHA při uživatelské registraci. require_sign_in_view=Vyžadovat přihlášení pro zobrazení obsahu instance -require_sign_in_view_popup=Povolí přístup ke stránkám jen přihlášeným uživatelům. Návštěvníci uvidí jen přihlašovací a registrační stránky. -admin_setting_desc=Vytvoření účtu správce je nepovinné. První registrovaný uživatel se automaticky stane správcem. -admin_title=Nastavení účtu správce -admin_name=Uživatelské jméno správce +require_sign_in_view_popup=Povolit přístup ke stránkám jen přihlášeným uživatelům. Návštěvníci uvidí jen stránky přihlášení a registrace. +admin_setting_desc=Vytvoření administrátorského účtu je nepovinné. První zaregistrovaný uživatel se automaticky stane administrátorem. +admin_title=Nastavení administrátorského účtu +admin_name=Uživatelské jméno administrátora admin_password=Heslo confirm_password=Potvrzení hesla admin_email=E-mailová adresa @@ -303,33 +303,32 @@ test_git_failed=Chyba při testu příkazu „git“: %v sqlite3_not_available=Tato verze Forgejo nepodporuje SQLite3. Stáhněte si oficiální binární verzi z %s (nikoli verzi „gobuild“). invalid_db_setting=Nastavení databáze je neplatné: %v invalid_db_table=Databázová tabulka „%s“ je neplatná: %v -invalid_repo_path=Kořenový adresář repozitářů není správný: %v +invalid_repo_path=Kořenový adresář repozitářů je neplatný: %v invalid_app_data_path=Cesta k datům aplikace je neplatná: %v run_user_not_match=Uživatelské jméno v poli „spustit jako uživatel“ není aktuální uživatelské jméno: %s -> %s -internal_token_failed=Nepodařilo se vytvořit interní token: %v -secret_key_failed=Nepodařilo se vytvořit tajný klíč: %v -save_config_failed=Uložení konfigurace se nezdařilo: %v -invalid_admin_setting=Nastavení účtu správce není správné: %v -invalid_log_root_path=Kořenový adresář logů není správný: %v +internal_token_failed=Nepodařilo se vygenerovat interní token: %v +secret_key_failed=Nepodařilo se vygenerovat tajný klíč: %v +save_config_failed=Nepodařilo se uložit konfiguraci: %v +invalid_admin_setting=Nastavení administrátorského účtu je neplatné: %v +invalid_log_root_path=Kořenový adresář protokolů je neplatný: %v default_keep_email_private=Ve výchozím nastavení skrýt e-mailové adresy -default_keep_email_private_popup=Nastaví e-mailové adresy novým uživatelským účtům jako skryté. +default_keep_email_private_popup=Ve výchozím nastavení skrýt e-mailové adresy nových uživatelských účtů. default_allow_create_organization=Povolit novým uživatelům zakládat organizace -default_allow_create_organization_popup=Povolit novým uživatelským účtům vytvářet organizace. +default_allow_create_organization_popup=Ve výchozím nastavení povolit novým uživatelským účtům vytvářet organizace. default_enable_timetracking=Povolit ve výchozím nastavení sledování času -default_enable_timetracking_popup=Povolí sledování času pro nové repozitáře. +default_enable_timetracking_popup=Ve výchozím nastavení povolit u nových repozitářů sledovat čas. no_reply_address=Skrytá e-mailová doména no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. password_algorithm=Hashovací algoritmus hesla -invalid_password_algorithm=Neplatný algoritmus hash hesla +invalid_password_algorithm=Neplatný algoritmus hashe hesla password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. enable_update_checker=Povolit kontrolu aktualizací -enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke gitea.io. env_config_keys=Konfigurace prostředí env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor: enable_update_checker_helper_forgejo = Bude pravidelně kontrolovat nové verze Forgejo kontrolou TXT DNS záznamu na adrese release.forgejo.org. allow_dots_in_usernames = Povolit uživatelům používat tečky ve svých uživatelských jménech. Neovlivní stávající účty. smtp_from_invalid = Adresa v poli „Poslat e-mail jako“ je neplatná -config_location_hint = Tyto možnosti konfigurace budou uloženy do: +config_location_hint = Tyto konfigurační možnosti budou uloženy do: [home] uname_holder=Uživatelské jméno nebo e-mailová adresa @@ -343,16 +342,16 @@ my_mirrors=Má zrcadla view_home=Zobrazit %s search_repos=Nalézt repozitář… filter=Ostatní filtry -filter_by_team_repositories=Filtrovat podle repozitářů týmu -feed_of=Kanál z „%s“ +filter_by_team_repositories=Filtrovat podle týmových repozitářů +feed_of=Kanál „%s“ show_archived=Archivováno -show_both_archived_unarchived=Zobrazeny jak archivované tak nearchivované +show_both_archived_unarchived=Zobrazeny archivované i nearchivované show_only_archived=Zobrazeny pouze archivované show_only_unarchived=Zobrazeny pouze nearchivované -show_private=Soukromý -show_both_private_public=Zobrazeny jak veřejné tak soukromé +show_private=Soukromé +show_both_private_public=Zobrazeny veřejné i soukromé show_only_private=Zobrazeny pouze soukromé show_only_public=Zobrazeny pouze veřejné @@ -388,42 +387,42 @@ stars_few = %d hvězd create_new_account=Registrovat účet register_helper_msg=Již máte účet? Přihlaste se! social_register_helper_msg=Již máte účet? Připojte ho! -disable_register_prompt=Registrace jsou vypnuty. Prosíme, kontaktujte správce systému. +disable_register_prompt=Registrace jsou vypnuty. Kontaktujte prosím administrátora webu. disable_register_mail=E-mailové potvrzení o registraci je zakázané. -manual_activation_only=Pro dokončení aktivace kontaktujte správce webu. +manual_activation_only=Pro dokončení aktivace kontaktujte administrátora webu. remember_me=Pamatovat si toto zařízení remember_me.compromised=Přihlašovací token již není platný, což může znamenat napadení účtu. Zkontrolujte prosím svůj účet pro neobvyklé aktivity. forgot_password_title=Zapomenuté heslo forgot_password=Zapomenuté heslo? -sign_up_now=Potřebujete účet? Zaregistrujte se. +sign_up_now=Nemáte účet? Zaregistrujte se. sign_up_successful=Účet byl úspěšně vytvořen. Vítejte! -confirmation_mail_sent_prompt=Na adresu %s byl zaslán nový potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces registrace. -must_change_password=Aktualizujte své heslo +confirmation_mail_sent_prompt=Na adresu %s byl zaslán nový potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces registrace. Pokud jste zadali nesprávný e-mail, můžete se přihlásit a požádat o poslání nového potvrzovacího e-mailu na jinou adresu. +must_change_password=Změňte své heslo allow_password_change=Vyžádat od uživatele změnu hesla (doporučeno) -reset_password_mail_sent_prompt=Na adresu %s byl zaslán potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s, abyste dokončili proces obnovení účtu. +reset_password_mail_sent_prompt=Na adresu %s byl zaslán potvrzovací e-mail. Zkontrolujte prosím vaši doručenou poštu během následujících %s pro dokončení procesu obnovení účtu. active_your_account=Aktivujte si váš účet account_activated=Účet byl aktivován prohibit_login=Přihlašování je zakázáno prohibit_login_desc=Vašemu účtu je zakázáno se přihlásit, kontaktujte prosím správce webu. -resent_limit_prompt=Omlouváme se, ale před chvílí jste požádal o zaslání aktivačního e-mailu. Počkejte prosím 3 minuty a pak to zkuste znovu. -has_unconfirmed_mail=Zdravím, %s, máte nepotvrzenou e-mailovou adresu (%s). Pokud jste nedostali e-mail pro potvrzení nebo potřebujete zaslat nový, klikněte prosím na tlačítku níže. -resend_mail=Klikněte zde pro odeslání aktivačního e-mailu +resent_limit_prompt=Omlouváme se, ale nedávno jste již požádali o zaslání aktivačního e-mailu. Počkejte prosím 3 minuty a zkuste to znovu. +has_unconfirmed_mail=Zdravíme, %s, máte nepotvrzenou e-mailovou adresu (%s). Pokud jste nedostali e-mail pro potvrzení nebo potřebujete zaslat nový, klikněte prosím na tlačítko níže. +resend_mail=Klikněte sem pro opětovné odeslání aktivačního e-mailu email_not_associate=Tato e-mailová adresa není spojena s žádným účtem. send_reset_mail=Zaslat e-mail pro obnovení účtu reset_password=Obnovení účtu -invalid_code=Tento potvrzující kód je neplatný nebo mu vypršela platnost. +invalid_code=Tento potvrzovací kód je neplatný nebo mu vypršela platnost. invalid_code_forgot_password=Váš potvrzovací kód je neplatný nebo mu vypršela platnost. Klikněte zde pro vytvoření nového kódu. invalid_password=Vaše heslo se neshoduje s heslem, které bylo použito k vytvoření účtu. reset_password_helper=Obnovit účet reset_password_wrong_user=Jste přihlášen/a jako %s, ale odkaz pro obnovení účtu je pro %s password_too_short=Délka hesla musí být minimálně %d znaků. -non_local_account=Externě ověřovaní uživatelé nemohou aktualizovat své heslo prostřednictvím webového rozhraní Forgejo. +non_local_account=Externě ověřovaní uživatelé nemohou změnit své heslo prostřednictvím webového rozhraní Forgejo. verify=Ověřit -scratch_code=Pomocný kód -use_scratch_code=Použijte pomocný kód -twofa_scratch_used=Použili jste váš pomocný kód. Byli jste přesměrování na stránku s nastavením dvoufaktorového ověřování, takže můžete odstranit registraci vašeho zařízení nebo vygenerovat nový pomocný kód. -twofa_passcode_incorrect=Vaše heslo je neplatné. Pokud jste ztratili vaše zařízení, použijte pomocný kód k přihlášení. -twofa_scratch_token_incorrect=Váš pomocný kód není správný. +scratch_code=Záložní kód +use_scratch_code=Použít záložní kód +twofa_scratch_used=Použili jste váš záložní kód. Byli jste přesměrování na stránku s nastavením dvoufaktorového ověřování, kde můžete odstranit registraci vašeho zařízení nebo vygenerovat nový záložní kód. +twofa_passcode_incorrect=Vaše heslo je neplatné. Pokud jste ztratili vaše zařízení, použijte záložní kód k přihlášení. +twofa_scratch_token_incorrect=Váš záložní kód není správný. login_userpass=Přihlásit se tab_openid=OpenID oauth_signup_tab=Zaregistrovat nový účet @@ -432,11 +431,11 @@ oauth_signup_submit=Dokončit účet oauth_signin_tab=Propojit s existujícím účtem oauth_signin_title=Přihlaste se pro ověření propojeného účtu oauth_signin_submit=Propojit účet -oauth.signin.error=Došlo k chybě při zpracování žádosti o autorizaci. Pokud tato chyba přetrvává, obraťte se na správce webu. +oauth.signin.error=Při zpracování žádosti o autorizaci došlo k chybě. Pokud tato chyba přetrvává, obraťte se na správce webu. oauth.signin.error.access_denied=Žádost o autorizaci byla zamítnuta. oauth.signin.error.temporarily_unavailable=Autorizace se nezdařila, protože ověřovací server je dočasně nedostupný. Opakujte akci později. openid_connect_submit=Připojit -openid_connect_title=Připojení k existujícímu účtu +openid_connect_title=Připojit k existujícímu účtu openid_connect_desc=Zvolené OpenID URI není známé. Přidružte nový účet zde. openid_register_title=Vytvořit nový účet openid_register_desc=Zvolené OpenID URI není známé. Přidružte nový účet zde. @@ -633,13 +632,22 @@ username_error_no_dots = ` může obsahovat pouze alfanumerické znaky („0-9 admin_cannot_delete_self=Nemůžete se smazat, dokud jste správce. Nejdříve prosím odeberte svá administrátorská oprávnění. unset_password = Tento uživatel nemá nastavené heslo. unsupported_login_type = U tohoto typu účtu není funkce odstranění účtu podporována. +required_prefix = Vstup musí začínat textem „%s“ +FullName = Celé jméno +Description = Popis +Pronouns = Zájmena +Website = Webová stránka +Location = Umístění +To = Název větve +Biography = Životopis +AccessToken = Přístupový token [user] change_avatar=Změnit váš avatar… joined_on=Přidal/a se %s repositories=Repozitáře activity=Veřejná aktivita -followers_few=%d sledující +followers_few=%d sledujících starred=Oblíbené repozitáře watched=Sledované repozitáře code=Kód @@ -666,6 +674,8 @@ block_user.detail_3 = Tento uživatel vás nebude moci přidat jako spolupracovn follow_blocked_user = Tohoto uživatele nemůžete sledovat, protože jste si jej zablokovali nebo si on zablokoval vás. block = Zablokovat unblock = Odblokovat +followers_one = %d sledující +following_one = %d následuje [settings] profile=Profil @@ -923,11 +933,11 @@ twofa_is_enrolled=Váš účet aktuálně používá dvoufaktor twofa_not_enrolled=Váš účet aktuálně nepoužívá dvoufaktorové ověřování. twofa_disable=Zakázat dvoufaktorové ověřování twofa_scratch_token_regenerate=Znovu vygenerovat jednorázový klíč pro obnovení -twofa_scratch_token_regenerated=Váš jednorázový obnovovací klíč je nyní %s. Uložte jej na bezpečném místě, protože se znovu nezobrazí. +twofa_scratch_token_regenerated=Váš jednorázový klíč pro obnovení je nyní %s. Uložte jej na bezpečné místo, protože se znovu nezobrazí. twofa_enroll=Povolit dvoufaktorové ověřování twofa_disable_note=Dvoufaktorové ověřování můžete zakázat, když bude potřeba. twofa_disable_desc=Zakážete-li dvoufaktorové ověřování, bude váš účet méně zabezpečený. Pokračovat? -regenerate_scratch_token_desc=Jestli jste někam založili váš pomocný token nebo jste jej již použili k přihlášení, můžete jej resetovat zde. +regenerate_scratch_token_desc=Pokud jste ztratili svůj klíč pro obnovení nebo jste jej již použili k přihlášení, můžete jej resetovat zde. twofa_disabled=Dvoufaktorové ověřování bylo zakázáno. scan_this_image=Naskenujte tento obrázek s vaší ověřovací aplikací: or_enter_secret=Nebo zadejte tajný kód: %s @@ -989,6 +999,9 @@ additional_repo_units_hint = Podíbnout k povolení dalších jednotek úložiš update_hints = Aktualizovat nápovědy update_hints_success = Nápovědy byly aktualizovány. additional_repo_units_hint_description = Zobrazit tlačítko „Přidat další jednotky...“ u repozitářů, které nemají povolené všechny dostupné jednotky. +pronouns = Zájmena +pronouns_custom = Vlastní +pronouns_unspecified = Neurčené [repo] new_repo_helper=Repozitář obsahuje všechny projektové soubory, včetně historie revizí. Už jej hostujete jinde? Migrovat repozitář. @@ -1924,7 +1937,7 @@ milestones.filter_sort.least_issues=Nejméně problémů signing.will_sign=Tento commit bude podepsána klíčem „%s“. signing.wont_sign.error=Došlo k chybě při kontrole, zda může být commit podepsán. -signing.wont_sign.nokey=K podpisu tohoto commitu není k dispozici žádný klíč. +signing.wont_sign.nokey=Tato instance nemá žádný klíč k podepsání tohoto commitu. signing.wont_sign.never=Commity nejsou nikdy podepsány. signing.wont_sign.always=Commity jsou vždy podepsány. signing.wont_sign.pubkey=Commit nebude podepsán, protože nemáte veřejný klíč spojený s vaším účtem. @@ -2079,7 +2092,7 @@ settings.mirror_settings.push_mirror.edit_sync_time=Upravit interval synchroniza settings.sync_mirror=Synchronizovat nyní settings.site=Webová stránka -settings.update_settings=Aktualizovat nastavení +settings.update_settings=Uložit nastavení settings.update_mirror_settings=Aktualizovat nastavení zrcadla settings.branches.switch_default_branch=Přepnout výchozí větev settings.branches.update_default_branch=Aktualizovat výchozí větev @@ -2337,7 +2350,7 @@ settings.protected_branch.delete_rule=Odstranit pravidlo settings.protected_branch_can_push=Povolit nahrání? settings.protected_branch_can_push_yes=Můžete nahrávat settings.protected_branch_can_push_no=Nemůžete nahrávat -settings.branch_protection=Pravidla ochrany větve pro větev „%s“ +settings.branch_protection=Pravidla ochrany pro větev „%s“ settings.protect_this_branch=Povolit ochranu větve settings.protect_this_branch_desc=Zabraňuje smazání a omezuje gitu nahrávání a slučování do větve. settings.protect_disable_push=Zakázat nahrávání @@ -2411,7 +2424,7 @@ settings.tags.protection.allowed.teams=Povolené týmy settings.tags.protection.allowed.noone=Nikdo settings.tags.protection.create=Přidat pravidlo settings.tags.protection.none=Neexistují žádné chráněné značky. -settings.tags.protection.pattern.description=Můžete použít jediné jméno nebo vzor glob nebo regulární výraz, který bude odpovídat více značek. Přečtěte si více v průvodci chráněnými značkami. +settings.tags.protection.pattern.description=Můžete použít jediné jméno nebo vzor glob nebo regulární výraz, který bude odpovídat více značek. Přečtěte si více v průvodci chráněnými značkami. settings.bot_token=Token bota settings.chat_id=ID chatu settings.thread_id=ID vlákna @@ -2724,6 +2737,18 @@ settings.enforce_on_admins_desc = Správci repozitáře nemohou obejít toto pra issues.num_participants_one = %d účastník size_format = %[1]s: %[2]s, %[3]s: %[4]s issues.archived_label_description = (Archivován) %s +release.download_count_one = %d stažení +release.download_count_few = %d stažení +release.system_generated = Tato příloha byla automaticky vygenerována. +settings.add_webhook.invalid_path = Cesta nesmí obsahovat část, která je „.“ nebo „..“ nebo prázdný řetězec. Nesmí začínat ani končit lomítkem. +settings.web_hook_name_sourcehut_builds = Sestavení SourceHut +settings.sourcehut_builds.manifest_path = Cesta manifestu sestavení +settings.sourcehut_builds.graphql_url = URL GraphQL (např. https://builds.sr.ht/query) +settings.sourcehut_builds.visibility = Viditelnost práce +settings.sourcehut_builds.secrets = Tajné klíče +settings.sourcehut_builds.secrets_helper = Udělit práci přístup k tajným klíčům sestavení (vyžaduje oprávnění SECRETS:RO) +settings.graphql_url = URL GraphQL +settings.sourcehut_builds.access_token_helper = Přístupový token, který má oprávnění JOBS:RW. Vygenerujte token builds.sr.ht nebo token builds.sr.ht s přístupem k tajným klíčům na meta.sr.ht. [graphs] component_loading_info = Tohle může chvíli trvat… @@ -3012,12 +3037,12 @@ users.list_status_filter.not_active=Neaktivní users.list_status_filter.is_admin=Administrátor users.list_status_filter.not_admin=Není administrátor users.list_status_filter.is_restricted=Omezeno -users.list_status_filter.not_restricted=Není omezeno +users.list_status_filter.not_restricted=Není omezen users.list_status_filter.is_prohibit_login=Zakázat přihlášení users.list_status_filter.not_prohibit_login=Povolit přihlášení users.list_status_filter.is_2fa_enabled=2FA povoleno users.list_status_filter.not_2fa_enabled=2FA zakázáno -users.details=Detaily uživatele +users.details=Podrobnosti o uživateli emails.email_manage_panel=Správa uživatelských e-mailů emails.primary=Hlavní @@ -3801,7 +3826,7 @@ no_results = Nenalezeny žádné odpovídající výsledky. fuzzy_tooltip = Zahrnout také výsledky, které úzce odpovídají hledanému výrazu search = Hledat... keyword_search_unavailable = Hledání pomocí klíčových slov momentálně není dostupné. Kontaktujte prosím administrátora webu. -code_search_by_git_grep = Aktuální výsledky vyhledávání kódu jsou poskytovány službou „git grep“. Lepší výsledky dostanete, když administrátor webu povolí indexování repozitářů. +code_search_by_git_grep = Aktuální výsledky vyhledávání kódu jsou poskytovány službou „git grep“. Lepší výsledky dostanete, když administrátor webu povolí indexování kódu. [markup] filepreview.lines = Řádky %[1]d až %[2]d v souboru %[3]s diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index bfd18cc308..7b872fc8f2 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -18,8 +18,8 @@ template=Vorlage language=Sprache notifications=Benachrichtigungen active_stopwatch=Aktive Zeiterfassung -create_new=Erstellen … -user_profile_and_more=Profil und Einstellungen … +create_new=Erstellen… +user_profile_and_more=Profil und Einstellungen… signed_in_as=Angemeldet als enable_javascript=Diese Website benötigt JavaScript. toc=Inhaltsverzeichnis @@ -211,7 +211,7 @@ platform_desc=Forgejo läuft überall, wo Forgejo! Tritt uns bei, indem du uns hilfst, dieses Projekt noch besser zu machen. Scheue dich nicht davor, bei uns mitzuwirken! +license_desc=Hole dir Forgejo! Tritt uns bei, indem du uns hilfst, dieses Projekt noch besser zu gestalten. Scheue dich nicht davor, bei uns mitzuwirken! [install] install=Installation @@ -321,7 +321,6 @@ password_algorithm=Passwort Hashing Algorithmus invalid_password_algorithm=Ungültiger Passwort-Hash-Algorithmus password_algorithm_helper=Lege einen Passwort-Hashing-Algorithmus fest. Algorithmen haben unterschiedliche Anforderungen und Stärken. Der argon2-Algorithmus ist ziemlich sicher, aber er verbraucht viel Speicher und kann für kleine Systeme ungeeignet sein. enable_update_checker=Aktualisierungsprüfung aktivieren -enable_update_checker_helper=Stellt regelmäßig eine Verbindung zu gitea.io her, um nach neuen Versionen zu prüfen. env_config_keys=Umgebungskonfiguration env_config_keys_prompt=Die folgenden Umgebungsvariablen werden auch auf Ihre Konfigurationsdatei angewendet: allow_dots_in_usernames = Erlaubt Benutzern die Verwendung von Punkten in ihren Benutzernamen. Hat keine Auswirkungen auf bestehende Konten. @@ -454,7 +453,7 @@ password_pwned_err=Anfrage an HaveIBeenPwned konnte nicht abgeschlossen werden change_unconfirmed_email_summary = Ändern der E-Mail-Adresse, an die die Aktivierungsnachricht gesendet wird. change_unconfirmed_email_error = Ändern der E-Mail-Adresse fehlgeschlagen: %v last_admin = Du kannst den letzten Administrator nicht entfernen. Es muss mindestens einen Administrator geben. -change_unconfirmed_email = Wenn Sie bei der Registrierung eine falsche E-Mail-Adresse angegeben haben, können Sie diese unten ändern, woraufhin eine Bestätigung an die neue Adresse geschickt wird. +change_unconfirmed_email = Wenn du bei der Anmeldung eine falsche E-Mail-Adresse angegeben hast, kannst du diese unten ändern und bekommst stattdessen eine Bestätigung an die neue Adresse geschickt. remember_me.compromised = Der Anmeldetoken ist nicht mehr gültig, dies könnte auf ein kompromittiertes Konto hindeuten. Bitte prüfe dein Konto auf ungewöhnliche Aktivitäten. tab_signin = Anmelden tab_signup = Registrieren @@ -629,6 +628,15 @@ username_error_no_dots = ` darf nur alphanumerische Zeichen („0-9“, „a-z admin_cannot_delete_self = Du kannst dich nicht selbst löschen, wenn du ein Admin bist. Bitte entferne zuerst deine Adminrechte. unset_password = Für den Benutzer ist das Passwort nicht gesetzt. unsupported_login_type = Dieser Login-Typ unterstützt keine Accountlöschung. +required_prefix = Eingabe muss mit „%s“ anfangen +Description = Beschreibung +FullName = Vollständiger Name +Pronouns = Pronomen +Biography = Biografie +Website = Webseite +Location = Ort +To = Branchname +AccessToken = Zugangstoken [user] @@ -663,6 +671,8 @@ block = Blockieren follow_blocked_user = Du kannst diesen Benutzer nicht folgen, weil du ihn blockiert hast, oder er dich blockiert hat. block_user.detail_3 = Dieser Benutzer kann dich nicht als einen Mitarbeiter hinzufügen, und du kannst ihn nicht als Mitarbeiter hinzufügen. unblock = Nicht mehr blockieren +followers_one = %d Follower +following_one = einem gefolgt [settings] profile=Profil @@ -729,7 +739,7 @@ keep_activity_private_popup=Deine Aktivität wird nur für dich und die Instanza lookup_avatar_by_mail=Profilbild anhand der E-Mail-Addresse suchen federated_avatar_lookup=Suche nach föderierten Profilbildern -enable_custom_avatar=Benutzerdefiniertes Profilbild benutzen +enable_custom_avatar=Benutzerdefiniertes Profilbild verwenden choose_new_avatar=Neues Profilbild auswählen update_avatar=Profilbild aktualisieren delete_current_avatar=Aktuelles Profilbild löschen @@ -973,7 +983,7 @@ visibility.limited_tooltip=Nur für authentifizierte Benutzer sichtbar visibility.private=Privat visibility.private_tooltip=Sichtbar nur für Mitglieder von Organisationen, denen du beigetreten bist user_block_success = Dieser Benutzer wurde erfolgreich blockiert. -twofa_recovery_tip = Falls du dein Gerät verlierst, wirst du in der Lage sein, einen einmalig verwendbaren Wiederherstellungsschlüssel zu benutzen, um den auf dein Konto wiederherzustellen. +twofa_recovery_tip = Falls du dein Gerät verlierst, wirst du in der Lage sein, einen einmalig verwendbaren Wiederherstellungsschlüssel zu verwenden, um den auf dein Konto wiederherzustellen. webauthn_alternative_tip = Du möchtest vielleicht eine zusätzliche Authentifizierungsmethode einrichten. blocked_users_none = Keine Benutzer blockiert. webauthn_key_loss_warning = Falls du deine Security-Keys verlierst, wirst du Zugang zu deinem Konto verlieren. @@ -986,6 +996,9 @@ additional_repo_units_hint = Zur Aktivierung zusätzlicher Repository-Einheiten update_hints = Hinweise aktualisieren update_hints_success = Hinweise wurden aktualisiert. additional_repo_units_hint_description = Einen „Mehr Einheiten hinzufügen …“-Button für Repositorys, welche nicht alle verfügbaren Einheiten aktiviert haben, anzeigen. +pronouns = Pronomen +pronouns_custom = Eigene +pronouns_unspecified = Nicht spezifiziert [repo] owner=Besitzer @@ -1027,7 +1040,7 @@ issue_labels=Issue-Labels issue_labels_helper=Wähle eine Issue-Label-Sammlung. license=Lizenz license_helper=Wähle eine Lizenz aus. -license_helper_desc=Eine Lizenz regelt, was andere mit deinem Code tun (oder nicht tun) können. Unsicher, welches für dein Projekt die Richtige ist? Siehe den Lizenzwahlhelfer. +license_helper_desc=Eine Lizenz regelt, was andere mit deinem Code tun (oder nicht tun) können. Unsicher, welches für dein Projekt die Richtige ist? Siehe Choose a license.. readme=README readme_helper=Wähle eine README-Vorlage aus. readme_helper_desc=Hier kannst du eine komplette Beschreibung für dein Projekt schreiben. @@ -1230,9 +1243,9 @@ file_view_raw=Originalformat anzeigen file_permalink=Permalink file_too_large=Die Datei ist zu groß zum Anzeigen. invisible_runes_header=`Diese Datei enthält unsichtbare Unicode-Zeichen` -invisible_runes_description=`Diese Datei enthält unsichtbare Unicode-Zeichen, die für Menschen nicht unterscheidbar sind, aber von einem Computer unterschiedlich verarbeitet werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.` +invisible_runes_description=`Diese Datei enthält unsichtbare Unicode-Zeichen, die für Menschen nicht unterscheidbar sind, jedoch von einem Computer unterschiedlich verarbeitet werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escapen“-Button, um versteckte Zeichen anzuzeigen.` ambiguous_runes_header=`Diese Datei enthält mehrdeutige Unicode-Zeichen` -ambiguous_runes_description=`Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escape“-Button, um versteckte Zeichen anzuzeigen.` +ambiguous_runes_description=`Diese Datei enthält Unicode-Zeichen, die mit anderen Zeichen verwechselt werden können. Wenn du glaubst, dass das absichtlich so ist, kannst du diese Warnung ignorieren. Benutze den „Escapen“-Button, um versteckte Zeichen anzuzeigen.` invisible_runes_line=`Diese Zeile enthält unsichtbare Unicode-Zeichen` ambiguous_runes_line=`Diese Zeile enthält mehrdeutige Unicode-Zeichen` ambiguous_character=`%[1]c [U+%04[1]X] kann mit %[2]c [U+%04[2]X] verwechselt werden` @@ -1675,7 +1688,7 @@ issues.dependency.issue_no_dependencies=Keine Abhängigkeiten gesetzt. issues.dependency.pr_no_dependencies=Keine Abhängigkeiten gesetzt. issues.dependency.no_permission_1=Du bist nicht berechtigt, %d Abhängigkeit zu lesen issues.dependency.no_permission_n=Du bist nicht berechtigt, %d Abhängigkeiten zu lesen -issues.dependency.no_permission.can_remove=Du hast keine Berechtigung, diese Abhängigkeit zu lesen, kannst diese Abhängigkeit aber entfernen +issues.dependency.no_permission.can_remove=Du hast keine Berechtigung, diese Abhängigkeit zu lesen, kannst diese Abhängigkeit jedoch entfernen issues.dependency.add=Abhängigkeit hinzufügen … issues.dependency.cancel=Abbrechen issues.dependency.remove=Entfernen @@ -1794,7 +1807,7 @@ pulls.remove_prefix=Präfix „%s“ entfernen pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden. pulls.files_conflicted=Dieser Pull-Request hat Änderungen, die im Widerspruch zum Ziel-Branch stehen. pulls.is_checking=Die Merge-Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. -pulls.is_ancestor=Dieser Branch ist bereits im Zielbranch enthalten. Es gibt nichts zusammenzuführen. +pulls.is_ancestor=Dieser Branch ist bereits im Zielbranch enthalten. Es existiert nichts zusammenzuführen. pulls.is_empty=Die Änderungen an diesem Branch sind bereits auf dem Zielbranch. Dies wird ein leerer Commit sein. pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht erfolgreich. pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. @@ -1806,7 +1819,7 @@ pulls.blocked_by_outdated_branch=Dieser Pull-Request ist blockiert, da er veralt pulls.blocked_by_changed_protected_files_1=Dieser Pull-Request ist blockiert, weil er eine geschützte Datei ändert: pulls.blocked_by_changed_protected_files_n=Dieser Pull-Request ist blockiert, weil er geschützte Dateien ändert: pulls.can_auto_merge_desc=Dieser Pull-Request kann automatisch zusammengeführt werden. -pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch zusammengeführt werden, da es Konflikte gibt. +pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch zusammengeführt werden, da Konflikte existieren. pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte zu beheben. pulls.num_conflicting_files_1=%d Datei mit Konflikten pulls.num_conflicting_files_n=%d Dateien mit Konflikten @@ -1832,9 +1845,9 @@ pulls.merge_commit_id=Die Merge-Commit-ID pulls.require_signed_wont_sign=Der Branch erfordert einen signierten Commit, aber dieser Merge wird nicht signiert pulls.invalid_merge_option=Du kannst diese Merge-Option auf diesen Pull-Request nicht anwenden. -pulls.merge_conflict=Merge fehlgeschlagen: Beim Zusammenführen gab es einen Konflikt. Tipp: Probiere eine andere Strategie +pulls.merge_conflict=Merge fehlgeschlagen: Beim Zusammenführen existierte ein Konflikt. Tipp: Probiere eine andere Strategie pulls.merge_conflict_summary=Fehlermeldung -pulls.rebase_conflict=Merge fehlgeschlagen: Es gab einen Konflikt beim Rebasen des Commits: %[1]s. Tipp: Versuche eine andere Strategie +pulls.rebase_conflict=Merge fehlgeschlagen: Es existierte ein Konflikt beim Rebasen des Commits: %[1]s. Tipp: Versuche eine andere Strategie pulls.rebase_conflict_summary=Fehlermeldung pulls.unrelated_histories=Merge fehlgeschlagen: Der Head des Merges und die Basis haben keinen gemeinsamen Verlauf. Tipp: Versuche eine andere Strategie pulls.merge_out_of_date=Merge fehlgeschlagen: Während des Zusammenführens wurde die Basis aktualisiert. Tipp: Versuche es erneut. @@ -1842,7 +1855,7 @@ pulls.head_out_of_date=Mergen fehlgeschlagen: Der Head wurde aktualisiert, währ pulls.has_merged=Fehler: Der Pull-Request wurde zusammengeführt, du kannst den Zielbranch nicht wieder zusammenführen oder ändern. pulls.push_rejected=Pushen fehlgeschlagen: Der Push wurde abgelehnt. Überprüfe die Git-Hooks für dieses Repository. pulls.push_rejected_summary=Vollständige Ablehnungsmeldung -pulls.push_rejected_no_message=Pushen fehlgeschlagen: Der Push wurde abgelehnt, aber es gab keine Fehlermeldung. Überprüfe die Git-Hooks für dieses Repository +pulls.push_rejected_no_message=Pushen fehlgeschlagen: Der Push wurde abgelehnt, doch es existierte keine Fehlermeldung. Überprüfe die Git-Hooks für dieses Repository pulls.open_unmerged_pull_exists=`Du kannst diesen Pull-Request nicht erneut öffnen, da noch ein anderer (#%d) mit identischen Eigenschaften offen ist.` pulls.status_checking=Einige Prüfungen sind noch ausstehend pulls.status_checks_success=Alle Prüfungen waren erfolgreich @@ -1913,7 +1926,7 @@ milestones.filter_sort.least_issues=Wenigste Issues signing.will_sign=Dieser Commit wird mit dem Key „%s“ signiert werden. signing.wont_sign.error=Es gab einen Fehler bei der Prüfung, ob der Commit signiert werden kann. -signing.wont_sign.nokey=Es ist kein Schlüssel zum Signieren dieses Commits verfügbar. +signing.wont_sign.nokey=Diese Instanz hat keinen Schlüssel, um diesen Commit zu signieren. signing.wont_sign.never=Commits werden nie signiert. signing.wont_sign.always=Commits werden immer signiert. signing.wont_sign.pubkey=Der Commit wird nicht signiert, da du keinen öffentlichen Schlüssel mit deinem Account verknüpft hast. @@ -2344,7 +2357,7 @@ settings.protect_this_branch_desc=Verhindert das Löschen und schränkt Git auf settings.protect_disable_push=Push deaktivieren settings.protect_disable_push_desc=Kein Push auf diesen Branch erlauben. settings.protect_enable_push=Push aktivieren -settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch pushen (aber kein Force-Push). +settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch pushen (jedoch kein Force-Push). settings.protect_enable_merge=Merge aktivieren settings.protect_enable_merge_desc=Jeder mit Schreibzugriff darf die Pull-Requests in diesen Branch zusammenführen. settings.protect_whitelist_committers=Whitelist-eingeschränkter Push @@ -2369,7 +2382,7 @@ settings.protect_no_valid_status_check_patterns=Keine gültigen Statuscheck-Must settings.protect_required_approvals=Erforderliche Genehmigungen: settings.protect_required_approvals_desc=Erlaube das Zusammenführen des Pull-Requests nur mit genügend positiven Reviews. settings.protect_approvals_whitelist_enabled=Genehmigungen auf Benutzer oder Teams auf der Whitelist beschränken -settings.protect_approvals_whitelist_enabled_desc=Nur Reviews von Benutzern auf der Whitelist oder Teams zählen zu den erforderlichen Genehmigungen. Gibt es keine Whitelist, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. +settings.protect_approvals_whitelist_enabled_desc=Nur Reviews von Benutzern auf der Whitelist oder Teams zählen zu den erforderlichen Genehmigungen. Existiert keine Whitelist, so zählen Reviews von jedem mit Schreibzugriff zu den erforderlichen Genehmigungen. settings.protect_approvals_whitelist_users=Reviewer auf der Whitelist: settings.protect_approvals_whitelist_teams=Für Reviews gewhitelistete Teams: settings.dismiss_stale_approvals=Entferne alte Genehmigungen @@ -2390,9 +2403,9 @@ settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ settings.protected_branch_deletion=Branch-Schutz löschen settings.protected_branch_deletion_desc=Wenn du den Branch-Schutz deaktivierst, können alle Nutzer mit Schreibrechten auf den Branch pushen. Fortfahren? settings.block_rejected_reviews=Zusammenführung bei abgelehnten Reviews blockieren -settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn es genügend Genehmigungen gibt. +settings.block_rejected_reviews_desc=Merge ist nicht möglich, wenn Änderungen durch offizielle Reviewer angefragt werden, auch wenn genügend Genehmigungen existieren. settings.block_on_official_review_requests=Merge bei offiziellen Review-Anfragen blockieren -settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Review-Anfrangen vorliegen, selbst wenn es genügend Genehmigungen gibt. +settings.block_on_official_review_requests_desc=Merge ist nicht möglich, wenn offizielle Review-Anfrangen vorliegen, selbst wenn genügend Genehmigungen existieren. settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet ist settings.block_outdated_branch_desc=Merge ist nicht möglich, wenn der Head-Branch hinter dem Basis-Branch ist. settings.default_branch_desc=Wähle einen Standardbranch für Pull-Requests und Code-Commits: @@ -2428,8 +2441,8 @@ settings.archive.error=Beim Versuch, das Repository zu archivieren, ist ein Fehl settings.archive.error_ismirror=Du kannst kein gespiegeltes Repo archivieren. settings.archive.branchsettings_unavailable=Branch-Einstellungen sind nicht verfügbar wenn das Repo archiviert ist. settings.archive.tagsettings_unavailable=Tag Einstellungen sind nicht verfügbar, wenn das Repo archiviert wurde. -settings.unarchive.button=Archivieren rückgängig machen -settings.unarchive.header=Archivieren dieses Repositorys rückgängig machen +settings.unarchive.button=Archivierung zurücksetzen +settings.unarchive.header=Archivierung dieses Repositorys zurücksetzen settings.unarchive.text=Durch das Aufheben der Archivierung kann das Repo wieder Commits und Pushes sowie neue Issues und Pull-Requests empfangen. settings.unarchive.success=Die Archivierung des Repos wurde erfolgreich wieder rückgängig gemacht. settings.unarchive.error=Beim Versuch, die Archivierung des Repos aufzuheben, ist ein Fehler aufgetreten. Weitere Details finden sich im Log. @@ -2698,7 +2711,7 @@ open_with_editor = Öffnen mit %s commits.search_branch = Dieser Branch pulls.ready_for_review = Bereit zum Review? settings.rename_branch_failed_protected = Branch %s kann nicht umbenannt werden, weil er ein geschützter Branch ist. -editor.commit_id_not_matching = Die Commit-ID passt nicht zur ID die du bearbeitet hast hast. Committe in einen neuen Branch, dann mach einen Merge. +editor.commit_id_not_matching = Die Commit-ID passt nicht zur ID die du bearbeitet hast hast. Committe in einen neuen Branch, dann führe einen Merge durch. editor.push_out_of_date = Der Push scheint veraltet zu sein. n_commit_few = %s Commits n_branch_one = %s Branch @@ -2713,6 +2726,20 @@ settings.enforce_on_admins = Erzwinge diese Regel für alle Repositoryadministra settings.event_pull_request_enforcement = Erzwingung size_format = %[1]s: %[2]s, %[3]s: %[4]s issues.archived_label_description = (Archiviert) %s +release.download_count_one = %s Download +release.download_count_few = %s Downloads +release.system_generated = Dieser Anhang wurde automatisch generiert. +settings.add_webhook.invalid_path = Der Pfad darf kein „.“ oder „..“ enhalten, und darf auch nicht leer sein. Er darf auch nicht mit einem Slash anfangen oder enden. +settings.sourcehut_builds.graphql_url = GraphQL-URL (z.B. https://builds.sr.ht/query) +settings.sourcehut_builds.manifest_path = Build-Manifest-Pfad +settings.sourcehut_builds.visibility = Job-Sichtbarkeit +settings.sourcehut_builds.secrets = Geheimnisse +settings.sourcehut_builds.secrets_helper = Dem Job zugriff auf die Build-Geheimnisse geben (benötigt die SECRETS:RO-Berechtigung) +settings.web_hook_name_sourcehut_builds = SourceHut-Builds +settings.graphql_url = GraphQL-URL +settings.matrix.room_id_helper = Die Raum-ID kann über den Element-Webclient ermittelt werden: Raumeinstellungen > Erweitert > Interne Raum-ID. Beispielsweise %s. +settings.sourcehut_builds.access_token_helper = Zugangstoken der die JOBS:RW-Freigabe hat. Generiere auf meta.sr.ht einen builds.sr.ht-Token oder einen builds.sr.ht-Token mit Zugriff auf die Secrets. +settings.matrix.access_token_helper = Es wird empfohlen, einen dedizierten Matrix-Account hierfür anzulegen. Der Zugangstoken kann in einem Incognito-Tab über den Element-Webclient geholt werden: Benutzermenü (oben links) > Alle Einstellungen > Hilfe & Über > Erweitert > Zugangstoken (direkt unter der Homeserver-URL). Schließe das Incognito-Tab dann (Abmelden würde den Token ungültig werden lassen). [graphs] @@ -3366,7 +3393,7 @@ notices.type_2=Aufgabe notices.desc=Beschreibung notices.op=Aktion notices.delete_success=Diese Systemmeldung wurde gelöscht. -self_check.database_fix_mysql = Für MySQL-/MariaDB-Benutzer: Du kannst den Befehl „gitea doctor convert“ benutzen, um die Collation-Probleme zu lösen, oder du kannst das Problem mit „ALTER … COLLATE …“-SQLs manuell lösen. +self_check.database_fix_mysql = Für MySQL-/MariaDB-Benutzer: Du kannst den Befehl „gitea doctor convert“ verwenden, um die Collation-Probleme zu lösen, oder du kannst das Problem mit „ALTER … COLLATE …“-SQLs manuell lösen. dashboard.sync_tag.started = Tags-Synchronisierung gestartet self_check.database_collation_case_insensitive = Datenbank benutzt eine Collation %s, welcher der Groß-/Kleinschreibung egal ist. Obwohl Forgejo damit arbeiten könnte, könnte es ein paar seltene Fälle geben, bei denen es nicht wie erwartet funktioniert. self_check = Selbstprüfung @@ -3374,7 +3401,7 @@ dashboard.sync_repo_tags = Tags aus Git-Daten zu Datenbank synchronisieren emails.change_email_text = Bist du dir sicher, dass du diese E-Mail-Addresse aktualisieren möchtest? packages.cleanup.success = Abgelaufene Daten erfolgreich gesäubert self_check.no_problem_found = Noch kein Problem gefunden. -self_check.database_inconsistent_collation_columns = Datenbank benutzt Collation %s, aber diese Spalten benutzen Collations, die nicht zusammenpassen. Das könnte ein paar unerwartete Probleme verursachen. +self_check.database_inconsistent_collation_columns = Datenbank benutzt Collation %s, doch diese Spalten verwenden Collations, die nicht zusammenpassen. Das könnte ein paar unerwartete Probleme verursachen. self_check.database_collation_mismatch = Erwarte von Datenbank, folgende Collation zu verwenden: %s auths.tips.gmail_settings = Gmail-Einstellungen: config_settings = Einstellungen @@ -3391,8 +3418,8 @@ create_issue=`hat das Issue %[3]s#%[2]s geöffnet` close_issue=`hat das Issue %[3]s#%[2]s geschlossen` reopen_issue=`hat das Issue %[3]s#%[2]s wiedereröffnet` create_pull_request=`hat den Pull-Request %[3]s#%[2]s erstellt` -close_pull_request=`Pull-Request %[3]s#%[2]s wurde geschlossen` -reopen_pull_request=`Pull-Request %[3]s#%[2]s wurde wiedereröffnet` +close_pull_request=`hat Pull-Request %[3]s#%[2]s geschlossen` +reopen_pull_request=`hat Pull-Request %[3]s#%[2]s wiedereröffnet` comment_issue=`hat das Issue %[3]s#%[2]s kommentiert` comment_pull=`hat den Pull-Request %[3]s#%[2]s kommentiert` merge_pull_request=`führte Pull-Request %[3]s#%[2]s zusammen` @@ -3464,7 +3491,7 @@ error.no_committer_account=Es ist kein Account mit der E-Mail-Adresse des Commit error.no_gpg_keys_found=Es konnte kein GPG-Schlüssel zu dieser Signatur gefunden werden error.not_signed_commit=Kein signierter Commit error.failed_retrieval_gpg_keys=Fehler beim Abrufen eines Keys des Commiter-Kontos -error.probable_bad_signature=WARNHINWEIS! Obwohl es einen Schlüssel mit dieser ID in der Datenbank gibt, verifiziert er nicht diesen Commit! Dieser Commit ist VERDÄCHTIG. +error.probable_bad_signature=WARNHINWEIS! Obwohl ein Schlüssel mit dieser ID in der Datenbank existiert, verifiziert er nicht diesen Commit! Dieser Commit ist VERDÄCHTIG. error.probable_bad_default_signature=WARNHINWEIS! Obwohl der Standardschlüssel diese ID hat, verifiziert er nicht diesen Commit! Dieser Commit ist VERDÄCHTIG. [units] @@ -3728,8 +3755,8 @@ variables.creation.success=Die Variable „%s“ wurde hinzugefügt. variables.update.failed=Fehler beim Bearbeiten der Variable. variables.update.success=Die Variable wurde bearbeitet. runs.no_workflows.quick_start = Weißt du nicht, wie du mit Forgejo Actions anfangen sollst? Sieh dir die Schnellstartanleitung an. -runs.no_matching_online_runner_helper = Es gibt keinen passenden Online-Runner mit dem Label: %s -runs.no_workflows = Es gibt noch keine Workflows. +runs.no_matching_online_runner_helper = Es existiert kein passender Online-Runner mit dem Label: %s +runs.no_workflows = Es existieren noch keine Workflows. runs.no_workflows.documentation = Für weitere Informationen über Forgejo Actions, siehe die Dokumentation. runs.empty_commit_message = (leere Commit-Nachricht) variables.id_not_exist = Variable mit ID %d existiert nicht. diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 5116f6b2e4..0be1aab87f 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -33,7 +33,7 @@ password=Κωδικός πρόσβασης access_token=Διακριτικό πρόσβασης re_type=Επιβεβαίωση κωδικού captcha=CAPTCHA -twofa=Πιστοποίηση Δύο Παραγόντων +twofa=Πιστοποίηση δύο παραγόντων twofa_scratch=Κωδικός μίας χρήσης (για πιστοποίηση δύο παραγόντων / 2FA) passcode=Κωδικός @@ -44,7 +44,7 @@ webauthn_use_twofa=Χρησιμοποιήστε έναν κωδικό δύο π webauthn_error=Δεν ήταν δυνατή η επικοινωνία με το κλειδί ασφαλείας σας. webauthn_unsupported_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει επί του παρόντος WebAuthn. webauthn_error_unknown=Παρουσιάστηκε ένα άγνωστο σφάλμα. Παρακαλώ προσπαθήστε ξανά. -webauthn_error_insecure=Το WebAuthn υποστηρίζει μόνο ασφαλές συνδέσεις. Για την διεξαγωγή δοκιμών μέσω HTTP, μπορείτε να χρησιμοποιήσετε την προέλευση «localhost» ή «127.0.0.1» +webauthn_error_insecure=Το WebAuthn υποστηρίζει μόνο ασφαλές συνδέσεις. Αν θέλετε να διεξάγετε δοκιμές μέσω HTTP, μπορείτε να χρησιμοποιήσετε την προέλευση «localhost» ή «127.0.0.1» webauthn_error_unable_to_process=Ο διακομιστής δεν μπόρεσε να επεξεργαστεί το αίτημά σας. webauthn_error_duplicated=Το κλειδί ασφαλείας δεν επιτρέπεται για αυτό το αίτημα. Βεβαιωθείτε ότι το κλειδί δεν έχει ήδη καταχωρηθεί. webauthn_error_empty=Πρέπει να ορίσετε ένα όνομα για αυτό το κλειδί. @@ -54,16 +54,16 @@ webauthn_reload=Ανανέωση repository=Αποθετήριο organization=Οργανισμός mirror=Αντίγραφο -new_repo=Νέο Αποθετήριο -new_migrate=Νέα Μεταφορά -new_mirror=Νέο Είδωλο -new_fork=Νέο Fork Αποθετηρίου -new_org=Νέος Οργανισμός -new_project=Νέο Έργο -new_project_column=Νέα Στήλη +new_repo=Νέο αποθετήριο +new_migrate=Νέα μεταφορά +new_mirror=Νέο είδωλο +new_fork=Νέο fork αποθετηρίου +new_org=Νέος οργανισμός +new_project=Νέο έργο +new_project_column=Νέα στήλη manage_org=Διαχείριση Οργανισμών admin_panel=Διαχείριση -account_settings=Ρυθμίσεις Λογαριασμού +account_settings=Ρυθμίσεις λογαριασμού settings=Ρυθμίσεις your_profile=Προφίλ your_starred=Αγαπημένα @@ -76,7 +76,7 @@ collaborative=Συνεργατικά forks=Forks activities=Δραστηριότητες -pull_requests=Pull Requests +pull_requests=Pull requests issues=Ζητήματα milestones=Ορόσημα @@ -146,14 +146,17 @@ confirm_delete_artifact = Είστε βέβαιοι πως θέλετε να δ filter = Φίλτρο filter.is_archived = Αρχειοθετημένο filter.clear = Απενεργοποίηση φίλτρου -filter.not_archived = Μη αρχειοθετημένο +filter.not_archived = Μη αρχειοθετημένα filter.is_template = Πρότυπο filter.public = Δημόσιο filter.private = Ιδιωτικό -filter.not_fork = Εξαίρεση Fork +filter.not_fork = Εξαίρεση fork filter.is_mirror = Είδωλα -filter.not_mirror = Εξαίρεση Ειδώλων -filter.not_template = Εξαίρεση Προτύπων +filter.not_mirror = Εξαίρεση ειδώλων +filter.not_template = Εξαίρεση προτύπων +filter.is_fork = Forked +more_items = Περισσότερα αντικείμενα +invalid_data = Τα δεδομένα δεν είναι έγκυρα: %v [aria] navbar=Γραμμή Πλοήγησης @@ -166,6 +169,9 @@ number_of_contributions_in_the_last_12_months=%s συνεισφορές τους contributions_zero=Χωρίς συνεισφορές less=Λιγότερα more=Περισσότερα +contributions_format = {contributions} στις {day} του μήνα {month} του έτους {year} +contributions_one = συνεισφορά +contributions_few = συνεισφορές [editor] buttons.heading.tooltip=Προσθήκη επικεφαλίδας @@ -189,19 +195,19 @@ string.desc=Z - A [error] occurred=Παρουσιάστηκε ένα σφάλμα -report_message=Αν πιστεύετε ότι αυτό προέκυψε λόγω κάποιου σφάλματος στο Forgejo, σας παρακαλούμε να αναζητήσετε τα ζητήματα στο Codeberg ή να ανοίξετε ένα νέο ζήτημα εάν είναι απαραίτητο. +report_message=Αν πιστεύετε ότι αυτό προέκυψε λόγω κάποιου σφάλματος στο Forgejo, σας παρακαλούμε να ρίξετε μία ματιά στα ζητήματα στο Codeberg ή να ανοίξετε ένα νέο ζήτημα εάν είναι απαραίτητο. missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF not_found=Ο προορισμός δεν βρέθηκε. network_error=Σφάλμα δικτύου -server_internal = Σφάλμα Διακομιστή +server_internal = Σφάλμα διακομιστή [startpage] app_desc=Μια ανώδυνη, αυτο-φιλοξενούμενη υπηρεσία Git install=Εύκολη εγκατάσταση install_desc=Απλά τρέξε το αρχείο που αντιστοιχεί στην πλατφόρμα σου, εγκατέστησε το με το Docker ή χρησιμοποίησε ένα πακέτο λογισμικού. platform=Τρέχει παντού -platform_desc=Το Forgejo τρέχει σε όλα τα συστήματα που υποστηρίζει η γλώσσα Go, όπως: Windows, macOS, Linux, ARM, κλπ. Διάλεξε αυτό που αγαπάς! +platform_desc=Το Forgejo τρέχει σε κάθε σύστημα που υποστηρίζει η γλώσσα Go, όπως: Windows, macOS, Linux, ARM, κλπ. Διάλεξε αυτό που αγαπάς! lightweight=Ελαφρύ lightweight_desc=Το Forgejo έχει ελάχιστες απαιτήσεις, μπορείς και να το τρέξεις σε ένα φτηνό Raspberry Pi. Εξοικονόμησε ενέργεια! license=Ανοικτού κώδικα @@ -237,7 +243,7 @@ err_admin_name_pattern_not_allowed=Το Όνομα χρήστη του Διαχ err_admin_name_is_invalid=Το Όνομα Χρήστη του Διαχειριστή δεν είναι έγκυρο general_title=Γενικές ρυθμίσεις -app_name=Όνομα διακομιστή +app_name=Τίτλος διακομιστή app_name_helper=Μπορείτε να εισάγετε το όνομα της εταιρείας σας εδώ. repo_path=Τοποθεσία αρχείων αποθετηρίων repo_path_helper=Τα απομακρυσμένα αποθετήρια Git θα αποθηκεύονται σε αυτόν τον κατάλογο. @@ -270,9 +276,9 @@ server_service_title=Ρυθμίσεις διακομιστή και υπηρεσ offline_mode=Ενεργοποίηση τοπικής λειτουργίας offline_mode_popup=Απενεργοποιήση των δικτύων διανομής περιεχομένου τρίτων και σερβίρετε όλων των πόρων τοπικά. disable_gravatar=Απενεργοποίηση Gravatar -disable_gravatar_popup=Απενεργοποιήση του Gravatar και των εξωτερικών πηγών avatar. Θα χρησιμοποιηθεί ένα προεπιλεγμένο avatar εκτός αν ένας χρήστης ανεβάσει τοπικά ένα avatar. -federated_avatar_lookup=Ενεργοποίηση αποκεντρωμένων avatars -federated_avatar_lookup_popup=Ενεργοποίηση ομόσπονδης αναζήτησης avatar χρησιμοποιώντας το Libravatar. +disable_gravatar_popup=Το Gravatar και άλλες εξωτερικές πηγές εικόνων προφίλ θα απενεργοποιηθούν. Θα χρησιμοποιηθεί μία προεπιλεγμένη εικόνα προφίλ, εκτός αν ο χρήστης ανεβάσει από μόνος του ένα avatar. +federated_avatar_lookup=Ενεργοποίηση αποκεντρωμένων εικόνων προφίλ +federated_avatar_lookup_popup=Ενεργοποίηση αποκεντρωμένης αναζήτησης εικόνων προφίλ μέσω Libravatar. disable_registration=Απενεργοποίηση αυτοεγγραφής disable_registration_popup=Απενεργοποίηση αυτοεγγραφής χρήστη. Μόνο οι διαχειριστές θα μπορούν να δημιουργήσουν νέους λογαριασμούς χρηστών. allow_only_external_registration_popup=Να επιτρέπεται η εγγραφή μόνο μέσω εξωτερικών υπηρεσιών @@ -297,7 +303,7 @@ invalid_db_setting=Οι ρυθμίσεις της βάσης δεδομένων invalid_db_table=Ο πίνακας βάσης δεδομένων «%s» δεν είναι έγκυρος: %v invalid_repo_path=Η αρχική διαδρομή των αποθετηρίων δεν είναι έγκυρη: %v invalid_app_data_path=Η διαδρομή δεδομένων εφαρμογής (app data) δεν είναι έγκυρη: %v -run_user_not_match=Το όνομα χρήστη «Εκτέλεση ως» δεν είναι το τρέχον όνομα χρήστη: %s -> %s +run_user_not_match=Το όνομα του χρήστη, του οποίου θα θέλατε να εκτελεί το Forgejo, δεν συνάδει με το τρέχον όνομα χρήστη: %s -> %s internal_token_failed=Αποτυχία δημιουργίας εσωτερικού διακριτικού: %v secret_key_failed=Αποτυχία δημιουργίας μυστικού κλειδιού: %v save_config_failed=Αποτυχία αποθήκευσης ρυθμίσεων: %v @@ -315,7 +321,6 @@ password_algorithm=Αλγόριθμος hash για κωδικούς invalid_password_algorithm=Μη έγκυρος αλγόριθμος κωδικού πρόσβασης password_algorithm_helper=Ορίστε τον αλγόριθμο κατακερματισμού για το κωδικό πρόσβασης. Οι αλγόριθμοι διαφέρουν σε απαιτήσεις και αντοχή. Ο αλγόριθμος argon2 είναι αρκετά ασφαλής, αλλά χρησιμοποιεί πολλή μνήμη και μπορεί να είναι ακατάλληλος για μικρά συστήματα. enable_update_checker=Ενεργοποίηση ελέγχου ενημερώσεων -enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο gitea.io. env_config_keys=Ρυθμίσεις Περιβάλλοντος env_config_keys_prompt=Οι ακόλουθες μεταβλητές περιβάλλοντος θα εφαρμοστούν επίσης στο αρχείο ρυθμίσεων σας: allow_dots_in_usernames = Επιτρέπει την χρήση τελείων σε ονόματα χρηστών. Δεν θα επηρεαστούν λογαριασμοί που ήδη υπάρχουν. @@ -330,7 +335,7 @@ switch_dashboard_context=Εναλλαγή Περιεχομένων Αρχικο my_repos=Αποθετήρια show_more_repos=Περισσότερα αποθετήρια… collaborative_repos=Συνεργατικά Αποθετήρια -my_orgs=Οι Οργανισμοί Μου +my_orgs=Οργανισμοί my_mirrors=Τα Αντίγραφα Μου view_home=Προβολή %s search_repos=Βρείτε ένα αποθετήριο… @@ -371,6 +376,10 @@ code_search_results=`Αποτελέσματα για «%s»` code_last_indexed_at=Τελευταία δημιουργία ευρετηρίου: %s relevant_repositories_tooltip=Τα αποθετήρια που είναι forks ή που δεν έχουν θέμα, εικονίδιο και περιγραφή είναι κρυμμένα. relevant_repositories=Εμφανίζονται μόνο αποθετήρια που θεωρούμε «σχετικά» για εσάς. Εμφάνιση αποτελεσμάτων χωρίς φίλτρο. +stars_one = %d αστέρι +stars_few = %d αστέρια +forks_one = %d fork +forks_few = %d forks [auth] create_new_account=Δημιουργία Λογαριασμού @@ -391,7 +400,7 @@ allow_password_change=Απαιτείται από το χρήστη να αλλ reset_password_mail_sent_prompt=Ένα email επιβεβαίωσης έχει σταλεί στο %s. Για την ολοκλήρωση της διαδικασίας ανάκτησης του λογαριασμού σας, παρακαλώ ελέγξτε τα εισερχόμενα σας στις επόμενες %s. active_your_account=Ενεργοποιήστε Το Λογαριασμό Σας account_activated=Ο λογαριασμός έχει ενεργοποιηθεί -prohibit_login=Απαγορεύεται η Σύνδεση +prohibit_login=Δεν επιτρέπεται η σύνδεση prohibit_login_desc=Δεν επιτρέπεται η σύνδεση στον λογαριασμό σας, παρακαλούμε επικοινωνήστε με το διαχειριστή σας. resent_limit_prompt=Έχετε ήδη ζητήσει ένα email ενεργοποίησης πρόσφατα. Παρακαλώ περιμένετε 3 λεπτά και προσπαθήστε ξανά. has_unconfirmed_mail=Γειά %s, η διεύθυνση ηλεκτρονικού ταχυδρομείου σας (%s) δεν έχει επιβεβαιωθεί ακόμα. Εάν δεν έχετε λάβει κάποιο email επιβεβαίωσης ή αν χρειάζεστε ένα νέο email επιβεβαίωσης, παρακαλώ πατήστε το παρακάτω κουμπί. @@ -446,6 +455,8 @@ change_unconfirmed_email_error = Δεν ήταν δυνατή η αλλαγή τ last_admin = Δεν μπορείτε να αφαιρέσετε τον μοναδικό διαχειριστή. Πρέπει να υπάρχει τουλάχιστον ένας διαχειριστής. change_unconfirmed_email = Αν έχετε εισάγει μία λανθασμένη διεύθυνση email κατά την εγγραφή σας, μπορείτε να την αλλάξετε παρακάτω, έτσι ώστε να σας σταλεί εκ νέου ένα νέο email επιβεβαίωσης στην νέα σας διεύθυνση. change_unconfirmed_email_summary = Αλλαγή της διεύθυνσης email στην οποία θα σταλεί το email επιβεβαίωσης. +tab_signin = Είσοδος +tab_signup = Εγγραφή [mail] view_it_on=Δείτε το στο %s @@ -514,7 +525,7 @@ team_invite.text_2=Παρακαλώ κάντε κλικ στον παρακάτ team_invite.text_3=Σημείωση: Αυτή η πρόσκληση προοριζόταν για %[1]s. Αν δεν περιμένατε αυτή την πρόσκληση, μπορείτε να αγνοήσετε αυτό το email. admin.new_user.text = Παρακαλώ πατήστε εδώ για να διαχειριστείτε τον χρήστη μέσω του πίνακα διαχειριστών. admin.new_user.subject = Εγγραφή νέου χρήστη %s -admin.new_user.user_info = Πληροφορίες Χρήστη +admin.new_user.user_info = Πληροφορίες χρήστη [modal] yes=Ναι @@ -578,7 +589,7 @@ visit_rate_limit=Συναντήθηκε το όριο ρυθμού κατά τη 2fa_auth_required=Απαιτήθηκε ταυτοποίηση δύο παραγόντων κατά την απομακρυσμένη πρόσβαση. org_name_been_taken=Το όνομα του οργανισμού χρησιμοποιείται ήδη. team_name_been_taken=Το όνομα της ομάδας χρησιμοποιείται ήδη. -team_no_units_error=Να επιτρέπεται η πρόσβαση σε τουλάχιστον μία ενότητα αποθετηρίου. +team_no_units_error=Να επιτρέπεται η πρόσβαση σε τουλάχιστον μία μονάδα αποθετηρίου. email_been_used=Η διεύθυνση email χρησιμοποιείται ήδη. email_invalid=Η διεύθυνση email δεν είναι έγκυρη. openid_been_used=Η διεύθυνση OpenID «%s» χρησιμοποιείται ήδη. @@ -615,16 +626,19 @@ org_still_own_packages=Αυτός ο οργανισμός κατέχει ακό target_branch_not_exist=Ο κλάδος προορισμού δεν υπάρχει. username_error_no_dots = `: Μπορεί να περιέχει μόνο αλφαριθμητικούς χαρακτήρες ('0-9','a-z','A-Z'), παύλα ('-') και κάτω παύλα ('_'). Δεν μπορεί να αρχίζει ή να τελειώνει με μη αλφαριθμητικούς χαρακτήρες. Επίσης δεν επιτρέπεται η χρήση διαδοχικών μη αλφαριθμητικών χαρακτήρων.` admin_cannot_delete_self = Δεν μπορείτε να διαγράψετε τον εαυτό σας όσο είστε διαχειριστής. Πρέπει πρώτα να αφαιρέσετε τα δικαιώματα διαχειριστή σας. +unset_password = Ο χρήστης δεν έχει ορίσει κάποιον κωδικό. +unsupported_login_type = Η μέθοδος με την οποίο γίνεται η σύνδεση δεν υποστηρίζει την διαγραφή λογαριασμών. +required_prefix = Το εισαγώμενο κείμενο πρέπει να ξεκινά με «%s» [user] -change_avatar=Αλλαγή του avatar σας… +change_avatar=Αλλαγή εικόνας προφίλ… joined_on=Εγγράφηκε στις %s repositories=Αποθετήρια -activity=Δημόσια Δραστηριότητα +activity=Δημόσια δραστηριότητα followers_few=%d ακόλουθοι -starred=Αγαπημένα Αποθετήρια -watched=Ακολουθούμενα Αποθετήρια +starred=Αγαπημένα αποθετήρια +watched=Ακολουθούμενα αποθετήρια code=Κώδικας projects=Έργα overview=Επισκόπηση @@ -649,6 +663,8 @@ block_user.detail_1 = Ο χρήστης δεν θα ακολουθεί πια τ block_user.detail_2 = Ο χρήστης δεν θα μπορεί να αλληλεπιδράσει με τα αποθετήριά σας, να δημιουργήσει ζητήματα ή να αφήσει σχόλια. block_user.detail_3 = Ο χρήστης δεν θα μπορέσει να σας προσθέσει στα αποθετήριά του ως συνεργάτη και αντίστοιχα δεν θα μπορείτε να τον προσθέσετε στα δικά σας αποθετήρια. block_user.detail = Επισημαίνεται πως αν αποκλείσετε αυτόν τον χρήστη, θα προκύψουν ταυτόχρονα και άλλες ενέργειες. Μερικές από αυτές: +followers_one = %d ακόλουθος +following_one = ακολουθεί %d [settings] profile=Προφίλ @@ -656,30 +672,30 @@ account=Λογαριασμός appearance=Εμφάνιση password=Κωδικός πρόσβασης security=Ασφάλεια -avatar=Εικόνα +avatar=Εικόνα προφίλ ssh_gpg_keys=Κλειδιά SSH / GPG social=Λογαριασμοί κοινωνικών δικτύων applications=Εφαρμογές orgs=Διαχείριση οργανισμών repos=Αποθετήρια delete=Διαγραφή λογαριασμού -twofa=Πιστοποίηση Δύο Παραγόντων (TOTP) +twofa=Πιστοποίηση δύο παραγόντων (TOTP) account_link=Συνδεδεμένοι Λογαριασμοί organization=Οργανισμοί uid=UID -webauthn=Πιστοποίηση Δύο Παραγόντων (Κλειδιά Ασφαλείας) +webauthn=Πιστοποίηση δύο παραγόντων (Κλειδιά Ασφαλείας) public_profile=Δημόσιο προφίλ biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown) location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους profile_desc=Ελέγξτε πώς εμφανίζεται το προφίλ σας σε άλλους χρήστες. Η κύρια διεύθυνση email σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση κωδικού πρόσβασης και λειτουργίες Git που βασίζονται στο web. password_username_disabled=Οι μη τοπικοί χρήστες δεν επιτρέπεται να αλλάξουν το όνομα χρήστη τους. Επικοινωνήστε με το διαχειριστή σας για περισσότερες λεπτομέρειες. -full_name=Πλήρες Όνομα +full_name=Πλήρες όνομα website=Ιστοσελίδα location=Τοποθεσία -update_theme=Ενημέρωση Θέματος Διεπαφής -update_profile=Ενημέρωση Προφίλ -update_language=Ενημέρωση Γλώσσας +update_theme=Αλλαγή θέματος διεπαφής +update_profile=Ενημέρωση προφίλ +update_language=Αλλαγή γλώσσας update_language_not_found=Η γλώσσα «%s» δεν είναι διαθέσιμη. update_language_success=Η γλώσσα έχει ενημερωθεί. update_profile_success=Το προφίλ σας έχει ενημερωθεί. @@ -700,31 +716,31 @@ comment_type_group_milestone=Ορόσημο comment_type_group_assignee=Υπεύθυνος comment_type_group_title=Τίτλος comment_type_group_branch=Κλάδος -comment_type_group_time_tracking=Καταγραφή Χρόνου +comment_type_group_time_tracking=Καταγραφή χρόνου comment_type_group_deadline=Προθεσμία comment_type_group_dependency=Εξάρτηση -comment_type_group_lock=Κατάσταση Κλειδώματος +comment_type_group_lock=Κατάσταση κλειδώματος comment_type_group_review_request=Αίτηση αξιολόγησης comment_type_group_pull_request_push=Προστέθηκαν υποβολές comment_type_group_project=Έργο comment_type_group_issue_ref=Αναφορά ζητήματος saved_successfully=Οι ρυθμίσεις σας αποθηκεύτηκαν επιτυχώς. privacy=Απόρρητο -keep_activity_private=Απόκρυψη δραστηριότητας από τη σελίδα προφίλ -keep_activity_private_popup=Με αυτή την επιλογή η δραστηριότητα σας είναι ορατή μόνο σε εσάς και τους διαχειριστές +keep_activity_private=Απόκρυψη δραστηριότητας από το προφίλ +keep_activity_private_popup=Με αυτή την επιλογή η δραστηριότητα σας είναι ορατή μόνο σε εσάς και στους διαχειριστές -lookup_avatar_by_mail=Αναζήτηση ενός Avatar με διεύθυνση email -federated_avatar_lookup=Συνενωμένη Αναζήτηση Avatar +lookup_avatar_by_mail=Αναζήτηση εικόνας προφίλ με διεύθυνση email +federated_avatar_lookup=Αποκεντρωμένη αναζήτηση εικόνων προφίλ enable_custom_avatar=Χρήση προσαρμοσμένης εικόνας προφίλ -choose_new_avatar=Επιλέξτε νέα εικόνα -update_avatar=Ενημέρωση Εικόνας -delete_current_avatar=Διαγραφή Τρέχουσας Εικόνας -uploaded_avatar_not_a_image=Το αρχείο που ανεβάσατε δεν είναι εικόνα. -uploaded_avatar_is_too_big=Το μέγεθος του ανεβασμένου αρχείου (%d KiB) υπερβαίνει το μέγιστο μέγεθος (%d KiB). -update_avatar_success=To avatar σας έχει ενημερωθεί. -update_user_avatar_success=Το avatar του χρήστη ενημερώθηκε. +choose_new_avatar=Επιλογή νέας εικόνας προφίλ +update_avatar=Αλλαγή εικόνας +delete_current_avatar=Διαγραφή τρέχουσας εικόνας +uploaded_avatar_not_a_image=Το ανεβασμένο αρχείο δεν είναι εικόνα. +uploaded_avatar_is_too_big=Το μέγεθος του ανεβασμένου αρχείου (%d KiB) υπερβαίνει το μέγιστο όριο μεγέθους (%d KiB). +update_avatar_success=Η εικόνα προφίλ σας ενημερώθηκε. +update_user_avatar_success=Η εικόνα προφίλ του χρήστη έχει ενημερωθεί. -update_password=Ενημέρωση κωδικού πρόσβασης +update_password=Αλλαγή κωδικού πρόσβασης old_password=Τρέχων κωδικός πρόσβασης new_password=Νέος κωδικός πρόσβασης retype_new_password=Επιβεβαίωση νέου κωδικού @@ -735,7 +751,7 @@ password_change_disabled=Οι μη τοπικοί χρήστες δεν μπορ emails=Διευθύνσεις Email manage_emails=Διαχείριση διευθύνσεων email manage_themes=Επιλέξτε προεπιλεγμένο θέμα διεπαφής -manage_openid=Διαχείριση Διευθύνσεων OpenID +manage_openid=Διαχείριση διευθύνσεων OpenID email_desc=Η κύρια διεύθυνση ηλεκτρονικού ταχυδρομείου σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση του κωδικού πρόσβασης και, εφόσον δεν είναι κρυμμένη, λειτουργίες Git στον ιστότοπο. theme_desc=Αυτό θα είναι το προεπιλεγμένο θέμα διεπαφής σας σε όλη την ιστοσελίδα. primary=Κύριο @@ -756,27 +772,27 @@ openid_deletion_desc=Η κατάργηση αυτής της διεύθυνση openid_deletion_success=Η διεύθυνση OpenID αφαιρέθηκε. add_new_email=Προσθήκη νέας διεύθυνσης email add_new_openid=Προσθήκη Νέου OpenID URI -add_email=Προσθήκη Διεύθυνσης Email +add_email=Προσθήκη διεύθυνσης email add_openid=Προσθήκη OpenID URI add_email_confirmation_sent=Ένα email επιβεβαίωσης έχει σταλεί στην διεύθυνση «%s». Για να επιβεβαιώσετε τη διεύθυνση email σας, παρακαλώ ελέγξτε τα εισερχόμενα σας μέσα σε %s. add_email_success=Η νέα διεύθυνση email έχει προστεθεί. email_preference_set_success=Οι προτιμήσεις email έχουν οριστεί επιτυχώς. add_openid_success=Προστέθηκε η νέα διεύθυνση OpenID. keep_email_private=Απόκρυψη διεύθυνσης email -keep_email_private_popup=Αυτό θα κρύψει τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας από το προφίλ σας, καθώς και όταν κάνετε ένα pull request ή επεξεργαστείτε ένα αρχείο χρησιμοποιώντας την ιστοσελίδα. Οι υποβολές που ωθείτε δεν θα τροποποιηθούν. Χρησιμοποιήστε το %s στις υποβολές για να τις συσχετίσετε με το λογαριασμό σας. +keep_email_private_popup=Αυτό θα κρύψει το email σας από το προφίλ σας, καθώς και όταν κάνετε ένα pull request ή επεξεργάζεστε ένα αρχείο μέσω της ιστοσελίδας. Οι υποβολές που έχετε ήδη ωθήσει δεν θα τροποποιηθούν. Χρησιμοποιήστε το %s στις υποβολές για να τις συσχετίσετε με το λογαριασμό σας. openid_desc=Το OpenID σας επιτρέπει να αναθέσετε τον έλεγχο ταυτότητας σε έναν εξωτερικό πάροχο. manage_ssh_keys=Διαχείριση κλειδιών SSH manage_ssh_principals=Διαχείριση Των Αρχών Πιστοποιητικού SSH manage_gpg_keys=Διαχείριση κλειδιών GPG -add_key=Προσθήκη Κλειδιού +add_key=Προσθήκη κλειδιού ssh_desc=Αυτά τα δημόσια κλειδιά SSH θα συσχετιθούν με το λογαριασμό σας. Τα ιδιωτικά κλειδιά που τους αντιστοιχούν θα επιτρέπουν πλήρη πρόσβαση στα αποθετήριά σας. Ένα επιβεβαιωμένο κλειδί SSH μπορεί να χρησιμοποιηθεί για την υπογραφή των υποβολών (commits) σας. principal_desc=Αυτές οι αρχές πιστοποιητικών SSH συνδέονται με το λογαριασμό σας και επιτρέπουν την πλήρη πρόσβαση στα αποθετήριά σας. gpg_desc=Αυτά τα δημόσια κλειδιά GPG συσχετίζονται με το λογαριασμό σας και επιτρέπουν την επικύρωση των υποβολών (commits) σας. Κρατήστε τα ιδιωτικά κλειδιά σας ασφαλή, καθώς επιτρέπουν την υπογραφή των υποβολών (commits) εκ μέρους σας. ssh_helper=Χρειάζεστε βοήθεια; Συμβουλευτείτε τον οδηγό του GitHub για να δημιουργήσετε τα δικά σας κλειδιά SSH ή να επιλύσετε κοινά προβλήματα που ίσως αντιμετωπίσετε με τη χρήση του SSH. gpg_helper=Χρειάζεστε βοήθεια; Συμβουλευτείτε τον οδηγό του GitHub για το GPG. -add_new_key=Προσθήκη SSH Κλειδιού -add_new_gpg_key=Προσθήκη GPG Κλειδιού +add_new_key=Προσθήκη κλειδιού SSH +add_new_gpg_key=Προσθήκη κλειδιού GPG key_content_ssh_placeholder=Αρχίζει με «ssh-ed25519», «ssh-rsa», «ecdsa-sha2-nistp256», «ecdsa-sha2-nistp384», «ecdsa-sha2-nistp521», «sk-ecdsa-sha2-nistp256@openssh.com», ή «sk-ssh-ed25519@openssh.com» key_content_gpg_placeholder=Αρχίζει με «-----BEGIN PGP PUBLIC KEY BLOCK-----» add_new_principal=Προσθήκη Αρχής (Principal) @@ -787,7 +803,7 @@ gpg_key_id_used=Υπάρχει ήδη δημόσιο κλειδί GPG με το gpg_no_key_email_found=Αυτό το κλειδί GPG δεν ταιριάζει με οποιαδήποτε ενεργοποιημένη διεύθυνση ηλεκτρονικού ταχυδρομείου που σχετίζεται με το λογαριασμό σας. Μπορεί ακόμα να προστεθεί, αν υπογράψετε το παρεχόμενο διακριτικό (token). gpg_key_matched_identities=Ταυτότητες που ταιριάζουν: gpg_key_matched_identities_long=Οι ενσωματωμένες ταυτότητες σε αυτό το κλειδί ταιριάζουν με τις ακόλουθες ενεργοποιημένες διευθύνσεις email για αυτόν το χρήστη. Οι υποβολές που ταιριάζουν με αυτές τις διευθύνσεις email μπορούν να επαληθευτούν με αυτό το κλειδί. -gpg_key_verified=Επαληθευμένο Κλειδί +gpg_key_verified=Επαληθευμένο κλειδί gpg_key_verified_long=Το κλειδί έχει επαληθευτεί με ένα διακριτικό (token) και μπορεί να χρησιμοποιηθεί για να επαληθεύσει τις υποβολές που ταιριάζουν με οποιεσδήποτε ενεργοποιημένες διευθύνσεις email για αυτόν το χρήστη εκτός από οποιαδήποτε αντιστοιχισμένη ταυτότητα για αυτό το κλειδί. gpg_key_verify=Επαλήθευση gpg_invalid_token_signature=Το κλειδί GPG, η υπογραφή και το διακριτικό (token) δεν ταιριάζουν ή το διακριτικό (token) είναι παρωχημένο. @@ -798,7 +814,7 @@ gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Θωρακισμένη υπογραφή GPG key_signature_gpg_placeholder=Αρχίζει με «-----BEGIN PGP SIGNATURE-----» verify_gpg_key_success=Το κλειδί GPG «%s» επαληθεύτηκε. -ssh_key_verified=Επαληθευμένο Κλειδί +ssh_key_verified=Επαληθευμένο κλειδί ssh_key_verified_long=Το κλειδί έχει επαληθευτεί με ένα διακριτικό και μπορεί να χρησιμοποιηθεί για να επαληθεύσει τα commits που ταιριάζουν με οποιεσδήποτε ενεργοποιημένες διευθύνσεις ηλεκτρονικού ταχυδρομείου για αυτόν το χρήστη. ssh_key_verify=Επαλήθευση ssh_invalid_token_signature=Το παρεχόμενο κλειδί SSH, υπογραφή ή διακριτικό δεν ταιριάζει ή το διακριτικό έληξε. @@ -817,8 +833,8 @@ add_key_success=Το κλειδί SSH «%s» προστέθηκε. add_gpg_key_success=Το κλειδί GPG «%s» προστέθηκε. add_principal_success=Προστέθηκε η αρχή πιστοποίησης SSH «%s». delete_key=Διαγραφή -ssh_key_deletion=Διαγραφή Κλειδιού SSH -gpg_key_deletion=Διαγραφή Κλειδιού GPG +ssh_key_deletion=Διαγραφή κλειδιού SSH +gpg_key_deletion=Διαγραφή κλειδιού GPG ssh_principal_deletion=Διαγραφή Αρχών Πιστοποιητικού SSH ssh_key_deletion_desc=Η διαγραφή ενός κλειδιού SSH ανακαλεί την πρόσβασή του στο λογαριασμό σας. Συνέχεια; gpg_key_deletion_desc=Η διαγραφή ενός κλειδιού GPG απο-επαληθεύει τις υποβολές που έχουν υπογραφεί από αυτό. Συνέχεια; @@ -850,11 +866,11 @@ manage_access_token=Διαχείριση διακριτικών πρόσβαση generate_new_token=Δημιουργία νέου διακριτικού (token) tokens_desc=Αυτά τα διακριτικά (tokens) παρέχουν πρόσβαση στο λογαριασμό σας μέσω του API του Forgejo. token_name=Όνομα διακριτικού -generate_token=Δημιουργία Διακριτικού +generate_token=Δημιουργία διακριτικού generate_token_success=Το νέο διακριτικό σας έχει δημιουργηθεί. Αντιγράψτε το τώρα καθώς δεν θα εμφανιστεί ξανά. generate_token_name_duplicate=Το %s χρησιμοποιείται ήδη ως όνομα εφαρμογής. Παρακαλούμε χρησιμοποιήστε ένα νέο. delete_token=Διαγραφή -access_token_deletion=Διαγραφή Διακριτικού Πρόσβασης +access_token_deletion=Διαγραφή διακριτικού πρόσβασης access_token_deletion_cancel_action=Άκυρο access_token_deletion_confirm_action=Διαγραφή access_token_deletion_desc=Η διαγραφή ενός διακριτικού θα ανακαλέσει οριστικά την πρόσβαση στο λογαριασμό σας για εφαρμογές που το χρησιμοποιούν. Συνέχεια; @@ -863,9 +879,9 @@ repo_and_org_access=Πρόσβαση στο Αποθετήριο και Οργα permissions_public_only=Δημόσια μόνο permissions_access_all=Όλα (δημόσια, ιδιωτικά, και περιορισμένα) select_permissions=Επιλέξτε δικαιώματα -permission_no_access=Καμία Πρόσβαση +permission_no_access=Καμία πρόσβαση permission_read=Αναγνωσμένες -permission_write=Ανάγνωση και Εγγραφή +permission_write=Ανάγνωση και εγγραφή access_token_desc=Τα επιλεγμένα δικαιώματα διακριτικών περιορίζουν την άδεια μόνο στις αντίστοιχες διαδρομές API. Διαβάστε το εγχειρίδιο για περισσότερες πληροφορίες. at_least_one_permission=Πρέπει να επιλέξετε τουλάχιστον ένα δικαίωμα για να δημιουργήσετε ένα διακριτικό permissions_list=Δικαιώματα: @@ -877,7 +893,7 @@ remove_oauth2_application=Αφαίρεση Εφαρμογής Oauth2 remove_oauth2_application_desc=Η αφαίρεση μιας εφαρμογής OAuth2 θα ανακαλέσει την πρόσβαση σε όλα τα υπογεγραμμένα διακριτικά πρόσβασης. Συνέχεια; remove_oauth2_application_success=Η εφαρμογή έχει διαγραφεί. create_oauth2_application=Δημιουργία νέας εφαρμογής OAuth2 -create_oauth2_application_button=Δημιουργία Εφαρμογής +create_oauth2_application_button=Δημιουργία εφαρμογής create_oauth2_application_success=Δημιουργήσατε επιτυχώς μια νέα εφαρμογή OAuth2. update_oauth2_application_success=Ενημερώσατε την εφαρμογή OAuth2 επιτυχώς. oauth2_application_name=Όνομα εφαρμογής @@ -885,19 +901,19 @@ oauth2_confidential_client=Εμπιστευτικός Πελάτης. Επιλέ oauth2_redirect_uris=URI Ανακατεύθυνσης. Χρησιμοποιήστε μια νέα γραμμή για κάθε URI. save_application=Αποθήκευση oauth2_client_id=Ταυτότητα Πελάτη -oauth2_client_secret=Μυστικό Πελάτη -oauth2_regenerate_secret=Αναδημιουργία Μυστικού +oauth2_client_secret=Μυστικό πελάτη +oauth2_regenerate_secret=Δημιουργία νέου μυστικού oauth2_regenerate_secret_hint=Χάσατε το μυστικό σας; oauth2_client_secret_hint=Το μυστικό δε θα εμφανιστεί ξανά αν κλείσετε ή ανανεώσετε αυτή τη σελίδα. Παρακαλώ βεβαιωθείτε ότι το έχετε αποθηκεύσει. oauth2_application_edit=Επεξεργασία oauth2_application_create_description=Οι εφαρμογές OAuth2 δίνει πρόσβαση στην εξωτερική εφαρμογή σας σε λογαριασμούς χρηστών σε αυτή την υπηρεσία. oauth2_application_remove_description=Αφαιρώντας μια εφαρμογή OAuth2 θα αποτραπεί η πρόσβαση αυτής, σε εξουσιοδοτημένους λογαριασμούς χρηστών σε αυτή την υπηρεσία. Συνέχεια; -oauth2_application_locked=Το Forgejo κάνει προεγγραφή σε μερικές εφαρμογές OAuth2 κατά την εκκίνηση αν είναι ενεργοποιημένες στις ρυθμίσεις. Για την αποφυγή απροσδόκητης συμπεριφοράς, αυτές δεν μπορούν ούτε να επεξεργαστούν ούτε να καταργηθούν. Παρακαλούμε ανατρέξτε στην τεκμηρίωση OAuth2 για περισσότερες πληροφορίες. +oauth2_application_locked=Το Forgejo κάνει προεγγραφή σε μερικές εφαρμογές OAuth2 κατά την εκκίνηση του, εφόσον αυτές έχουν ενεργοποιηθεί στις ρυθμίσεις. Αυτές δεν μπορούν ούτε να επεξεργαστούν ούτε να καταργηθούν, έτσι ώστε να αποφευχθούν απροσδόκητα προβλήματα. Παρακαλούμε ανατρέξτε στην τεκμηρίωση OAuth2 για περισσότερες πληροφορίες. authorized_oauth2_applications=Εξουσιοδοτημένες εφαρμογές OAuth2 authorized_oauth2_applications_description=Έχετε χορηγήσει πρόσβαση στον προσωπικό σας λογαριασμό σε αυτές τις εφαρμογές τρίτων. Παρακαλείσθε να ανακαλέσετε την πρόσβαση για εφαρμογές που δεν χρειάζεστε πλέον. revoke_key=Ανάκληση -revoke_oauth2_grant=Ανάκληση Πρόσβασης +revoke_oauth2_grant=Ανάκληση πρόσβασης revoke_oauth2_grant_description=Αν ανακαλέσετε την πρόσβαση αυτής της ανεξάρτητης («third-party») εφαρμογής, θα ανακληθεί ταυτόχρονα και η πρόσβασή της στα δεδομένα σας. Είστε βέβαιοι; revoke_oauth2_grant_success=Η πρόσβαση ανακλήθηκε επιτυχώς. @@ -906,9 +922,9 @@ twofa_recovery_tip=Αν χάσετε τη συσκευή σας, θα είστε twofa_is_enrolled=Ο λογαριασμός σας είναι εγγεγραμμένος στην πιστοποίηση δύο παραγόντων. twofa_not_enrolled=Ο λογαριασμός σας δεν είναι εγγεγραμμένος στην πιστοποιήση δύο παραγόντων. twofa_disable=Απενεργοποίηση πιστοποίησης δύο παραγόντων -twofa_scratch_token_regenerate=Αναδημιουργία Διακριτικού Μίας Χρήσης +twofa_scratch_token_regenerate=Δημιουργία νέου κλειδιού μίας χρήσης twofa_scratch_token_regenerated=Το κλειδί ανάκτησης μιας χρήσης είναι τώρα %s. Αποθηκεύστε το σε ασφαλές μέρος, καθώς δε θα εμφανιστεί ξανά. -twofa_enroll=Εγγραφή στην πιστοποίηση δύο παραγόντων +twofa_enroll=Ενεργοποίηση πιστοποίησης δύο παραγόντων twofa_disable_note=Αν χρειαστεί, θα μπορέσετε να απενεργοποιήσετε την πιστοποίηση δύο παραγόντων μεταγενέστερα. twofa_disable_desc=Η απενεργοποίηση της πιστοποίησης δύο παραγόντων θα καταστήσει τον λογαριασμό σας λιγότερο ασφαλή. Είστε βέβαιοι; regenerate_scratch_token_desc=Αν χάσατε το διακριτικό μίας χρήσης σας ή το έχετε ήδη χρησιμοποιήσει για να συνδεθείτε μπορείτε να το επαναφέρετε εδώ. @@ -923,7 +939,7 @@ twofa_failed_get_secret=Αποτυχία λήψης μυστικού. webauthn_desc=Τα κλειδιά ασφαλείας είναι συσκευές που περιέχουν κρυπτογραφικά κλειδιά. Μπορούν να χρησιμοποιηθούν για την πιστοποίηση δύο παραγόντων. Τα κλειδιά ασφαλείας πρέπει να συνάδουν με τις προδιαγραφές που ορίζει το πρότυπο WebAuthn. webauthn_register_key=Προσθήκη κλειδιού ασφαλείας webauthn_nickname=Ψευδώνυμο -webauthn_delete_key=Αφαίρεση Κλειδιού Ασφαλείας +webauthn_delete_key=Αφαίρεση κλειδιού ασφαλείας webauthn_delete_key_desc=Αν αφαιρέσετε ένα κλειδί ασφαλείας δεν μπορείτε πλέον να συνδεθείτε με αυτό. Συνέχεια; webauthn_key_loss_warning=Αν χάσετε τα κλειδιά ασφαλείας σας, θα χάσετε την πρόσβαση στο λογαριασμό σας. webauthn_alternative_tip=Μπορεί να θέλετε να ρυθμίσετε μια πρόσθετη μέθοδο ταυτοποίησης. @@ -941,18 +957,18 @@ hooks.desc=Προσθήκη webhooks που θα ενεργοποιούνται orgs_none=Δεν είστε μέλος σε κάποιο οργανισμό. repos_none=Δεν σας ανήκει κανένα κάποιο αποθετήριο. -delete_account=Διαγραφή Του Λογαριασμού Σας +delete_account=Διαγραφή του λογαριασμού σας delete_prompt=Αυτή η ενέργεια θα διαγράψει μόνιμα το λογαριασμό σας. ΔΕΝ ΘΑ ΜΠΟΡΕΙ να επανέλθει. delete_with_all_comments=Ο λογαριασμός σας είναι νεότερος από %s. Για να αποφύγετε τα σχόλια φαντάσματα, όλα τα σχόλια σε ζητήματα/PR θα διαγραφούν από αυτόν. -confirm_delete_account=Επιβεβαίωση Διαγραφής -delete_account_title=Διαγραφή Λογαριασμού Χρήστη +confirm_delete_account=Επιβεβαίωση διαγραφής +delete_account_title=Διαγραφή λογαριασμού χρήστη delete_account_desc=Είστε βέβαιοι ότι θέλετε να διαγράψετε μόνιμα αυτό τον λογαριασμό; -email_notifications.enable=Ενεργοποίηση Ειδοποιήσεων Μέσω Email -email_notifications.onmention=Email Μόνο κατά την Αναφορά -email_notifications.disable=Απενεργοποίηση Ειδοποιήσεων μέσω Email -email_notifications.submit=Ορισμός Προτιμότερου Email -email_notifications.andyourown=Και Τις Δικές Σας Ειδοποιήσεις +email_notifications.enable=Ενεργοποίηση ειδοποιήσεων email +email_notifications.onmention=Να λαμβάνω email μόνο όταν με αναφέρουν +email_notifications.disable=Απενεργοποίηση ειδοποιήσεων email +email_notifications.submit=Ορισμός κύριου email +email_notifications.andyourown=Και τις δικιές σας ειδοποιήσεις visibility=Ορατότητα χρήστη visibility.public=Δημόσια @@ -967,6 +983,14 @@ user_unblock_success = Η άρση αποκλεισμού του χρήστη ή change_password = Αλλαγή κωδικού πρόσβασης blocked_users = Αποκλεισμένοι χρήστες user_block_success = Ο αποκλεισμός του χρήστη ήταν επιτυχής. +additional_repo_units_hint = Να γίνεται ενθάρρυνση προσθήκης μονάδων σε αποθετήρια +pronouns = Αντωνυμίες +pronouns_custom = Κάποια άλλη προτίμηση +pronouns_unspecified = Απροσδιόριστες +hints = Συμβουλές +additional_repo_units_hint_description = Εμφάνιση κουμπιού «Προσθήκη μονάδων...» σε αποθετήρια που δεν έχουν ενεργοποιημένες όλες τις διαθέσιμες μονάδες. +update_hints = Ενημέρωση συμβουλών +update_hints_success = Οι συμβουλές ενημερώθηκαν. [repo] new_repo_helper=Ένα αποθετήριο περιέχει όλα τα αρχεία έργου, συμπεριλαμβανομένου του ιστορικού εκδόσεων. Ήδη φιλοξενείται αλλού; Μετεγκατάσταση αποθετηρίου. @@ -983,10 +1007,10 @@ visibility=Ορατότητα visibility_description=Μόνο ο ιδιοκτήτης ή τα μέλη του οργανισμού εάν έχουν δικαιώματα, θα είναι σε θέση να το δουν. visibility_helper=Αλλαγή ορατότητας σε «Ιδιωτικό» visibility_helper_forced=Ο διαχειριστής σας αναγκάζει τα νέα αποθετήρια να είναι ιδιωτικά. -visibility_fork_helper=(Αλλάζοντας αυτό θα επηρεάσει όλα τα forks.) +visibility_fork_helper=(Η αλλαγή της ρύθμισης αυτής θα επηρεάσει και όλα τα forks.) clone_helper=Χρειάζεστε βοήθεια για τη κλωνοποίηση; Επισκεφθείτε τη Βοήθεια. -fork_repo=Δημιουργία ενός Fork -fork_from=Fork Από Το +fork_repo=Δημιουργία Fork +fork_from=Fork του already_forked=Έχετε ήδη κάνει fork το %s fork_to_different_account=Fork σε διαφορετικό λογαριασμό fork_visibility_helper=Η ορατότητα ενός fork αποθετηρίου δεν μπορεί να αλλάξει. @@ -999,13 +1023,13 @@ download_zip=Λήψη ZIP download_tar=Λήψη TAR.GZ download_bundle=Κατεβάστε Το ΔΕΜΑ generate_repo=Δημιουργία αποθετηρίου -generate_from=Δημιουργία Από +generate_from=Δημιουργία από repo_desc=Περιγραφή repo_desc_helper=Εισάγετε μια σύντομη περιγραφή (προαιρετικό) repo_lang=Γλώσσα repo_gitignore_helper=Επιλέξτε πρότυπα .gitignore. repo_gitignore_helper_desc=Επιλέξτε ποια αρχεία δεν θα παρακολουθείτε από μια λίστα προτύπων για κοινές γλώσσες προγραμματισμού. Τυπικά αντικείμενα που δημιουργούνται από τα εργαλεία κατασκευής κάθε γλώσσας περιλαμβάνονται ήδη στο .gitignore. -issue_labels=Σήματα Ζητήματος +issue_labels=Σήματα ζητημάτων issue_labels_helper=Επιλέξτε ένα σύνολο σημάτων ζητημάτων. license=Άδεια license_helper=Επιλέξτε ένα αρχείο άδειας. @@ -1013,14 +1037,14 @@ license_helper_desc=Μια άδεια διέπει τι άλλοι μπορού readme=README readme_helper=Επιλέξτε ένα πρότυπο αρχείου README. readme_helper_desc=Αυτό είναι το μέρος όπου μπορείτε να γράψετε μια πλήρη περιγραφή για το έργο σας. -auto_init=Αρχικοποίηση Αποθετηρίου (Προσθέτει .gitignore, License και README) +auto_init=Αρχικοποίηση αποθετηρίου (Προσθήκη .gitignore, άδειας χρήσης και README) trust_model_helper=Επιλέξτε ένα μοντέλο εμπιστοσύνης για την επαλήθευση υπογραφής. Πιθανές επιλογές είναι: trust_model_helper_collaborator=Συνεργάτης: Εμπιστοσύνη υπογραφών από συνεργάτες trust_model_helper_committer=Υποβολέας: Εμπιστοσύνη των υπογραφών που ταιριάζουν με τους υποβολείς trust_model_helper_collaborator_committer=Συνεργάτης+Υποβολέας: Εμπιστοσύνη των υπογραφών από συνεργάτες που ταιριάζουν με τον υποβολέα trust_model_helper_default=Προεπιλογή: Χρήση προεπιλεγμένου μοντέλου εμπιστοσύνης για αυτήν την εγκατάσταση create_repo=Δημιουργία αποθετηρίου -default_branch=Προεπιλεγμένος Κλάδος +default_branch=Προεπιλεγμένος κλάδος default_branch_label=προεπιλογή default_branch_helper=Ο προεπιλεγμένος κλάδος είναι ο βασικός κλάδος για pull requests και υποβολές κώδικα. mirror_prune=Καθαρισμός @@ -1028,7 +1052,7 @@ mirror_prune_desc=Αφαίρεση παρωχημένων αναφορών απ mirror_interval=Διάστημα ανανέωσης ειδώλου (έγκυρες μονάδες ώρας είναι "h", "m", "s"). 0 για απενεργοποίηση του αυτόματου συγχρονισμού. (Ελάχιστο διάστημα: %s) mirror_interval_invalid=Το χρονικό διάστημα του ειδώλου δεν είναι έγκυρο. mirror_sync_on_commit=Συγχρονισμός κατά την ώθηση -mirror_address=Κλωνοποίηση Από Το URL +mirror_address=Κλωνοποίηση από το URL mirror_address_desc=Τοποθετήστε όλα τα απαιτούμενα διαπιστευτήρια στην ενότητα Εξουσιοδότηση. mirror_address_url_invalid=Η παρεχόμενη διεύθυνση URL δεν είναι έγκυρη. Πρέπει να κάνετε escape όλα τα στοιχεία του url σωστά. mirror_address_protocol_invalid=Η παρεχόμενη διεύθυνση URL δεν είναι έγκυρη. Μόνο οι τοποθεσίες http(s):// ή git:// μπορούν να χρησιμοποιηθούν για τη δημιουργία ειδώλου. @@ -1036,7 +1060,7 @@ mirror_lfs=Large File Storage (LFS) mirror_lfs_desc=Ενεργοποίηση αντικατοπτρισμού δεδομένων LFS. mirror_lfs_endpoint=Άκρο LFS mirror_lfs_endpoint_desc=Ο συγχρονισμός θα προσπαθήσει να χρησιμοποιήσει το url κλωνοποίησης για να καθορίσει τον διακομιστή LFS. Μπορείτε επίσης να καθορίσετε μια άλλη διεύθυνση αν τα δεδομένα LFS του αποθετηρίου αποθηκεύονται κάπου αλλού. -mirror_last_synced=Τελευταίος Συγχρονισμός +mirror_last_synced=Τελευταίος συγχρονισμός mirror_password_placeholder=(Χωρίς αλλαγή) mirror_password_blank_placeholder=(Μη ορισμένο) mirror_password_help=Αλλάξτε το όνομα χρήστη για να διαγράψετε έναν αποθηκευμένο κωδικό πρόσβασης. @@ -1048,7 +1072,7 @@ reactions_more=και %d περισσότερα unit_disabled=Ο διαχειριστής του ιστότοπου έχει απενεργοποιήσει αυτήν την ενότητα αποθετηρίου. language_other=Άλλο adopt_search=Εισάγετε όνομα χρήστη για αναζήτηση μη υιοθετημένων αποθετηρίων... (αφήστε κενό για να βρείτε όλα) -adopt_preexisting_label=Υιοθέτηση Αρχείων +adopt_preexisting_label=Υιοθέτηση αρχείων adopt_preexisting=Υιοθετήστε τα προϋπάρχοντα αρχεία adopt_preexisting_content=Δημιουργία αποθετηρίου από %s adopt_preexisting_success=Υιοθετήθηκαν αρχεία και δημιουργήθηκε το αποθετήριο από %s @@ -1065,9 +1089,9 @@ tree_path_not_found_commit=Η διαδρομή %[1]s δεν υπάρχει στ tree_path_not_found_branch=Η διαδρομή %[1]s δεν υπάρχει στον κλάδο %[2]s tree_path_not_found_tag=Η διαδρομή %[1]s δεν υπάρχει στην ετικέτα %[2]s -transfer.accept=Αποδοχή Μεταφοράς +transfer.accept=Αποδοχή μεταφοράς transfer.accept_desc=`Μεταφορά στο «%s»` -transfer.reject=Απόρριψη Μεταφοράς +transfer.reject=Απόρριψη μεταφοράς transfer.reject_desc=`Ακύρωση μεταφοράς στο «%s»` transfer.no_permission_to_accept=Δεν έχετε άδεια να αποδεχτείτε αυτή τη μεταφορά. transfer.no_permission_to_reject=Δεν έχετε άδεια να απορρίψετε αυτή τη μεταφορά. @@ -1078,14 +1102,14 @@ desc.template=Πρότυπο desc.internal=Εσωτερικό desc.archived=Αρχειοθετημένο -template.items=Αντικείμενα Προτύπου -template.git_content=Περιεχόμενο Git (Προεπιλεγμένος Κλάδος) -template.git_hooks=Git Hooks -template.git_hooks_tooltip=Δεν μπορείτε να τροποποιήσετε ή να καταργήσετε τα Άγκιστρα Git αφού προστεθούν. Επιλέξτε αυτό μόνο αν εμπιστεύεστε το πρότυπο αποθετήριο. +template.items=Αντικείμενα προτύπου +template.git_content=Περιεχόμενο Git (Προεπιλεγμένος κλάδος) +template.git_hooks=Git hooks +template.git_hooks_tooltip=Δεν θα μπορέσετε να αφαιρέσετε ή να τροποποιήσετε τα Git hook αφού τα έχετε προσθέσει. Επιλέξτε την ρύθμιση αυτή μόνο αν εμπιστεύεστε το πρότυπο αποθετήριο. template.webhooks=Webhooks template.topics=Θέματα template.avatar=Εικόνα -template.issue_labels=Σήματα Ζητήματος +template.issue_labels=Σήματα ζητημάτων template.one_item=Πρέπει να επιλέξετε τουλάχιστον ένα αντικείμενο στο πρότυπο template.invalid=Πρέπει να επιλέξετε ένα πρότυπο αποθετήριο @@ -1113,10 +1137,10 @@ migrate_items_wiki=Wiki migrate_items_milestones=Ορόσημα migrate_items_labels=Σήματα migrate_items_issues=Ζητήματα -migrate_items_pullrequests=Pull Requests -migrate_items_merge_requests=Merge Requests +migrate_items_pullrequests=Pull requests +migrate_items_merge_requests=Merge requests migrate_items_releases=Κυκλοφορίες -migrate_repo=Μεταφορά Αποθετηρίου +migrate_repo=Μεταφορά αποθετηρίου migrate.clone_address=Μεταφορά / Κλωνοποίηση από το URL migrate.clone_address_desc=Το HTTP(S) ή το Git URL «κλωνοποίησης» ενός υπάρχοντος αποθετηρίου migrate.github_token_desc=Μπορείτε να βάλετε ένα ή περισσότερα διακριτικά εδώ, χωρισμένα με κόμμα, για να κάνετε τη μετεγκατάσταση πιο γρήγορα, λόγω του ορίου ρυθμού του GitHub API. ΠΡΟΣΟΧΗ: Η κατάχρηση αυτής της δυνατότητας μπορεί να παραβιάσει την πολιτική του παρόχου υπηρεσιών και να οδηγήσει σε αποκλεισμό του λογαριασμού σας. @@ -1126,7 +1150,7 @@ migrate.permission_denied_blocked=Δεν μπορείτε να εισαγάγε migrate.invalid_local_path=Η τοποθεσία αρχείου δεν είναι έγκυρη. Το αρχείο δεν υπάρχει ή δεν είναι φάκελος. migrate.invalid_lfs_endpoint=Η διεύθυνση LFS δεν είναι έγκυρο. migrate.failed=Η μεταφορά απέτυχε: %v -migrate.migrate_items_options=Το Διακριτικό Πρόσβασης απαιτείται για τη μεταφορά πρόσθετων στοιχείων +migrate.migrate_items_options=Το διακριτικό πρόσβασης (token) είναι απαραίτητο για τη μεταφορά πρόσθετων στοιχείων migrated_from=Μεταφέρθηκε από %[2]s migrated_from_fake=Μεταφέρθηκε από %[1]s migrate.migrate=Μεταφορά από το %s @@ -1142,19 +1166,19 @@ migrate.gogs.description=Μεταφορά δεδομένων από το notabug migrate.onedev.description=Μεταφορά δεδομένων από το code.onedev.io ή άλλες εγκαταστάσεις OneDev. migrate.codebase.description=Μεταφορά δεδομένων από το codebasehq.com. migrate.gitbucket.description=Μεταφορά δεδομένων από διακομιστές GitBucket. -migrate.migrating_git=Μεταφορά Δεδομένων Git -migrate.migrating_topics=Μεταφορά Θεμάτων -migrate.migrating_milestones=Μετανάστευση Ορόσημων -migrate.migrating_labels=Μεταφορά Σημάτων -migrate.migrating_releases=Μεταφορά Κυκλοφοριών -migrate.migrating_issues=Μετανάστευση Θεμάτων -migrate.migrating_pulls=Μεταφορά Pull Requests -migrate.cancel_migrating_title=Ακύρωση Μεταφοράς +migrate.migrating_git=Τα δεδομένα Git μεταφέρονται +migrate.migrating_topics=Τα θέματα μεταφέρονται +migrate.migrating_milestones=Τα ορόσημα μεταφέρονται +migrate.migrating_labels=Τα σήματα μεταφέρονται +migrate.migrating_releases=Οι κυκλοφορίες μεταφέρονται +migrate.migrating_issues=Τα ζητήματα μεταφέρονται +migrate.migrating_pulls=Μεταφέρονται τα pull requests +migrate.cancel_migrating_title=Ακύρωση μεταφοράς migrate.cancel_migrating_confirm=Θέλετε να ακυρώσετε αυτή τη μεταφορά; mirror_from=είδωλο του forked_from=forked από -generated_from=παράγονται από +generated_from=παραγμένο από fork_from_self=Δεν μπορείτε να κάνετε fork σε ένα αποθετήριο που κατέχετε. fork_guest_user=Συνδεθείτε για να κάνετε fork αυτό το αποθετήριο. watch_guest_user=Συνδεθείτε για να παρακολουθήσετε αυτό το αποθετήριο. @@ -1164,11 +1188,11 @@ watch=Παρακολούθηση unstar=Όχι Αστέρι star=Αστέρι fork=Fork -download_archive=Λήψη Αποθετηρίου -more_operations=Περισσότερες Λειτουργίες +download_archive=Λήψη αποθετηρίου +more_operations=Περισσότερες λειτουργίες -no_desc=Χωρίς Περιγραφή -quick_guide=Γρήγορος Οδηγός +no_desc=Χωρίς περιγραφή +quick_guide=Γρήγορος οδηγός clone_this_repo=Κλωνοποίηση αυτού του αποθετηρίου cite_this_repo=Αναφορά σε αυτό το αποθετήριο create_new_repo_command=Δημιουργία νέου αποθετηρίου στη γραμμή εντολών @@ -1186,7 +1210,7 @@ find_tag=Εύρεση ετικέτας branches=Κλάδοι tags=Ετικέτες issues=Ζητήματα -pulls=Pull Requests +pulls=Pull requests project_board=Έργα packages=Πακέτα actions=Actions @@ -1205,9 +1229,9 @@ tagged_this=πρόσθεσε ετικέτα για το file.title=%s στο %s file_raw=Ακατέργαστο file_history=Ιστορικό -file_view_source=Προβολή Πηγαίου -file_view_rendered=Προβολή Απόδοσης -file_view_raw=Προβολή Ακατέργαστου +file_view_source=Προβολή πηγαίου κώδικα +file_view_rendered=Προβολή προεπισκόπησης +file_view_raw=Προβολή ακατέργαστου file_permalink=Permalink file_too_large=Το αρχείο είναι πολύ μεγάλο για να εμφανιστεί. invisible_runes_header=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode ` @@ -1220,16 +1244,16 @@ ambiguous_character=`ο %[1]c [U+%04[1]X] μπορεί να μπερδευτεί escape_control_characters=Escape unescape_control_characters=Unescape -file_copy_permalink=Αντιγραφή Permalink -view_git_blame=Προβολή Git Blame +file_copy_permalink=Αντιγραφή permalink +view_git_blame=Προβολή git blame video_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «video». audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 «audio». stored_lfs=Αποθηκεύτηκε με το Git LFS symbolic_link=Symbolic link -executable_file=Εκτελέσιμο Αρχείο -commit_graph=Γράφημα Υποβολών +executable_file=Εκτελέσιμο αρχείο +commit_graph=Γράφημα υποβολών commit_graph.select=Επιλογή κλάδων -commit_graph.hide_pr_refs=Απόκρυψη Pull Requests +commit_graph.hide_pr_refs=Απόκρυψη pull request commit_graph.monochrome=Μονόχρωμο commit_graph.color=Έγχρωμο commit.contained_in=Αυτή η υποβολή περιλαμβάνεται σε: @@ -1242,26 +1266,26 @@ line=γραμμή lines=γραμμές from_comment=(σχόλιο) -editor.add_file=Προσθήκη Αρχείου -editor.new_file=Νέο Αρχείο -editor.upload_file=Ανέβασμα Αρχείου -editor.edit_file=Επεξεργασία Αρχείου -editor.preview_changes=Προεπισκόπηση Αλλαγών +editor.add_file=Προσθήκη αρχείου +editor.new_file=Νέο αρχείο +editor.upload_file=Ανέβασμα αρχείου +editor.edit_file=Επεξεργασία αρχείου +editor.preview_changes=Προεπισκόπηση αλλαγών editor.cannot_edit_lfs_files=Τα αρχεία LFS δεν μπορούν να επεξεργαστούν στη διεπαφή web. editor.cannot_edit_non_text_files=Τα δυαδικά αρχεία δεν μπορούν να επεξεργαστούν στη διεπαφή web. -editor.edit_this_file=Επεξεργασία Αρχείου +editor.edit_this_file=Επεξεργασία αρχείου editor.this_file_locked=Το αρχείο είναι κλειδωμένο editor.must_be_on_a_branch=Πρέπει να βρίσκεστε σε έναν κλάδο για να κάνετε ή να προτείνετε αλλαγές σε αυτό το αρχείο. editor.fork_before_edit=Πρέπει να κάνετε fork αυτό το αποθετήριο για να κάνετε ή να προτείνετε αλλαγές σε αυτό το αρχείο. -editor.delete_this_file=Διαγραφή Αρχείου +editor.delete_this_file=Διαγραφή αρχείου editor.must_have_write_access=Πρέπει να έχετε πρόσβαση εγγραφής για να κάνετε ή να προτείνετε αλλαγές σε αυτό το αρχείο. editor.file_delete_success=Το αρχείο «%s» έχει διαγραφεί. editor.name_your_file=Ονομάστε το αρχείο σας… editor.filename_help=Προσθέστε έναν φάκελο πληκτρολογώντας μια κάθετο ('/') και το όνομα του φακέλου. Αφαιρέστε έναν φάκελο πληκτρολογώντας ένα backspace στην αρχή του πεδίου. editor.or=ή editor.cancel_lower=Ακύρωση -editor.commit_signed_changes=Υποβολή Υπογεγραμμένων Αλλαγών -editor.commit_changes=Υποβολή Αλλαγών +editor.commit_signed_changes=Υποβολή υπογεγραμμένων αλλαγών +editor.commit_changes=Υποβολή αλλαγών editor.add_tmpl=Προσθήκη «» editor.add=Προσθήκη %s editor.update=Ενημέρωση %s @@ -1288,15 +1312,15 @@ editor.file_is_a_symlink=`Το «%s» είναι συμβολικός σύνδε editor.filename_is_a_directory=Το όνομα αρχείου «%s» χρησιμοποιείται ήδη ως όνομα φακέλου σε αυτό το αποθετήριο. editor.file_editing_no_longer_exists=Το αρχείο «%s», το οποίο επεξεργάζεστε, δεν υπάρχει πλέον σε αυτό το αποθετήριο. editor.file_deleting_no_longer_exists=Το αρχείο «%s», το οποίο διαγράφεται, δεν υπάρχει πλέον σε αυτό το αποθετήριο. -editor.file_changed_while_editing=Τα περιεχόμενα του αρχείου άλλαξαν από τότε που ξεκίνησε η επεξεργασία. Κάντε κλικ εδώ για να τα δείτε ή Υποβολή Αλλαγών ξανά για να τα αντικαταστήσετε. +editor.file_changed_while_editing=Προέκυψαν κάποιες αλλαγές στα περιεχόμενα του αρχείου από τότε που ξεκινήσατε να τα επεξεργάζεστε. Κάντε κλικ εδώ για να τα δείτε ή ξανακάντε μία υποβολή των αλλαγών σας για να τις αντικαταστήσετε. editor.file_already_exists=Υπάρχει ήδη ένα αρχείο με το όνομα «%s» σε αυτό το αποθετήριο. editor.commit_empty_file_header=Υποβολή ενός κενού αρχείου editor.commit_empty_file_text=Το αρχείο που πρόκειται να υποβληθεί είναι κενό. Συνέχεια; editor.no_changes_to_show=Δεν υπάρχουν αλλαγές για εμφάνιση. editor.fail_to_update_file=Αποτυχία ενημέρωσης/δημιουργίας του αρχείου «%s». editor.fail_to_update_file_summary=Μήνυμα Σφάλματος: -editor.push_rejected_no_message=Η αλλαγή απορρίφθηκε από το διακομιστή χωρίς κάποιο μήνυμα. Παρακαλώ ελέγξτε τα Άγκιστρα Git. -editor.push_rejected=Η αλλαγή απορρίφθηκε από τον διακομιστή. Παρακαλώ ελέγξτε τα Άγκιστρα Git. +editor.push_rejected_no_message=Η αλλαγή απορρίφθηκε από τον διακομιστή και δεν γνωρίζουμε τον λόγο. Παρακαλώ ελέγξτε τα Git hooks σας. +editor.push_rejected=Η αλλαγή απορρίφθηκε από τον διακομιστή. Παρακαλώ ελέγξτε τα Git hook σας. editor.push_rejected_summary=Μήνυμα Πλήρους Απόρριψης: editor.add_subdir=Προσθήκη φακέλου… editor.unable_to_upload_files=Αποτυχία αποστολής αρχείων στο «%s» με το σφάλμα: %v @@ -1325,8 +1349,8 @@ commits.newer=Νεότερα commits.signed_by=Υπογράφηκε από commits.signed_by_untrusted_user=Υπογράφηκε από μη έμπιστο χρήστη commits.signed_by_untrusted_user_unmatched=Υπογράφηκε από ένα μη έμπιστο χρήστη ο οποίος δεν ταιριάζει με τον υποβολέα -commits.gpg_key_id=ID Κλειδιού GPG -commits.ssh_key_fingerprint=Αποτύπωμα Κλειδιού SSH +commits.gpg_key_id=Αναγνωριστικό κλειδιού GPG +commits.ssh_key_fingerprint=Αποτύπωμα κλειδιού SSH commits.view_path=Προβολή σε αυτή τη στιγμή στο ιστορικό commit.operations=Λειτουργίες @@ -1342,27 +1366,27 @@ commitstatus.failure=Αποτυχία commitstatus.pending=Εκκρεμεί commitstatus.success=Επιτυχές -ext_issues=Πρόσβαση στα Εξωτερικά Ζητήματα +ext_issues=Πρόσβαση σε εξωτερικά ζητήματα ext_issues.desc=Σύνδεση σε εξωτερικό εφαρμογή ζητημάτων. projects=Έργα projects.desc=Διαχείριση ζητημάτων και pulls στους πίνακες των έργων. projects.description=Περιγραφή (προαιρετικό) projects.description_placeholder=Περιγραφή -projects.create=Δημιουργία Έργου +projects.create=Δημιουργία έργου projects.title=Τίτλος projects.new=Νέο έργο projects.new_subheader=Συντονισμός, παρακολούθηση και ενημέρωση της δουλειάς σας σε ένα μέρος, έτσι ώστε τα έργα να παραμένουν διαφανή και μέσα στο χρονοδιάγραμμα. projects.create_success=Το έργο «%s» δημιουργήθηκε. -projects.deletion=Διαγραφή Έργου +projects.deletion=Διαγραφή έργου projects.deletion_desc=Η διαγραφή ενός έργου το αφαιρεί από όλα τα σχετιζόμενα ζητήματα. Συνέχεια; projects.deletion_success=Το έργο έχει διαγραφεί. -projects.edit=Επεξεργασία Έργων +projects.edit=Επεξεργασία έργων projects.edit_subheader=Τα Έργα οργανώνουν τα ζητήματα και παρακολουθούν τη πρόοδο τους. -projects.modify=Ενημέρωση Έργου +projects.modify=Ενημέρωση έργου projects.edit_success=Το έργο «%s» ενημερώθηκε. projects.type.none=Κανένα -projects.type.basic_kanban=Βασικό Kanban +projects.type.basic_kanban=Απλό Kanban projects.type.bug_triage=Διαλογή σφαλμάτων projects.template.desc=Πρότυπο έργου projects.template.desc_helper=Επιλέξτε ένα πρότυπο έργου για να ξεκινήσετε @@ -1377,7 +1401,7 @@ projects.column.set_default_desc=Ορίστε αυτή τη στήλη ως πρ projects.column.unset_default=Αφαίρεση προεπιλογής projects.column.unset_default_desc=Αφαίρεση της προεπιλογής αυτής της στήλης projects.column.delete=Διαγραφή στήλης -projects.column.deletion_desc=Όταν διαγράφεται μία στήλη έργου, όλα τα ζητήματα που ανήκουν σε αυτή θα μείνουν «Χωρίς Κατηγορία». Συνέχεια; +projects.column.deletion_desc=Όταν διαγράφεται κάποια στήλη έργου, όλα τα ζητήματα που ανήκουν σε αυτή θα μείνουν «Χωρίς Κατηγορία». Να γίνει συνέχεια; projects.column.color=Έγχρωμο projects.open=Άνοιγμα projects.close=Κλείσιμο @@ -1392,7 +1416,7 @@ issues.filter_milestones=Φίλτρο Ορόσημου issues.filter_projects=Φίλτρο Έργου issues.filter_labels=Φίλτρο Σημάτων issues.filter_reviewers=Φιλτράρισμα Εξεταστή -issues.new=Νέο Ζήτημα +issues.new=Νέο ζήτημα issues.new.title_empty=Ο τίτλος δεν μπορεί να είναι κενός issues.new.labels=Σήματα issues.new.no_label=Χωρίς Σήμα @@ -1400,35 +1424,35 @@ issues.new.clear_labels=Καθαρισμός σημάτων issues.new.projects=Έργα issues.new.clear_projects=Εκκαθάριση έργων issues.new.no_projects=Χωρίς έργα -issues.new.open_projects=Ανοιχτά Έργα -issues.new.closed_projects=Κλειστά Έργα +issues.new.open_projects=Ανοιχτά έργα +issues.new.closed_projects=Κλειστά έργα issues.new.no_items=Δεν υπάρχουν αντικείμενα issues.new.milestone=Ορόσημο -issues.new.no_milestone=Χωρίς Ορόσημο +issues.new.no_milestone=Χωρίς ορόσημο issues.new.clear_milestone=Καθαρισμός ορόσημου -issues.new.open_milestone=Ανοιχτά Ορόσημα -issues.new.closed_milestone=Κλειστά Ορόσημα +issues.new.open_milestone=Ανοιχτά ορόσημα +issues.new.closed_milestone=Κλειστά ορόσημα issues.new.assignees=Υπεύθυνοι issues.new.clear_assignees=Εκκαθάριση υπεύθυνων issues.new.no_assignees=Χωρίς υπεύθυνους issues.new.no_reviewers=Δεν υπάρχουν εξεταστές -issues.choose.get_started=Ας Αρχίσουμε +issues.choose.get_started=Ας ξεκινήσουμε issues.choose.open_external_link=Άνοιγμα issues.choose.blank=Προεπιλογή issues.choose.blank_about=Δημιουργήστε ένα ζήτημα από το προεπιλεγμένο πρότυπο. issues.choose.ignore_invalid_templates=Μη έγκυρα πρότυπα έχουν αγνοηθεί issues.choose.invalid_templates=%v βρέθηκε μη έγκυρο πρότυπο(α) issues.choose.invalid_config=Οι ρυθμίσεις ζητημάτων περιέχουν λάθη: -issues.no_ref=Δεν Έχει Οριστεί Κλάδος/Ετικέτα -issues.create=Δημιουργία Ζητήματος -issues.new_label=Νέο Σήμα +issues.no_ref=Δεν έχει οριστεί κλάδος/ετικέτα +issues.create=Δημιουργία ζητήματος +issues.new_label=Νέο σήμα issues.new_label_placeholder=Όνομα σήματος issues.new_label_desc_placeholder=Περιγραφή -issues.create_label=Δημιουργία Σήματος +issues.create_label=Δημιουργία σήματος issues.label_templates.title=Χρήση ενός προκαθορισμένου συνόλου σημάτων issues.label_templates.info=Δεν υπάρχουν σήματα ακόμα. Δημιουργήστε ένα σήμα με το κουμπί «Νέο Σήμα» ή χρησιμοποιήστε ένα σετ προκαθορισμένων σημάτων: -issues.label_templates.helper=Επιλέξτε ένα σύνολο σημάτων -issues.label_templates.use=Χρήση Συνόλου Σημάτων +issues.label_templates.helper=Επιλέξτε ένα σύνολο προεπιλεγμένων σημάτων +issues.label_templates.use=Χρήση συνόλου προεπιλεγμένων σημάτων issues.label_templates.fail_to_load_file=Αποτυχία φόρτωσης των προτύπων σημάτων από το αρχείο «%s»: %v issues.add_label=πρόσθεσε τη σήμανση %s %s issues.add_labels=πρόσθεσε τα σήματα %s %s @@ -1514,18 +1538,18 @@ issues.num_comments_1=%d σχόλιο issues.num_comments=%d σχόλια issues.commented_at=`σχολίασε %s` issues.delete_comment_confirm=Είστε βέβαιοι πως θέλετε να διαγράψετε αυτό το σχόλιο; -issues.context.copy_link=Αντιγραφή Συνδέσμου -issues.context.quote_reply=Παράθεση Απάντησης -issues.context.reference_issue=Αναφορά σε νέο ζήτημα +issues.context.copy_link=Αντιγραφή συνδέσμου +issues.context.quote_reply=Παράθεση απάντησης +issues.context.reference_issue=Παράθεση σε νέο ζήτημα issues.context.edit=Επεξεργασία issues.context.delete=Διαγραφή issues.no_content=Δεν υπάρχει περιγραφή. -issues.close=Κλείσιμο Ζητήματος +issues.close=Κλείσιμο ζητήματος issues.comment_pull_merged_at=συγχώνευσε την υποβολή %[1]s στο %[2]s %[3]s issues.comment_manually_pull_merged_at=συγχώνευσε χειροκίνητα την υποβολή %[1]s στο %[2]s %[3]s -issues.close_comment_issue=Σχόλιο και κλείσιμο +issues.close_comment_issue=Αποστολή σχολίου και κλείσιμο ζητήματος issues.reopen_issue=Ανοίξτε ξανά -issues.reopen_comment_issue=Σχόλιο και Άνοιγμα ξανά +issues.reopen_comment_issue=Αποστολή σχολίου και επανάνοιγμα ζητήματος issues.create_comment=Προσθήκη Σχολίου issues.closed_at=`αυτό το ζήτημα έκλεισε %[2]s` issues.reopened_at=`ξανά άνοιξε αυτό το ζήτημα %[2]s` @@ -1553,7 +1577,7 @@ issues.re_request_review=Επαναίτηση ανασκόπησης issues.is_stale=Έχουν υπάρξει αλλαγές σε αυτό το PR από αυτή την αναθεώρηση issues.remove_request_review=Αφαίρεση αιτήματος αναθεώρησης issues.remove_request_review_block=Δεν είναι δυνατή η αφαίρεση του αιτήματος αξιολόγησης -issues.dismiss_review=Απόρριψη Αξιολόγησης +issues.dismiss_review=Απόρριψη αξιολόγησης issues.dismiss_review_warning=Είστε βέβαιοι ότι θέλετε να απορρίψετε αυτή την αξιολόγηση; issues.sign_in_require_desc=Συνδεθείτε για να συμμετάσχετε σε αυτή τη συνομιλία. issues.edit=Επεξεργασία @@ -1563,7 +1587,7 @@ issues.label_title=Όνομα σήματος issues.label_description=Περιγραφή σήματος issues.label_color=Χρώμα σήματος issues.label_exclusive=Αποκλειστικό -issues.label_archive=Αρχειοθέτηση Σήματος +issues.label_archive=Αρχειοθέτηση σήματος issues.label_archived_filter=Εμφάνιση αρχειοθετημένων σημάτων issues.label_archive_tooltip=Τα αρχειοθετημένα σήματα εξαιρούνται από τις προτάσεις στην αναζήτηση με σήματα. issues.label_exclusive_desc=Ονομάστε το σήμα πεδίο/στοιχείο για να το κάνετε αμοιβαία αποκλειστικό με άλλα σήματα πεδίου/. @@ -1572,8 +1596,8 @@ issues.label_count=%d σήματα issues.label_open_issues=%d ανοικτά ζητήματα issues.label_edit=Επεξεργασία issues.label_delete=Διαγραφή -issues.label_modify=Επεξεργασία Σήματος -issues.label_deletion=Διαγραφή Σήματος +issues.label_modify=Επεξεργασία σήματος +issues.label_deletion=Διαγραφή σήματος issues.label_deletion_desc=Η διαγραφή ενός σήματος θα το αφαιρέσει από όλα τα ζητήματα. Να γίνει συνέχεια; issues.label_deletion_success=Το σήμα έχει διαγραφεί. issues.label.filter_sort.alphabetically=Αλφαβητικά @@ -1585,7 +1609,7 @@ issues.attachment.open_tab=`Πατήστε εδώ για να ανοίξετε issues.attachment.download=`Πατήστε εδώ για να κατεβάσετε το «%s»` issues.subscribe=Εγγραφή issues.unsubscribe=Διαγραφή -issues.unpin_issue=Άφεση Ζητήματος +issues.unpin_issue=Ξεκαρφίτσωμα ζητήματος issues.max_pinned=Δεν μπορείτε να καρφιτσώσετε περισσότερα ζητήματα issues.pin_comment=καρφίτσωσε το %s issues.unpin_comment=ξεκαρφίτσωσε το %s @@ -1621,7 +1645,7 @@ issues.stop_tracking=Διακοπή χρονόμετρου issues.stop_tracking_history=`σταμάτησε να εργάζεται %s` issues.cancel_tracking=Απόρριψη issues.cancel_tracking_history=`ακύρωσε τη καταγραφή χρόνου %s` -issues.add_time=Χειροκίνητη προσθήκη ώρας +issues.add_time=Χειροκίνητη προσθήκη χρόνου issues.del_time=Διαγραφή αυτής της καταγραφής χρόνου issues.add_time_short=Προσθήκη χρόνου issues.add_time_cancel=Ακύρωση @@ -1630,8 +1654,8 @@ issues.del_time_history=`διέγραψε χρόνο που δαπανήθηκε issues.add_time_hours=ώρες issues.add_time_minutes=λεπτά issues.add_time_sum_to_small=Δεν εισήχθη χρόνος. -issues.time_spent_total=Συνολική δαπάνη χρόνου -issues.time_spent_from_all_authors=`Συνολική δαπάνη χρόνου: %s` +issues.time_spent_total=Συνολικός χρόνος +issues.time_spent_from_all_authors=`Συνολικός χρόνος: %s` issues.due_date=Ημερομηνία παράδοσης issues.invalid_due_date_format=Η ημερομηνίας παράδοσης πρέπει να έχει την μορφή «εεεε-μμ-ηη». issues.error_modifying_due_date=Προέκυψε σφάλμα κατά την αλλαγή ημερομηνίας παράδοσης. @@ -1675,7 +1699,7 @@ issues.dependency.blocked_by_short=Εξαρτάται από issues.dependency.remove_header=Αφαίρεση εξάρτησης issues.dependency.issue_remove_text=Αυτό θα αφαιρέσει την εξάρτηση από αυτό το ζήτημα. Να γίνει συνέχεια; issues.dependency.pr_remove_text=Αυτό θα αφαιρέσει την εξάρτηση από αυτό το pull request. Συνέχεια; -issues.dependency.setting=Ενεργοποίηση Εξαρτήσεων Για Ζητήματα και Pull Requests +issues.dependency.setting=Ενεργοποίηση εξαρτήσεων για ζητήματα και pull requests issues.dependency.add_error_same_issue=Δεν μπορείτε να εξαρτάτε ένα ζήτημα από τον εαυτό του. issues.dependency.add_error_dep_issue_not_exist=Εξαρτώμενο ζήτημα δεν υπάρχει. issues.dependency.add_error_dep_not_exist=Δεν υπάρχει η Εξάρτηση. @@ -1724,9 +1748,9 @@ compare.compare_base=βάση compare.compare_head=σύγκριση pulls.desc=Ενεργοποίηση των pull requests και της αξιολόγησης κώδικα. -pulls.new=Νέο Pull Request -pulls.view=Προβολή Pull Request -pulls.compare_changes=Νέο Pull Request +pulls.new=Νέο pull request +pulls.view=Προβολή pull request +pulls.compare_changes=Νέο pull request pulls.allow_edits_from_maintainers=Επιτρέπεται η επεξεργασία από συντηρητές pulls.allow_edits_from_maintainers_desc=Οι χρήστες με πρόσβαση εγγραφής στον βασικό κλάδο μπορούν επίσης να ωθήσουν και σε αυτό τον κλάδο pulls.allow_edits_from_maintainers_err=Η ενημέρωση απέτυχε @@ -1752,13 +1776,13 @@ pulls.filter_changes_by_commit=Φιλτράρισμα κατά υποβολή pulls.nothing_to_compare=Αυτοί οι κλάδοι είναι όμοιοι. Δεν υπάρχει ανάγκη να δημιουργήσετε ένα pull request. pulls.nothing_to_compare_and_allow_empty_pr=Αυτοί οι κλάδοι είναι ίσοι. Αυτό το PR θα είναι κενό. pulls.has_pull_request=`Υπάρχει ήδη pull request μεταξύ αυτών των κλάδων: %[2]s#%[3]d` -pulls.create=Δημιουργία Pull Request +pulls.create=Δημιουργία pull request pulls.title_desc_few=θέλει να συγχωνεύσει %[1]d υποβολές από %[2]s σε %[3]s pulls.merged_title_desc_few=συγχώνευσε %[1]d υποβολές από %[2]s σε %[3]s %[4]s pulls.change_target_branch_at=`άλλαξε τον κλάδο προορισμού από %s σε %s %s` pulls.tab_conversation=Συζήτηση pulls.tab_commits=Υποβολές -pulls.tab_files=Αρχεία Με Αλλαγές +pulls.tab_files=Αλλαγμένα αρχεία pulls.reopen_to_merge=Παρακαλώ ανοίξτε ξανά αυτό το pull request για να εκτελέσετε μια συγχώνευση. pulls.cant_reopen_deleted_branch=Αυτό το pull request δεν μπορεί να ανοίξει ξανά επειδή ο κλάδος διαγράφηκε. pulls.merged=Συγχωνευμένο @@ -1775,17 +1799,17 @@ pulls.remove_prefix=Αφαίρεση %s προθέματος pulls.data_broken=Αυτό το pull request είναι κατεστραμμένο λόγω των πληροφοριών του fork που λείπουν. pulls.files_conflicted=Αυτό το pull request περιέχει αλλαγές που συγκρούονται με το κλάδο προορισμού. pulls.is_checking=Ο έλεγχος συγκρούσεων κατά την συγχώνευση βρίσκεται σε εξέλιξη. Δοκιμάστε ξανά σε λίγα λεπτά. -pulls.is_ancestor=Αυτός ο κλάδος περιλαμβάνεται ήδη στον κλάδο προορισμού. Δεν υπάρχει τίποτα για συγχώνευση. +pulls.is_ancestor=Αυτός ο κλάδος δεν περιέχει κάτι καινούργιο που δεν έχει ο κλάδος προορισμού ήδη. Δεν υπάρχει τίποτα που θα μπορούσε να συγχωνευτεί. pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο βρίσκονται ήδη στον κλάδο προορισμού. Θα είναι μια κενή υποβολή. pulls.required_status_check_failed=Ορισμένοι απαιτούμενοι έλεγχοι δεν ήταν επιτυχείς. pulls.required_status_check_missing=Λείπουν ορισμένοι απαιτούμενοι έλεγχοι. pulls.required_status_check_administrator=Ως διαχειριστής, μπορείτε ακόμα να συγχωνεύσετε αυτό το pull request. -pulls.blocked_by_approvals=Το pull request δεν έχει ακόμα αρκετές εγκρίσεις. Δόθηκαν %d από %d εγκρίσεις. +pulls.blocked_by_approvals=Το pull request δεν έχει ακόμα λάβει τον απαραίτητο αριθμό εγκρίσεων. Δόθηκαν %d από %d εγκρίσεις. pulls.blocked_by_rejection=Αυτό το pull request έχει αλλαγές που ζητούνται από έναν επίσημο εξεταστή. pulls.blocked_by_official_review_requests=Αυτό το pull request έχει επίσημες αιτήσεις αξιολόγησης. -pulls.blocked_by_outdated_branch=Αυτό το pull request έχει αποκλειστεί επειδή είναι παρωχημένο. -pulls.blocked_by_changed_protected_files_1=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει ένα προστατευμένο αρχείο: -pulls.blocked_by_changed_protected_files_n=Αυτό το pull request έχει αποκλειστεί επειδή αλλάζει προστατευμένα αρχεία: +pulls.blocked_by_outdated_branch=Αυτό το pull request έχει αποκλειστεί, επειδή είναι παρωχημένο. +pulls.blocked_by_changed_protected_files_1=Αυτό το pull request έχει αποκλειστεί, επειδή αλλάζει ένα αρχείο που προστατεύεται: +pulls.blocked_by_changed_protected_files_n=Αυτό το pull request έχει αποκλειστεί, επειδή αλλάζει αρχεία που προστατεύονται: pulls.can_auto_merge_desc=Αυτό το Pull Request μπορεί να συγχωνευθεί αυτόματα. pulls.cannot_auto_merge_desc=Αυτό το pull request δεν μπορεί να συγχωνευθεί αυτόματα λόγω συγκρούσεων. pulls.cannot_auto_merge_helper=Χειροκίνητη Συγχώνευση για την επίλυση των συγκρούσεων. @@ -1797,7 +1821,7 @@ pulls.reject_count_1=%d αίτημα αλλαγής pulls.reject_count_n=%d αιτήματα αλλαγής pulls.waiting_count_1=%d αναμονή αξιολόγησης pulls.waiting_count_n=%d αναμονές αξιολόγησης -pulls.wrong_commit_id=Το id υποβολής πρέπει να είναι ένα id υποβολής στον κλάδο προορισμού +pulls.wrong_commit_id=Το αναγνωριστικό της υποβολής πρέπει να αντιστοιχεί σε μία υποβολή στον κλάδο προορισμού pulls.no_merge_desc=Αυτό το pull request δεν μπορεί να συγχωνευθεί επειδή όλες οι επιλογές συγχώνευσης αποθετηρίων είναι απενεργοποιημένες. pulls.no_merge_helper=Ενεργοποιήστε τις επιλογές συγχώνευσης στις ρυθμίσεις αποθετηρίου ή συγχωνεύστε το pull request χειροκίνητα. @@ -1821,9 +1845,9 @@ pulls.unrelated_histories=H Συγχώνευση Απέτυχε: Η κεφαλή pulls.merge_out_of_date=Η συγχώνευση απέτυχε: Κατά τη δημιουργία της συγχώνευσης, η βάση ενημερώθηκε. Συμβουλή: Δοκιμάστε ξανά. pulls.head_out_of_date=Η συγχώνευση απέτυχε: Κατά τη δημιουργία της συγχώνευσης, το HEAD ενημερώθηκε. Συμβουλή: Δοκιμάστε ξανά. pulls.has_merged=Αποτυχία: Το pull request έχει συγχωνευθεί, δεν είναι δυνατή η συγχώνευση ξανά ή να αλλάξει ο κλάδος προορισμού. -pulls.push_rejected=Αποτυχία ώθησης: Η ώθηση απορρίφθηκε. Ελέγξτε τα Άγκιστρα Git για αυτό το αποθετήριο. +pulls.push_rejected=Αποτυχία ώθησης: Η ώθηση απορρίφθηκε. Ελέγξτε τα Git hooks του αποθετήριο. pulls.push_rejected_summary=Μήνυμα Πλήρους Απόρριψης -pulls.push_rejected_no_message=H συγχώνευση απέτυχε: Η ώθηση απορρίφθηκε, αλλά δεν υπήρχε απομακρυσμένο μήνυμα.
    Ελέγξτε τα Git Hooks για αυτό το αποθετήριο +pulls.push_rejected_no_message=Αποτυχία ώθησης: Η ώθηση απορρίφθηκε, αλλά δεν λάβαμε κάποιο μήνυμα. Ελέγξτε τα Git hooks του αποθετηρίου pulls.open_unmerged_pull_exists=`Δεν μπορείτε να ανοίξετε εκ νέου, επειδή υπάρχει ένα εκκρεμές pull request (#%d) με πανομοιότυπες ιδιότητες.` pulls.status_checking=Μερικοί έλεγχοι εκκρεμούν pulls.status_checks_success=Όλοι οι έλεγχοι ήταν επιτυχείς @@ -1839,7 +1863,7 @@ pulls.update_branch_rebase=Ενημέρωση κλάδου με rebase pulls.update_branch_success=Η ενημέρωση του κλάδου ήταν επιτυχής pulls.update_not_allowed=Δεν επιτρέπεται να ενημερώσετε τον κλάδο pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ενημερωμένος με τον βασικό κλάδο -pulls.close=Κλείσιμο Pull Request +pulls.close=Κλείσιμο pull request pulls.closed_at=`έκλεισε αυτό το pull request %[2]s` pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %[2]s` pulls.cmd_instruction_hint=`Δείτε τις οδηγίες γραμμής εντολών.` @@ -1869,7 +1893,7 @@ pulls.recently_pushed_new_branches=Ωθήσατε στο κλάδοΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί. settings.delete_notices_2=- Αυτή η ενέργεια θα διαγράψει μόνιμα το αποθετήριο %s μαζί με τον κώδικα, τα ζητημάτα, τα σχόλια, τα δεδομένα των wiki και τις ρυθμίσεις συνεργατών που βρίσκονται μέσα σε αυτό. @@ -2163,7 +2187,7 @@ settings.delete_notices_fork_1=- Τα Forks αυτού του αποθετηρί settings.deletion_success=Το αποθετήριο έχει διαγραφεί. settings.update_settings_success=Οι ρυθμίσεις του αποθετηρίου έχουν ενημερωθεί. settings.update_settings_no_unit=Το αποθετήριο θα πρέπει να επιτρέπει τουλάχιστον κάποιο είδος αλληλεπίδρασης. -settings.confirm_delete=Διαγραφή Αποθετηρίου +settings.confirm_delete=Διαγραφή αποθετηρίου settings.add_collaborator=Προσθήκη συνεργάτη settings.add_collaborator_success=Ο συνεργάτης προστέθηκε. settings.add_collaborator_inactive_user=Δεν είναι δυνατή η προσθήκη ενός ανενεργού χρήστη ως συνεργάτη. @@ -2178,20 +2202,20 @@ settings.org_not_allowed_to_be_collaborator=Δεν μπορείτε να προ settings.change_team_access_not_allowed=Η αλλαγή της πρόσβασης ομάδας για το αποθετήριο έχει περιοριστεί στον ιδιοκτήτη του οργανισμού settings.team_not_in_organization=Η ομάδα δεν είναι στον ίδιο οργανισμό με το αποθετήριο settings.teams=Ομάδες -settings.add_team=Προσθήκη Ομάδας +settings.add_team=Προσθήκη ομάδας settings.add_team_duplicate=Η ομάδα έχει ήδη το αποθετήριο settings.add_team_success=Η ομάδα έχει πλέον πρόσβαση στο αποθετήριο. settings.search_team=Αναζήτηση Ομάδας… settings.change_team_permission_tip=Τα δικαιώματα της ομάδας έχουν οριστεί στη σελίδα ρυθμίσεων της ομάδας και δεν μπορούν να αλλάξουν ανά αποθετήριο settings.delete_team_tip=Αυτή η ομάδα έχει πρόσβαση σε όλα τα αποθετήρια και δεν μπορεί να αφαιρεθεί settings.remove_team_success=Έχει αφαιρεθεί η πρόσβαση της ομάδας στο αποθετήριο. -settings.add_webhook=Προσθήκη Webhook +settings.add_webhook=Προσθήκη webhook settings.add_webhook.invalid_channel_name=Το όνομα του καναλιού Webhook δεν μπορεί να είναι κενό και δεν μπορεί να περιέχει μόνο έναν χαρακτήρα #. settings.hooks_desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούνται ορισμένα γεγονότα στο Forgejo. Διαβάστε περισσότερα στον οδηγό webhooks. -settings.webhook_deletion=Αφαίρεση Webhook +settings.webhook_deletion=Αφαίρεση webhook settings.webhook_deletion_desc=Η αφαίρεση ενός webhook διαγράφει τις ρυθμίσεις και το ιστορικό παραδόσεων. Συνέχεια; settings.webhook_deletion_success=Το webhook έχει αφαιρεθεί. -settings.webhook.test_delivery=Δοκιμή Παράδοσης +settings.webhook.test_delivery=Δοκιμή παράδοσης settings.webhook.test_delivery_desc=Δοκιμάστε αυτό το webhook με ένα ψεύτικο συμβάν. settings.webhook.test_delivery_desc_disabled=Για να δοκιμάσετε αυτό το webhook με μια ψεύτικη κλήση, ενεργοποιήστε το. settings.webhook.request=Αίτημα @@ -2202,26 +2226,26 @@ settings.webhook.body=Σώμα settings.webhook.replay.description=Επανάληψη αυτού του webhook. settings.webhook.replay.description_disabled=Για να επαναλάβετε αυτό το webhook, ενεργοποιήστε το. settings.webhook.delivery.success=Ένα γεγονός έχει προστεθεί στην ουρά παράδοσης. Μπορεί να χρειαστούν λίγα δευτερόλεπτα μέχρι να εμφανιστεί στο ιστορικό. -settings.githooks_desc=Τα Άγκιστρα Git παρέχονται από το ίδιο το Git. Μπορείτε να επεξεργαστείτε τα αρχεία αγκίστρων παρακάτω για να ρυθμίσετε προσαρμοσμένες λειτουργίες. +settings.githooks_desc=Τα Git hook αποτελούν μία δυνατότητα του Git. Αν θέλετε να ρυθμίσετε κάποιες προσαρμοσμένες λειτουργείες, θα μπορέσετε να επεξεργαστείτε τα αρχεία ενός Git hook παρακάτω. settings.githook_edit_desc=Αν το hook είναι ανενεργό, θα παρουσιαστεί ένα παράδειγμα. Αφήνοντας το περιεχόμενο του hook κενό θα το απενεργοποιήσετε. -settings.githook_name=Όνομα Hook -settings.githook_content=Περιεχόμενο Hook -settings.update_githook=Ενημέρωση Hook -settings.add_webhook_desc=Ο Forgejo θα στείλει αιτήματα POST με συγκεκριμένο τύπο περιεχομένου στο URL προορισμού. Διαβάστε περισσότερα στον οδηγό webhooks. +settings.githook_name=Όνομα hook +settings.githook_content=Περιεχόμενο hook +settings.update_githook=Ενημέρωση hook +settings.add_webhook_desc=Το Forgejo θα στείλει αιτήματα POST με ένα συγκεκριμένο Content-Type (τύπο περιεχομένου) στο προοριζόμενο URL. Διαβάστε περισσότερα στον οδηγό webhook. settings.payload_url=URL Στόχου settings.http_method=Μέθοδος HTTP -settings.content_type=Τύπος Περιεχομένου POST +settings.content_type=Τύπος περιεχομένου POST settings.secret=Μυστικό settings.slack_username=Όνομα Χρήστη settings.slack_icon_url=URL Εικονιδίου settings.slack_color=Χρώμα settings.discord_username=Όνομα Χρήστη settings.discord_icon_url=URL Εικονιδίου -settings.event_desc=Ενεργοποίηση Σε: -settings.event_push_only=Γεγονότα Push -settings.event_send_everything=Όλα τα Γεγονότα -settings.event_choose=Προσαρμοσμένα Γεγονότα… -settings.event_header_repository=Γεγονότα Αποθετηρίου +settings.event_desc=Ενεργοποίηση σε: +settings.event_push_only=Συμβάντα push +settings.event_send_everything=Όλα τα συμβάντα +settings.event_choose=Προσαρμοσμένα συμβάντα… +settings.event_header_repository=Συμβάντα αποθετηρίου settings.event_create=Δημιουργία settings.event_create_desc=Ο κλάδος ή η ετικέτα δημιουργήθηκε. settings.event_delete=Διαγραφή @@ -2236,50 +2260,50 @@ settings.event_push=Push settings.event_push_desc=Git push σε ένα αποθετήριο. settings.event_repository=Αποθετήριο settings.event_repository_desc=Το αποθετήριο δημιουργήθηκε ή διαγράφηκε. -settings.event_header_issue=Γεγονότα Ζητήματος +settings.event_header_issue=Συμβάντα ζητημάτων settings.event_issues=Ζητήματα settings.event_issues_desc=Το ζήτημα άνοιξε, έκλεισε, ανοίχθηκε εκ νέου ή επεξεργάστηκε. -settings.event_issue_assign=Ζήτημα Ανατέθηκε +settings.event_issue_assign=Ανάθεση ζητήματος settings.event_issue_assign_desc=Ζήτημα εκχωρημένο ή μη εκχωρημένο. -settings.event_issue_label=Σήμανση Ζητήματος +settings.event_issue_label=Σήμανση ζητήματος settings.event_issue_label_desc=Τα σήματα των ζητημάτων ενημερώθηκαν ή εκκαθαρίστηκαν. -settings.event_issue_milestone=Ενεργοποιήθηκε Ορόσημο στο Ζήτημα +settings.event_issue_milestone=Ορισμός ορόσημου σε ζήτημα settings.event_issue_milestone_desc=Ενεργοποιήθηκε ή απενεργοποιήθηκε ορόσημο στο ζήτημα. -settings.event_issue_comment=Σχόλιο Ζητήματος +settings.event_issue_comment=Σχολιασμός κάποιου ζητήματος settings.event_issue_comment_desc=Το σχόλιο στο ζήτημα δημιουργήθηκε, επεξεργάστηκε ή διαγράφηκε. -settings.event_header_pull_request=Γεγονότα Pull Requests -settings.event_pull_request=Pull Request +settings.event_header_pull_request=Συμβάντα pull request +settings.event_pull_request=Pull request settings.event_pull_request_desc=Το pull request άνοιξε, έκλεισε, άνοιξε εκ νέου ή επεξεργάστηκε. -settings.event_pull_request_assign=Το Pull Request Ανατέθηκε +settings.event_pull_request_assign=Ανάθεση pull request settings.event_pull_request_assign_desc=Το pull request ανατέθηκε ή έγινε αδιάθετο. -settings.event_pull_request_label=Σήμανση Pull Request +settings.event_pull_request_label=Σήμανση pull request settings.event_pull_request_label_desc=Τα σήματα του pull request ενημερώθηκαν ή εκκαθαρίστηκαν. -settings.event_pull_request_milestone=Pull Request Με Ορόσημο +settings.event_pull_request_milestone=Ορισμός οροσήμου σε pull request settings.event_pull_request_milestone_desc=Μπήκε ή βγήκε ορόσημο στο Pull request. -settings.event_pull_request_comment=Σχόλιο Pull Requests +settings.event_pull_request_comment=Σχολιασμός pull request settings.event_pull_request_comment_desc=Το σχόλιο στο pull request δημιουργήθηκε, επεξεργάστηκε ή διαγράφηκε. -settings.event_pull_request_review=Pull Request Αξιολογήθηκε +settings.event_pull_request_review=Αξιολόγηση pull request settings.event_pull_request_review_desc=Το pull request εγκρίθηκε, απορρίφθηκε ή προστέθηκε αξιολόγηση. -settings.event_pull_request_sync=Pull Request Συγχρονίστηκε +settings.event_pull_request_sync=Συγχρονισμός pull request settings.event_pull_request_sync_desc=Το pull request συγχρονίστηκε. -settings.event_pull_request_review_request=Αίτηση Αξιολόγησης του Pull Request +settings.event_pull_request_review_request=Αίτηση αξιολόγησης pull request settings.event_pull_request_review_request_desc=Ζητήθηκε η αξιολόγηση του pull request ή η αίτηση αξιολόγησης αφαιρέθηκε. -settings.event_pull_request_approvals=Εγκρίσεις Pull Request -settings.event_pull_request_merge=Συγχώνευση Pull Request +settings.event_pull_request_approvals=Εγκρίσεις pull request +settings.event_pull_request_merge=Συγχώνευση pull request settings.event_package=Πακέτο settings.event_package_desc=Το πακέτο δημιουργήθηκε ή διαγράφηκε σε ένα αποθετήριο. settings.branch_filter=Φίλτρο κλάδου settings.branch_filter_desc=Λίστα επιτρεπόμενων κλάδων για ωθήσεις, δημιουργία κλάδων και γεγονότα διαγραφής κλάδων, που ορίζονται ως μοτίβο glob. Εάν είναι κενό ή *, αναφέρονται συμβάντα για όλους τους κλάδους. Δείτε τη τεκμηρίωσηgithub.com/gobwas/glob για σύνταξη. Παραδείγματα: master, {master,release*}. -settings.authorization_header=Κεφαλίδα Authorization +settings.authorization_header=Κεφαλίδα authorization settings.authorization_header_desc=Αν υπάρχει, θα προστίθεται ως κεφαλίδα authorization στις αιτήσεις HTTP. Παραδείγματα: %s. settings.active=Ενεργό settings.active_helper=Οι πληροφορίες σχετικά με τα γεγονότα που συμβαίνουν θα στέλνονται σε αυτό το URL webhook. settings.add_hook_success=Έχει προστεθεί το webhook. -settings.update_webhook=Ενημέρωση Webhook +settings.update_webhook=Ενημέρωση webhook settings.update_hook_success=Το webhook έχει ενημερωθεί. -settings.delete_webhook=Ενημέρωση Webhook -settings.recent_deliveries=Πρόσφατες Παραδόσεις -settings.hook_type=Τύπος Hook +settings.delete_webhook=Αφαίρεση webhook +settings.recent_deliveries=Πρόσφατες παραδόσεις +settings.hook_type=Είδος hook settings.slack_token=Διακριτικό settings.slack_domain=Domain settings.slack_channel=Κανάλι @@ -2301,10 +2325,10 @@ settings.web_hook_name_packagist=Packagist settings.packagist_username=Όνομα χρήστη Packagist settings.packagist_api_token=Διακριτικό API settings.packagist_package_url=URL πακέτων Packagist -settings.deploy_keys=Κλειδιά Διάθεσης -settings.add_deploy_key=Προσθήκη Κλειδιού Διάθεσης +settings.deploy_keys=Κλειδιά διάθεσης +settings.add_deploy_key=Προσθήκη κλειδιού διάθεσης settings.deploy_key_desc=Τα κλειδιά διάθεσης έχουν πρόσβαση μόνο-ανάγνωσης στο αποθετήριο. -settings.is_writable=Ενεργοποίηση Πρόσβασης Εγγραφής +settings.is_writable=Ενεργοποίηση πρόσβασης εγγραφής settings.is_writable_info=Επιτρέψτε σε αυτό το κλειδί διάθεσης να ωθήσει στο αποθετήριο. settings.no_deploy_keys=Δεν υπάρχουν ακόμα κλειδιά διάθεσης. settings.title=Τίτλος @@ -2312,37 +2336,37 @@ settings.deploy_key_content=Περιεχόμενο settings.key_been_used=Ένα κλειδί διάθεσης με το ίδιο περιεχόμενο χρησιμοποιείται ήδη. settings.key_name_used=Ένα κλειδί διάθεσης με το ίδιο όνομα υπάρχει ήδη. settings.add_key_success=Το κλειδί διάθεσης «%s» προστέθηκε. -settings.deploy_key_deletion=Αφαίρεση Κλειδιού Διάθεσης +settings.deploy_key_deletion=Αφαίρεση κλειδιού διάθεσης settings.deploy_key_deletion_desc=Η κατάργηση ενός κλειδί διάθεσης θα ανακαλέσει την πρόσβασή του σε αυτό το αποθετήριο. Συνέχεια; settings.deploy_key_deletion_success=Το κλειδί διάθεσης έχει αφαιρεθεί. settings.branches=Κλάδοι -settings.protected_branch=Προστασία Κλάδου -settings.protected_branch.save_rule=Αποθήκευση Κανόνα -settings.protected_branch.delete_rule=Διαγραφή Κανόνα +settings.protected_branch=Προστασία κλάδου +settings.protected_branch.save_rule=Αποθήκευση κανόνα +settings.protected_branch.delete_rule=Διαγραφή κανόνα settings.protected_branch_can_push=Επιτρέψτε ώθηση; settings.protected_branch_can_push_yes=Μπορείτε να ωθήσετε settings.protected_branch_can_push_no=Δεν μπορείτε να ωθήσετε settings.branch_protection=Κανόνες προστασίας για τον κλάδο «%s» -settings.protect_this_branch=Ενεργοποίηση Προστασίας Κλάδου +settings.protect_this_branch=Ενεργοποίηση προστασίας κλάδου settings.protect_this_branch_desc=Αποτρέπει τη διαγραφή και περιορίζει το Git push και συγχώνευση στον κλάδο. -settings.protect_disable_push=Απενεργοποίηση Ώθησης +settings.protect_disable_push=Απενεργοποίηση ωθήσεων settings.protect_disable_push_desc=Κανένα push δεν θα επιτρέπεται σε αυτόν τον κλάδο. -settings.protect_enable_push=Ενεργοποίηση Push +settings.protect_enable_push=Ενεργοποίηση ωθήσεων settings.protect_enable_push_desc=Οποιοσδήποτε έχει πρόσβαση εγγραφής θα επιτρέπεται να κάνει push σε αυτόν τον κλάδο (αλλά όχι και να κάνει force push). -settings.protect_enable_merge=Ενεργοποίηση Συγχώνευσης +settings.protect_enable_merge=Ενεργοποίηση συγχωνεύσεων settings.protect_enable_merge_desc=Οποιοσδήποτε έχει πρόσβαση εγγραφής θα επιτρέπεται να συγχωνεύσει τα pull request σε αυτόν τον κλάδο. -settings.protect_whitelist_committers=Περιορισμός του Push στη Λίστα +settings.protect_whitelist_committers=Περιορισμένος κανονισμός ώθησης settings.protect_whitelist_committers_desc=Μόνο χρήστες ή ομάδες στη λίστα θα επιτρέπεται να κάνουν push σε αυτόν τον κλάδο (αλλά όχι να κάνουν force push). settings.protect_whitelist_deploy_keys=Έγκριση κλειδιών διάθεσης με πρόσβαση εγγραφής για ώθηση. settings.protect_whitelist_users=Λίστα χρηστών που επιτρέπεται να κάνουν push: settings.protect_whitelist_search_users=Αναζήτηση χρηστών… settings.protect_whitelist_teams=Λίστα ομάδων που επιτρέπεται να κάνουν push: settings.protect_whitelist_search_teams=Αναζήτηση ομάδων… -settings.protect_merge_whitelist_committers=Ενεργοποίηση Λίστας Συγχώνευσης +settings.protect_merge_whitelist_committers=Ενεργοποίηση λίστας συγχώνευσης settings.protect_merge_whitelist_committers_desc=Επιτρέψτε μόνο σε χρήστες ή ομάδες στη λίστα να συγχωνεύσουν pull requests σε αυτό το κλάδο. settings.protect_merge_whitelist_users=Λίστα επιτρεπόμενων χρηστών για συγχώνευση: settings.protect_merge_whitelist_teams=Επιτρεπόμενες ομάδες για συγχώνευση: -settings.protect_check_status_contexts=Ενεργοποίηση Ελέγχου Κατάστασης +settings.protect_check_status_contexts=Ενεργοποίηση ελέγχου κατάστασης settings.protect_status_check_patterns=Μοτίβα ελέγχου κατάστασης: settings.protect_status_check_patterns_desc=Ορίστε μοτίβα για να καθορίσετε ποιοι έλεγχοι κατάστασης πρέπει να περάσουν πριν οι κλάδοι να μπορούν να συγχωνευτούν σε έναν κλάδο που ταιριάζει με αυτόν τον κανόνα. Κάθε γραμμή καθορίζει ένα μοτίβο. Τα μοτίβα δεν μπορούν να είναι κενά. settings.protect_check_status_contexts_desc=Απαιτείται έλεγχος κατάστασης για να περάσει το pull request πριν από τη συγχώνευση. Επιλέξτε ποιοι έλεγχοι κατάστασης πρέπει να περάσουν πριν κλάδοι μπορούν να συγχωνευτούν σε έναν κλάδο που ταιριάζει με αυτόν τον κανόνα. Όταν είναι ενεργοποιημένο, οι υποβολές πρέπει πρώτα να γίνονται push σε άλλο κλάδο, στη συνέχεια, να συγχωνεύονται ή γίνονται push απευθείας σε ένα κλάδο που ταιριάζει με αυτόν τον κανόνα, αφού έχουν ολοκληρωθεί οι έλεγχοι κατάστασης. Αν δεν επιλεχθεί κανένα πλαίσιο, η τελευταία υποβολή πρέπει να είναι επιτυχής ανεξάρτητα από το πλαίσιο. @@ -2358,21 +2382,21 @@ settings.protect_approvals_whitelist_users=Εγκεκριμένοι εξετασ settings.protect_approvals_whitelist_teams=Λίστα επιτρεπόμενων ομάδων για κριτικές: settings.dismiss_stale_approvals=Παράβλεψη καθυστερημένων εγκρίσεων settings.dismiss_stale_approvals_desc=Όταν οι νέες υποβολές που αλλάζουν το περιεχόμενο του pull request γίνονται push στο κλάδο, οι παλιές εγκρίσεις απορρίπτονται. -settings.require_signed_commits=Απαιτούνται Υπογεγραμμένες Υποβολές +settings.require_signed_commits=Να απαιτούνται υπογεγραμμένες υποβολές settings.require_signed_commits_desc=Απόρριψη νέων υποβολών σε αυτόν τον κλάδο εάν είναι μη υπογεγραμμένες ή μη επαληθεύσιμες. -settings.protect_branch_name_pattern=Μοτίβο Προστατευμένου Ονόματος Κλάδου -settings.protect_branch_name_pattern_desc=Μοτίβα ονόματος προστατευμένων κλάδων. Δείτε την τεκμηρίωση για σύνταξη μοτίβου. Παραδείγματα: main, release/** +settings.protect_branch_name_pattern=Μοτίβο προστατευμένου ονόματος κλάδου +settings.protect_branch_name_pattern_desc=Μοτίβα ονόματος προστατευμένων κλάδων. Συμβολευτείτε την τεκμηρίωση για την σύνταξη ενός μοτίβου. Παραδείγματα: main, release/** settings.protect_patterns=Μοτίβα -settings.protect_protected_file_patterns=Μοτίβα προστατευμένων αρχείων (διαχωρισμένα με ερωτηματικό ';'): +settings.protect_protected_file_patterns=Μοτίβα προστατευμένων αρχείων (διαχωρισμός με semicolon «;» και ΟΧΙ το ελληνικό ερωτηματικό): settings.protect_protected_file_patterns_desc=Τα προστατευόμενα αρχεία δεν επιτρέπεται να αλλάξουν άμεσα, ακόμη και αν ο χρήστης έχει δικαιώματα να προσθέσει, να επεξεργαστεί ή να διαγράψει αρχεία σε αυτόν τον κλάδο. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση github.com/gobwas/glob για τη σύνταξη του μοτίβου. Πχ: .drone.yml, /docs/**/*.txt. -settings.protect_unprotected_file_patterns=Μοτίβα μη προστατευμένων αρχείων (διαχωρισμένα με ερωτηματικό ';'): +settings.protect_unprotected_file_patterns=Μοτίβα μη προστατευμένων αρχείων (διαχωρισμένα με semicolon «;» και ΟΧΙ το ελληνικό ερωτηματικό): settings.protect_unprotected_file_patterns_desc=Μη προστατευμένα αρχεία που επιτρέπεται να αλλάξουν απευθείας εάν ο χρήστης έχει πρόσβαση εγγραφής, παρακάμπτοντας τον περιορισμό ώθησης. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση github.com/gobwas/glob για τη σύνταξη του μοτίβου. Πχ: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Ενεργοποίηση προστασίας settings.delete_protected_branch=Απενεργοποίηση προστασίας settings.update_protect_branch_success=Η προστασία κλάδου για τον κανόνα «%s» ενημερώθηκε. settings.remove_protected_branch_success=Η προστασία κλάδου για τον κανόνα "%s" αφαιρέθηκε. settings.remove_protected_branch_failed=Η αφαίρεση του κανόνα προστασίας κλάδου «%s» απέτυχε. -settings.protected_branch_deletion=Απενεργοποίηση Προστασίας Κλάδου +settings.protected_branch_deletion=Απενεργοποίηση προστασίας κλάδου settings.protected_branch_deletion_desc=Η απενεργοποίηση της προστασίας του κλάδου επιτρέπει στους χρήστες με άδεια εγγραφής να κάνουν push στον κλάδο. Συνέχεια; settings.block_rejected_reviews=Φραγή συγχώνευσης αν υπάρχουν απορριπτικές αξιολογήσεις settings.block_rejected_reviews_desc=Η συγχώνευση δεν θα είναι δυνατή όταν οι αλλαγές ζητούνται από τους επίσημους εξεταστές, ακόμη και αν υπάρχουν αρκετές εγκρίσεις. @@ -2381,7 +2405,7 @@ settings.block_on_official_review_requests_desc=Η συγχώνευση δεν settings.block_outdated_branch=Φραγή συγχώνευσης αν το pull request είναι ξεπερασμένο settings.block_outdated_branch_desc=Η συγχώνευση δεν θα είναι δυνατή όταν ο κλάδος κεφαλής είναι πίσω από τον βασικό κλάδο. settings.default_branch_desc=Επιλέξτε έναν προεπιλεγμένο κλάδο αποθετηρίου για pull requests και υποβολές κώδικα: -settings.merge_style_desc=Συγχώνευση Στυλ +settings.merge_style_desc=Στυλ συγχώνευσης settings.default_merge_style_desc=Προεπιλεγμένο στυλ συγχώνευσης settings.choose_branch=Επιλέξτε έναν κλάδο… settings.no_protected_branch=Δεν υπάρχουν προστατευμένοι κλάδοι. @@ -2390,22 +2414,22 @@ settings.protected_branch_required_rule_name=Απαιτούμενο όνομα settings.protected_branch_duplicate_rule_name=Υπάρχει ήδη ένας κανόνας για αυτούς τους κλάδους settings.protected_branch_required_approvals_min=Οι απαιτούμενες εγκρίσεις δεν μπορούν να είναι αρνητικές. settings.tags=Ετικέτες -settings.tags.protection=Προστασία Ετικετών -settings.tags.protection.pattern=Μοτίβο Ετικέτας +settings.tags.protection=Προστασία ετικετών +settings.tags.protection.pattern=Μοτίβο ετικετών settings.tags.protection.allowed=Επιτρέπεται settings.tags.protection.allowed.users=Επιτρεπόμενοι χρήστες settings.tags.protection.allowed.teams=Επιτρεπόμενες ομάδες settings.tags.protection.allowed.noone=Καμία -settings.tags.protection.create=Προστασία Ετικέτας +settings.tags.protection.create=Προστασία ετικέτας settings.tags.protection.none=Δεν υπάρχουν προστατευμένες ετικέτες. -settings.tags.protection.pattern.description=Μπορείτε να χρησιμοποιήσετε ένα μόνο όνομα ή ένα μοτίβο τύπου glob ή κανονική έκφραση για να ταιριάξετε πολλαπλές ετικέτες. Διαβάστε περισσότερα στον οδηγό προστατευμένων ετικετών. -settings.bot_token=Διακριτικό Bot +settings.tags.protection.pattern.description=Μπορείτε να χρησιμοποιήσετε ένα μόνο όνομα ή ένα μοτίβο τύπου glob ή κανονική έκφραση για να ταιριάξετε πολλαπλές ετικέτες. Διαβάστε περισσότερα στον οδηγό προστατευμένων ετικετών. +settings.bot_token=Διακριτικό bot settings.chat_id=ID Συνομιλίας settings.thread_id=ID Νήματος settings.matrix.homeserver_url=Homeserver URL settings.matrix.room_id=ID Δωματίου -settings.matrix.message_type=Τύπος Μηνύματος -settings.archive.button=Αρχειοθέτηση Αποθετηρίου +settings.matrix.message_type=Είδος μηνύματος +settings.archive.button=Αρχειοθέτηση αποθετηρίου settings.archive.header=Αρχειοθέτηση αποθετηρίου settings.archive.text=Η αρχειοθέτηση του αποθετηρίου θα το αλλάξει σε μόνο για ανάγνωση. Δε θα φαίνεται στον αρχικό πίνακα. Κανείς (ακόμα και εσείς!) δε θα μπορεί να κάνει νέες υποβολές, ή να ανοίξει ζητήματα ή pull request. settings.archive.success=Το αποθετήριο αρχειοθετήθηκε με επιτυχία. @@ -2426,7 +2450,7 @@ settings.lfs_findcommits=Εύρεση υποβολών settings.lfs_lfs_file_no_commits=Δεν βρέθηκαν υποβολές για αυτό το αρχείο LFS settings.lfs_noattribute=Αυτή η διαδρομή δεν έχει λειτουργία κλειδώματος στον προεπιλεγμένο κλάδο settings.lfs_delete=Διαγραφή αρχείου LFS με το OID %s -settings.lfs_delete_warning=Η διαγραφή ενός αρχείου LFS μπορεί να προκαλέσει σφάλματα 'object does not exist' κατά την ολοκλήρωση του checkout. Σίγουρα; +settings.lfs_delete_warning=Η διαγραφή ενός αρχείου LFS μπορεί να προκαλέσει σφάλματα «object does not exist» κατά την ολοκλήρωση του checkout. Είστε βέβαιοι; settings.lfs_findpointerfiles=Εύρεση αρχείων δείκτη settings.lfs_locks=Κλειδώματα settings.lfs_invalid_locking_path=Μη έγκυρη διαδρομή: %s @@ -2451,17 +2475,17 @@ settings.rename_branch_from=παλιό όνομα κλάδου settings.rename_branch_to=νέο όνομα κλάδου settings.rename_branch=Μετονομασία κλάδου -diff.browse_source=Περιήγηση Στο Πηγαίο +diff.browse_source=Προβολή πηγαίου κώδικα diff.parent=γονέας diff.commit=υποβολή diff.git-notes=Σημειώσεις -diff.data_not_available=Δεν Υπάρχει Διαθέσιμο Περιεχόμενο Diff -diff.options_button=Επιλογές Diff -diff.show_diff_stats=Εμφάνιση Στατιστικών -diff.download_patch=Λήψη Αρχείου Patch -diff.download_diff=Λήψη Αρχείου Diff -diff.show_split_view=Διαιρεμένη Προβολή -diff.show_unified_view=Ενοποιημένη Προβολή +diff.data_not_available=Δεν υπάρχει διαθέσιμο περιεχόμενο diff +diff.options_button=Επιλογές diff +diff.show_diff_stats=Εμφάνιση στατιστικών +diff.download_patch=Λήψη αρχείου patch +diff.download_diff=Λήψη αρχείου diff +diff.show_split_view=Διαιρεμένη προβολή +diff.show_unified_view=Ενοποιημένη προβολή diff.whitespace_button=Κενοί Χαρακτήρες diff.whitespace_show_everything=Εμφάνιση όλων diff.whitespace_ignore_all_whitespace=Παράβλεψη κενών κατά τη σύγκριση γραμμών @@ -2471,7 +2495,7 @@ diff.stats_desc= %d αρχεία άλλαξαν με %d diff.stats_desc_file=%d αλλαγές: %d προσθήκες και %d διαγραφές diff.bin=BIN diff.bin_not_shown=Το δυαδικό αρχείο δεν εμφανίζεται. -diff.view_file=Προβολή Αρχείου +diff.view_file=Προβολή αρχείου diff.file_before=Πριν diff.file_after=Μετά diff.file_image_width=Πλάτος @@ -2480,8 +2504,8 @@ diff.file_byte_size=Μέγεθος diff.file_suppressed=Το diff αρχείου καταστέλλεται επειδή είναι πολύ μεγάλο diff.file_suppressed_line_too_long=Το diff αρχείων καταστέλλεται επειδή μία ή περισσότερες γραμμές είναι πολύ μεγάλες diff.too_many_files=Ορισμένα αρχεία δεν εμφανίστηκαν επειδή έχουν αλλάξει πάρα πολλά αρχεία σε αυτή τη διαφορά -diff.show_more=Εμφάνιση Περισσότερων -diff.load=Φόρτωση Διαφορών +diff.show_more=Εμφάνιση περισσότερων +diff.load=Φόρτωση διαφορών diff.generated=δημιουργημένο diff.vendored=εξωτερικό diff.comment.add_line_comment=Προσθήκη σχολίου στη γραμμή @@ -2501,7 +2525,7 @@ diff.review.reject=Αίτηση αλλαγών diff.review.self_approve=Οι συγγραφείς pull request δεν μπορούν να εγκρίνουν τα δικά τους pull request diff.committed_by=υποβλήθηκε από diff.protected=Προστατευμένο -diff.image.side_by_side=Δίπλα Δίπλα +diff.image.side_by_side=Δίπλα-δίπλα diff.image.swipe=Σύρσιμο diff.image.overlay=Επικάλυψη diff.has_escaped=Αυτή η γραμμή έχει κρυφούς χαρακτήρες Unicode @@ -2556,11 +2580,11 @@ release.add_tag=Δημιουργία Ετικέτας Μόνο release.releases_for=Κυκλοφορίες για %s release.tags_for=Ετικέτες για %s -branch.name=Όνομα Κλάδου +branch.name=Όνομα κλάδου branch.already_exists=Ήδη υπάρχει ένας κλάδος με το όνομα «%s». branch.delete_head=Διαγραφή -branch.delete=`Διαγραφή του Κλάδου «%s»` -branch.delete_html=Διαγραφή Κλάδου +branch.delete=Διαγραφή κλάδου «%s» +branch.delete_html=Διαγραφή κλάδου branch.delete_desc=Η διαγραφή ενός κλάδου είναι μόνιμη. Αν και ο διαγραμμένος κλάδος μπορεί να συνεχίσει να υπάρχει για σύντομο χρονικό διάστημα πριν να αφαιρεθεί, ΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί στις περισσότερες περιπτώσεις. Συνέχεια; branch.deletion_success=Ο κλάδος «%s» διαγράφηκε. branch.deletion_failed=Η διαγραφή του κλάδου «%s» απέτυχε. @@ -2576,9 +2600,9 @@ branch.restore_success=Ο κλάδος «%s» επαναφέρθηκε. branch.restore_failed=Αποτυχία επαναφοράς του κλάδου «%s». branch.protected_deletion_failed=Ο κλάδος «%s» είναι προστατευόμενος. Δεν μπορεί να διαγραφεί. branch.default_deletion_failed=Ο κλάδος «%s» είναι προεπιλεγμένος κλάδος. Δεν μπορεί να διαγραφεί. -branch.restore=`Επαναφορά του κλάδου «%s»` -branch.download=`Λήψη του κλάδου «%s»` -branch.rename=`Μετονομασία κλάδου «%s»` +branch.restore=Επαναφορά κλάδου «%s» +branch.download=Λήψη κλάδου «%s» +branch.rename=Μετονομασία κλάδου «%s» branch.search=Αναζήτηση Κλάδου branch.included_desc=Αυτός ο κλάδος είναι μέρος του προεπιλεγμένου κλάδου branch.included=Περιλαμβάνεται @@ -2602,7 +2626,7 @@ tag.create_success=Η ετικέτα «%s» δημιουργήθηκε. topic.manage_topics=Διαχείριση Θεμάτων topic.done=Ολοκληρώθηκε topic.count_prompt=Δεν μπορείτε να επιλέξετε περισσότερα από 25 θέματα -topic.format_prompt=Τα θέματα πρέπει να ξεκινούν με γράμμα ή αριθμό, μπορούν να περιλαμβάνουν παύλες ('-') και τελείες ('.'), μπορεί να είναι μέχρι 35 χαρακτήρες. Τα γράμματα πρέπει να είναι πεζά. +topic.format_prompt=Τα θέματα πρέπει να ξεκινούν με κάποιο γράμμα ή αριθμό, μπορούν να περιλαμβάνουν παύλες ('-') και τελείες ('.') και έχουν ένα μέγιστο όριο 35 χαρακτήρων. Όλα τα γράμματα θα πρέπει να είναι πεζά. find_file.go_to_file=Αναζήτηση αρχείου find_file.no_matching=Δεν ταιριάζει κανένα αρχείο @@ -2616,7 +2640,7 @@ issues.comment.blocked_by_user = Δεν μπορείτε να αφήσετε σ pulls.blocked_by_user = Δεν μπορείτε να δημιουργήσετε pull request σε αυτό το αποθετήριο, επειδή ο κάτοχος του αποθετηρίου σας έχει αποκλείσει. pulls.made_using_agit = AGit wiki.cancel = Ακύρωση -settings.units.add_more = Προσθήκη περισσότερων... +settings.units.add_more = Προσθήκη μονάδων... settings.new_owner_blocked_doer = Ο νέος κάτοχος του αποθετηρίου σας έχει αποκλείσει. settings.enter_repo_name = Γράψτε το όνομα του κατόχου και του αποθετηρίου ακριβώς όπως το βλέπετε: settings.confirmation_string = Κείμενο επιβεβαίωσης @@ -2631,7 +2655,7 @@ migrate.forgejo.description = Μεταφορά δεδομένων από το co rss.must_be_on_branch = Για να αποκτήσετε ένα RSS feed, πρέπει να βρίσκεστε σε έναν κλάδο. clone_in_vscodium = Κλωνοποίηση στο VSCodium editor.invalid_commit_mail = Αυτή η διεύθυνση email δεν είναι έγκυρη για την δημιουργία μίας υποβολής. -pulls.nothing_to_compare_have_tag = Ο επιλεγμένος κλάδος/tag είναι παρόμοιος. +pulls.nothing_to_compare_have_tag = Ο επιλεγμένος κλάδος/tag είναι όμοιος. issues.blocked_by_user = Δεν μπορείτε να δημιουργήσετε ζητήματα σε αυτό το αποθετήριο, επειδή ο κάτοχος του αποθετηρίου σας έχει αποκλείσει. pulls.agit_explanation = Δημιουργημένο μέσω του AGit. Το AGit επιτρέπει σε συνεισφέροντες να προτείνουν αλλαγές χρησιμοποιώντας την εντολή «git push», χωρίς την δημιουργία fork ή έναν νέο κλάδο. activity.navbar.recent_commits = Πρόσφατες υποβολές @@ -2657,8 +2681,47 @@ pulls.reopen_failed.base_branch = Δεν είναι δυνατό το επανά activity.navbar.pulse = Παλμός error.broken_git_hook = Τα Git hook του αποθετηρίου δεν φαίνονται να λειτουργούν. Παρακαλώ ακολουθήστε τον οδηγό για να τα διορθώσετε, και μετά ωθήστε μερικές υποβολές (commits) για να γίνει επανέλεγχος. settings.add_collaborator_blocked_them = Δεν είναι δυνατή η προσθήκη του χρήστη ως συνεργάτη, καθώς έχει αποκλείσει τον κάτοχο του αποθετηρίου. -settings.wiki_rename_branch_main_notices_2 = Αυτό θα αλλάξει το όνομα του κλάδου που χρησιμοποιείται εσωτερικά για το wiki του αποθετηρίου %s. Αν έχετε ένα τοπικό αντίγραφο του αποθετηρίου, θα χρειαστεί να αλλάξετε τα checkout του. +settings.wiki_rename_branch_main_notices_2 = Αυτό θα αλλάξει το όνομα του κλάδου που χρησιμοποιείται «στο υπόβαθρο» για το wiki του αποθετηρίου %s. Αν έχετε ένα τοπικό αντίγραφο του αποθετηρίου, θα χρειαστεί να αλλάξετε τα checkout του. settings.add_collaborator_blocked_our = Δεν είναι δυνατή η προσθήκη του χρήστη ως συνεργάτη, καθώς ο κάτοχος του αποθετηρίου τον έχει αποκλείσει. +n_branch_few = %s κλάδοι +n_tag_one = %s ετικέτα +n_tag_few = %s ετικέτες +n_commit_one = %s υποβολή +stars = Αστέρια +n_branch_one = %s κλάδος +commits.search_branch = Αυτός ο κλάδος +pulls.title_desc_one = : θα ήθελε να συγχωνεύσει %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s +pulls.merged_title_desc_one = : συγχώνευσε %[1]d υποβολή από τον κλάδο %[2]s στον κλάδο %[3]s %[4]s +n_commit_few = %s υποβολές +settings.sourcehut_builds.secrets = Μυστικά +settings.add_webhook.invalid_path = Η τοποθεσία του αρχείου δεν μπορεί να περιέχει κενά, «.» ή «..». Δεν μπορεί να αρχίζει ή να τελειώνει με μία κάθετο. +commits.browse_further = Περιήγηση περισσοτέρων +issues.num_participants_one = %d συμμετέχων +settings.sourcehut_builds.graphql_url = Υπερσύνδεσμος GraphQL (π.χ. https://builds.sr.ht/query) +settings.sourcehut_builds.manifest_path = Τοποθεσία build manifest +size_format = %[1]s: %[2]s, %[3]s: %[4]s +issues.archived_label_description = (Αρχειοθετημένη) %s +vendored = Εξωτερικό +open_with_editor = Άνοιγμα με %s +release.download_count_one = %s λήψη +release.download_count_few = %s λήψεις +release.system_generated = Αυτό το αρχείο παράγεται αυτόματα. +pulls.ready_for_review = Έτοιμο για αξιολόγηση; +settings.rename_branch_failed_protected = Δεν είναι δυνατή η μετονομασία του κλάδου %s, επειδή είναι προστατευόμενος. +settings.event_pull_request_enforcement = Εξαναγκασμός +editor.commit_id_not_matching = Το αναγνωριστικό της υποβολής δεν ταιριάζει με την υποβολή που επεξεργαστήκατε. Θα πρέπει να υποβάλετε τις αλλαγές σας σε έναν νέο κλάδο και μετά να τις συγχωνεύσετε. +settings.sourcehut_builds.visibility = Ορατότητα εργασιών +object_format = Μορφή αντικειμένων («object format») +settings.ignore_stale_approvals_desc = Οι εγκρίσεις, οι οποίες αναφέρονται σε παλαιότερες υποβολές, δεν θα προσμετρούνται στο σύνολο των απαιτούμενων εγκρίσεων του pull request. Εφόσον αυτές οι εγκρίσεις έχουν ήδη ανακληθεί, τότε αυτή η ρύθμιση δεν θα παίξει κάποιον ρόλο. +settings.archive.mirrors_unavailable = Οι λειτουργίες ειδώλου δεν είναι διαθέσιμες εφόσον το αποθετήριο έχει αρχειοθετηθεί. +settings.web_hook_name_sourcehut_builds = SourceHut Builds +settings.enforce_on_admins = Εφαρμογή κανόνα σε διαχειριστές του αποθετηρίου +object_format_helper = Η μορφή των αντικειμένων του αποθετηρίου. Δεν θα μπορείτε να το αλλάξετε μεταγενέστερα. Η πιο συμβατή μορφή είναι η SHA1. +settings.enforce_on_admins_desc = Οι διαχειριστές του αποθετηρίου δεν μπορούν να προσπελάσουν τον κανόνα. +file_follow = Ακολούθηση συνδέσμου (symlink) +settings.sourcehut_builds.secrets_helper = Παραχώρηση πρόσβασης του job στα μυστικά ενός build (απαιτείται η άδεια SECRETS:RO) +generated = Παραγμένο +editor.push_out_of_date = Η αλλαγή φαίνεται να είναι παρωχημένη. [graphs] component_loading_failed = Δεν ήταν δυνατή η φόρτωση του %s @@ -2670,26 +2733,26 @@ recent_commits.what = πρόσφατες υποβολές code_frequency.what = συχνότητα κώδικα [org] -org_name_holder=Όνομα Οργανισμού -org_full_name_holder=Πλήρες Όνομα Οργανισμού +org_name_holder=Όνομα οργανισμού +org_full_name_holder=Πλήρες όνομα οργανισμού org_name_helper=Τα ονόματα οργανισμών θα πρέπει να είναι σύντομα και εύκολα στην απομνημόνευση. -create_org=Δημιουργία Οργανισμού +create_org=Δημιουργία οργανισμού repo_updated=Ενημερώθηκε members=Μέλη teams=Ομάδες code=Κώδικας lower_members=μέλη lower_repositories=αποθετήρια -create_new_team=Νέα Ομάδα -create_team=Δημιουργία Ομάδας +create_new_team=Νέα ομάδα +create_team=Δημιουργία ομάδας org_desc=Περιγραφή -team_name=Όνομα Ομάδας +team_name=Όνομα ομάδας team_desc=Περιγραφή team_name_helper=Τα Ονόματα ομάδων θα πρέπει να είναι σύντομα και εύκολα. team_desc_helper=Περιγράψτε τον σκοπό ή το ρόλο της ομάδας. team_access_desc=Πρόσβαση αποθετηρίου team_permission_desc=Δικαίωμα -team_unit_desc=Να επιτρέπεται η Πρόσβαση σε Τμήματα του Αποθετηρίου +team_unit_desc=Να επιτρέπεται η πρόσβαση σε τμήματα του αποθετηρίου team_unit_disabled=(Απενεργοποιημένο) form.name_reserved=Το όνομα οργανισμού «%s» είναι δεσμευμένο. @@ -2698,40 +2761,40 @@ form.create_org_not_allowed=Δεν επιτρέπεται να δημιουργ settings=Ρυθμίσεις settings.options=Οργανισμός -settings.full_name=Πλήρες Όνομα -settings.email=Email Επικοινωνίας +settings.full_name=Πλήρες όνομα +settings.email=Email επικοινωνίας settings.website=Ιστοσελίδα settings.location=Τοποθεσία settings.permission=Δικαιώματα settings.repoadminchangeteam=Ο διαχειριστής αποθετηρίου μπορεί να προσθέτει και να καταργεί πρόσβαση σε ομάδες settings.visibility=Ορατότητα settings.visibility.public=Δημόσιος -settings.visibility.limited=Περιορισμένο (ορατό μόνο σε ταυτοποιημένους χρήστες) +settings.visibility.limited=Περιορισμένη (ορατό μόνο σε ταυτοποιημένους χρήστες) settings.visibility.limited_shortname=Περιορισμένος -settings.visibility.private=Ιδιωτικός (ορατός μόνο στα μέλη του οργανισμού) +settings.visibility.private=Ιδιωτική (ορατός μόνο στα μέλη του οργανισμού) settings.visibility.private_shortname=Ιδιωτικός -settings.update_settings=Ενημέρωση Ρυθμίσεων +settings.update_settings=Ενημέρωση ρυθμίσεων settings.update_setting_success=Οι ρυθμίσεις του οργανισμού έχουν ενημερωθεί. settings.change_orgname_prompt=Σημείωση: Η αλλαγή του ονόματος του οργανισμού θα αλλάξει επίσης τη διεύθυνση URL του οργανισμού σας και θα απελευθερώσει το παλιό όνομα. settings.change_orgname_redirect_prompt=Το παλιό όνομα θα ανακατευθύνει μέχρι να διεκδικηθεί. settings.update_avatar_success=Η εικόνα του οργανισμού έχει ενημερωθεί. -settings.delete=Διαγραφή Οργανισμού -settings.delete_account=Διαγραφή Αυτού Του Οργανισμού +settings.delete=Διαγραφή οργανισμού +settings.delete_account=Διαγραφή αυτού του οργανισμού settings.delete_prompt=Ο οργανισμός θα αφαιρεθεί οριστικά. Αυτό το ΔΕΝ ΜΠΟΡΕΙ να αναιρεθεί! -settings.confirm_delete_account=Επιβεβαίωση Διαγραφής -settings.delete_org_title=Διαγραφή Οργανισμού +settings.confirm_delete_account=Επιβεβαίωση διαγραφής +settings.delete_org_title=Διαγραφή οργανισμού settings.delete_org_desc=Αυτός ο οργανισμός θα διαγραφεί οριστικά. Συνέχεια; settings.hooks_desc=Προσθήκη webhooks που θα ενεργοποιούνται για όλα τα αποθετήρια κάτω από αυτό τον οργανισμό. settings.labels_desc=Προσθήκη σημάτων που μπορούν να χρησιμοποιηθούν σε ζητήματα για όλα τα αποθετήρια κάτω από αυτό τον οργανισμό. -members.membership_visibility=Ορατότητα Μέλους: +members.membership_visibility=Ορατότητα μέλους: members.public=Ορατό members.public_helper=κάνε το κρυφό members.private=Κρυφό members.private_helper=κάνε ορατό -members.member_role=Ρόλος Μέλους: +members.member_role=Ρόλος μέλους: members.owner=Ιδιοκτήτης members.member=Μέλος members.remove=Αφαίρεση @@ -2739,40 +2802,40 @@ members.remove.detail=Αφαιρέστε το %[1]s από %[2]s; members.leave=Αποχώρηση members.leave.detail=Αποχώρηση από %s; members.invite_desc=Προσθέστε ένα νέο μέλος στο %s: -members.invite_now=Πρόσκληση Τώρα +members.invite_now=Αποστολή πρόσκλησης teams.join=Συμμετοχή teams.leave=Αποχώρηση teams.leave.detail=Αποχώρηση από την ομάδα %s; teams.can_create_org_repo=Δημιουργία αποθετηρίων teams.can_create_org_repo_helper=Τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό. Ο δημιουργός θα αποκτήσει πρόσβαση διαχειριστή στο νέο αποθετήριο. -teams.none_access=Καμία Πρόσβαση +teams.none_access=Καμία πρόσβαση teams.none_access_helper=Τα μέλη δεν μπορούν να δουν ή να κάνουν οποιαδήποτε άλλη ενέργεια σε αυτή τη μονάδα. -teams.general_access=Γενική Πρόσβαση +teams.general_access=Γενική πρόσβαση teams.general_access_helper=Τα δικαιώματα των μελών αποφασίζονται από το παρακάτω πίνακα αδειών. teams.read_access=Ανάγνωση teams.read_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. teams.write_access=Εγγραφή teams.write_access_helper=Τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. -teams.admin_access=Πρόσβαση Διαχειριστή +teams.admin_access=Πρόσβαση διαχειριστή teams.admin_access_helper=Τα μέλη μπορούν να κάνουν push και pull στα αποθετήρια της ομάδας, καθώς και να προσθέσουν συνεργάτες σε αυτά. teams.no_desc=Η ομάδα δεν έχει περιγραφή teams.settings=Ρυθμίσεις teams.owners_permission_desc=Οι ιδιοκτήτες έχουν πλήρη πρόσβαση σε όλα τα αποθετήρια και έχουν πρόσβαση διαχειριστή στον οργανισμό. -teams.members=Μέλη Ομάδας -teams.update_settings=Ενημέρωση Ρυθμίσεων -teams.delete_team=Διαγραφή Ομάδας -teams.add_team_member=Προσθήκη Μέλους Ομάδας +teams.members=Μέλη ομάδας +teams.update_settings=Ενημέρωση ρυθμίσεων +teams.delete_team=Διαγραφή ομάδας +teams.add_team_member=Προσθήκη μέλους ομάδας teams.invite_team_member=Πρόσκληση στην ομάδα %s -teams.invite_team_member.list=Εκκρεμείς Προσκλήσεις -teams.delete_team_title=Διαγραφή Ομάδας +teams.invite_team_member.list=Εκκρεμείς προσκλήσεις +teams.delete_team_title=Διαγραφή ομάδας teams.delete_team_desc=Η διαγραφή μιας ομάδας ανακαλεί τη πρόσβαση στο αποθετήριο από τα μέλη της. Συνέχεια; teams.delete_team_success=Η ομάδα έχει διαγραφεί. teams.read_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Ανάγνωσης: τα μέλη μπορούν να δουν και να κλωνοποιήσουν τα αποθετήρια της ομάδας. teams.write_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Εγγραφής: τα μέλη μπορούν να διαβάσουν και να κάνουν push στα αποθετήρια της ομάδας. teams.admin_permission_desc=Αυτή η ομάδα κατέχει δικαιώματα διαχειριστή: Τα μέλη της μπορούν να δουν αποθετήρια, να κάνουν push σε αυτά καθώς και να προσθέσουν συνεργάτες. teams.create_repo_permission_desc=Επιπλέον, αυτή η ομάδα χορηγεί άδεια Δημιουργία αποθετηρίου: τα μέλη μπορούν να δημιουργήσουν νέα αποθετήρια στον οργανισμό. -teams.repositories=Αποθετήρια Ομάδας +teams.repositories=Αποθετήρια ομάδας teams.search_repo_placeholder=Αναζήτηση αποθετηρίου… teams.remove_all_repos_title=Αφαίρεση όλων των αποθετηρίων της ομάδας teams.remove_all_repos_desc=Αυτό θα αφαιρέσει όλα τα αποθετήρια από την ομάδα. @@ -2796,22 +2859,22 @@ follow_blocked_user = Δεν μπορείτε να ακολουθήσετε αυ [admin] dashboard=Πίνακας Ελέγχου -identity_access=Ταυτότητα & Πρόσβαση -users=Λογαριασμοί Χρήστη +identity_access=Ταυτότητα & πρόσβαση +users=Λογαριασμοί χρήστη organizations=Οργανισμοί -assets=Στοιχεία Κώδικα +assets=Στοιχεία κώδικα repositories=Αποθετήρια hooks=Webhooks integrations=Ενσωματώσεις -authentication=Πηγές Ταυτοποίησης -emails=Email Χρήστη +authentication=Πηγές ταυτοποίησης +emails=Email χρήστη config=Διαμόρφωση -notices=Ειδοποιήσεις Συστήματος +notices=Ειδοποιήσεις συστήματος monitor=Παρακολούθηση first_page=Πρώτο last_page=Τελευταίο total=Σύνολο: %d -settings=Ρυθμίσεις Διαχειριστή +settings=Ρυθμίσεις διαχειριστή dashboard.new_version_hint=Το Forgejo %s είναι διαθέσιμο, χρησιμοποιείτε το %s. Ανατρέξτε στο blog για περισσότερες λεπτομέρειες. dashboard.statistic=Περίληψη @@ -2831,7 +2894,7 @@ dashboard.task.unknown=Άγνωστη εργασία: %[1]s dashboard.cron.started=Εκκίνηση Προγραμματισμένης Εργασίας: %[1]s dashboard.cron.process=Προγραμματισμένη Εργασία: %[1]s dashboard.cron.cancelled=Προγραμματισμένη εργασία: %[1]s ακυρώθηκε: %[3]s -dashboard.cron.error=Σφάλμα στη Προγραμματισμένη Εργασία: %s: %[3]s +dashboard.cron.error=Σφάλμα στη προγραμματισμένη εργασία: %s: %[3]s dashboard.cron.finished=Προγραμματισμένη Εργασία: %[1]s τελείωσε dashboard.delete_inactive_accounts=Διαγραφή όλων των μη ενεργοποιημένων λογαριασμών dashboard.delete_inactive_accounts.started=Η διαγραφή όλων των μη ενεργοποιημένων λογαριασμών ξεκίνησε. @@ -2839,9 +2902,9 @@ dashboard.delete_repo_archives=Διαγραφή όλων των αρχείων dashboard.delete_repo_archives.started=Η διαγραφή όλων των αρχείων λήψης του αποθετηρίου ξεκίνησε. dashboard.delete_missing_repos=Διαγραφή όλων των αποθετηρίων που δεν έχουν τα αρχεία Git τους dashboard.delete_missing_repos.started=Η διαγραφή όλων των αποθετηρίων που δεν έχουν αρχεία Git τους, ξεκίνησε. -dashboard.delete_generated_repository_avatars=Διαγραφή δημιουργημένων εικόνων αποθετηρίων -dashboard.sync_repo_branches=Συγχρονισμός κλάδων που λείπουν, από τα δεδομένα git στις βάσεις δεδομένων -dashboard.update_mirrors=Ενημέρωση Ειδώλων +dashboard.delete_generated_repository_avatars=Διαγραφή παραγμένων εικόνων αποθετηρίων +dashboard.sync_repo_branches=Συγχρονισμός κλάδων που λείπουν, χρησιμοποιώντας τα δεδομένα git στην βάση δεδομένων +dashboard.update_mirrors=Ενημέρωση ειδώλων dashboard.repo_health_check=Έλεγχος υγείας σε όλα τα αποθετήρια dashboard.check_repo_stats=Έλεγχος όλων των στατιστικών αποθετηρίων dashboard.archive_cleanup=Διαγραφή παλαιών αρχείων λήψης αποθετηρίων @@ -2880,7 +2943,7 @@ dashboard.profiling_bucket_hash_table_obtained=Profiling Bucket Hash Table πο dashboard.gc_metadata_obtained=Μεταδεδομένα GC που έχουν ληφθεί dashboard.other_system_allocation_obtained=Άλλες αναθέσεις συστήματος που έχουν ληφθεί dashboard.next_gc_recycle=Επόμενη ανακύκλωση GC -dashboard.last_gc_time=Από Την Τελευταία Φορά GC +dashboard.last_gc_time=Χρόνος από την τελευταία φορά που έγινε GC dashboard.total_gc_time=Σύνολο Παύσης GC dashboard.total_gc_pause=Σύνολο παύσης GC dashboard.last_gc_pause=Τελευταία παύση GC @@ -2900,7 +2963,7 @@ dashboard.rebuild_issue_indexer=Αναδόμηση ευρετηρίου ζητη users.user_manage_panel=Διαχείριση λογαριασμών χρηστών users.new_account=Δημιουργία Λογαριασμού Χρήστη users.name=Όνομα Χρήστη -users.full_name=Πλήρες Όνομα +users.full_name=Πλήρες όνομα users.activated=Ενεργοποιήθηκε users.admin=Διαχειριστής users.restricted=Περιορισμένος @@ -2910,29 +2973,29 @@ users.remote=Απομακρυσμένο users.2fa=2FA users.repos=Αποθετήρια users.created=Δημιουργήθηκε -users.last_login=Τελευταία Σύνδεση -users.never_login=Καμία Σύνδεση +users.last_login=Τελευταία σύνδεση +users.never_login=Καμία σύνδεση users.send_register_notify=Αποστολή ειδοποιήσεων εγγραφής χρήστη users.new_success=Ο λογαριασμός χρήστη «%s» δημιουργήθηκε. users.edit=Επεξεργασία -users.auth_source=Πηγή Ταυτοποίησης +users.auth_source=Πηγή ταυτοποίησης users.local=Τοπική -users.auth_login_name=Όνομα Σύνδεσης Ταυτοποίησης +users.auth_login_name=Όνομα σύνδεσης ταυτοποίησης users.password_helper=Αφήστε κενό τον κωδικό πρόσβασης για να τον κρατήσετε αμετάβλητο. users.update_profile_success=Ο λογαριασμός χρήστη έχει ενημερωθεί. -users.edit_account=Επεξεργασία Λογαριασμού Χρήστη -users.max_repo_creation=Μέγιστος Αριθμός Αποθετηρίων +users.edit_account=Επεξεργασία λογαριασμού χρήστη +users.max_repo_creation=Μέγιστος αριθμός αποθετηρίων users.max_repo_creation_desc=(Ορίστε -1 για να χρησιμοποιήσετε το προκαθορισμένο όριο.) users.is_activated=Ο Λογαριασμός Χρήστη Ενεργοποιήθηκε users.prohibit_login=Απενεργοποίηση εισόδου -users.is_admin=Είναι Διαχειριστής -users.is_restricted=Είναι Περιορισμένος -users.allow_git_hook=Μπορεί Να Δημιουργεί Git Hooks -users.allow_git_hook_tooltip=Τα Git Hooks εκτελούνται μέσω του χρήστη που εκτελεί το Forgejo και θα έχουν το ίδιο επίπεδο πρόσβασης στο διακομιστή. Ως αποτέλεσμα, οι χρήστες με αυτό το ειδικό προνόμιο Git Hook μπορούν να έχουν πρόσβαση και να τροποποιήσουν όλα τα αποθετήρια του Forgejo, καθώς και τη βάση δεδομένων που χρησιμοποιεί το Forgejo. Κατά συνέπεια, αυτοί οι χρήστες θα είναι σε θέση να αποκτήσουν δικαιώματα διαχειριστή στο Forgejo. -users.allow_import_local=Μπορεί Να Εισάγει Τοπικά Αποθετήρια -users.allow_create_organization=Μπορεί Να Δημιουργεί Οργανισμούς -users.update_profile=Ενημέρωση Λογαριασμού Χρήστη -users.delete_account=Διαγραφή Λογαριασμού Χρήστη +users.is_admin=Είναι διαχειριστής +users.is_restricted=Είναι περιορισμένος +users.allow_git_hook=Μπορεί να δημιουργεί Git hooks +users.allow_git_hook_tooltip=Τα Git hooks εκτελούνται μέσω του χρήστη που εκτελεί το Forgejo και θα έχουν το ίδιο επίπεδο πρόσβασης στο διακομιστή. Ως αποτέλεσμα, οι χρήστες με αυτό το ειδικό προνόμιο Git hook θα μπορούν να έχουν πρόσβαση και να τροποποιήσουν όλα τα αποθετήρια του Forgejo, καθώς και τη βάση δεδομένων που χρησιμοποιεί το Forgejo. Κατά συνέπεια, αυτοί οι χρήστες θα είναι σε θέση να αποκτήσουν δικαιώματα διαχειριστή στο Forgejo. +users.allow_import_local=Μπορεί να εισάγει τοπικά αποθετήρια +users.allow_create_organization=Μπορεί να δημιουργεί οργανισμούς +users.update_profile=Ενημέρωση λογαριασμού χρήστη +users.delete_account=Διαγραφή λογαριασμού χρήστη users.cannot_delete_self=Δεν μπορείτε να διαγράψετε τον εαυτό σας users.still_own_repo=Αυτός ο χρήστης εξακολουθεί να κατέχει ένα ή περισσότερα αποθετήρια. Διαγράψτε ή μεταφέρετε αυτά τα αποθετήρια πρώτα. users.still_has_org=Αυτός ο χρήστης είναι μέλος ενός οργανισμού. Αφαιρέστε πρώτα τον χρήστη από οποιονδήποτε οργανισμό. @@ -2960,8 +3023,8 @@ emails.primary=Κύριο emails.activated=Ενεργοποιήθηκε emails.filter_sort.email=Email emails.filter_sort.email_reverse=Email (αντίστροφη) -emails.filter_sort.name=Όνομα Χρήστη -emails.filter_sort.name_reverse=Όνομα Χρήστη (αντίστροφα) +emails.filter_sort.name=Όνομα χρήστη +emails.filter_sort.name_reverse=Όνομα χρήστη (αντίστροφα) emails.updated=Το email ενημερώθηκε emails.not_updated=Αποτυχία ενημέρωσης της ζητούμενης διεύθυνσης email: %v emails.duplicate_active=Αυτή η διεύθυνση email είναι ήδη ενεργή σε διαφορετικό χρήστη. @@ -2972,10 +3035,10 @@ orgs.org_manage_panel=Διαχείριση οργανισμών orgs.name=Όνομα orgs.teams=Ομάδες orgs.members=Μέλη -orgs.new_orga=Νέος Οργανισμός +orgs.new_orga=Νέος οργανισμός repos.repo_manage_panel=Διαχείριση αποθετηρίων -repos.unadopted=Μη Υιοθετημένα Αποθετήρια +repos.unadopted=Αποθετήρια χωρίς κατόχους repos.unadopted.no_more=Δεν βρέθηκαν μη υιοθετημένα αποθετήρια repos.owner=Ιδιοκτήτης repos.name=Όνομα @@ -3002,68 +3065,68 @@ packages.size=Μέγεθος packages.published=Δημοσιευμένα defaulthooks=Προεπιλεγμένα webhooks -defaulthooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούν ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ είναι προκαθορισμένα και θα αντιγραφούν σε όλα τα νέα αποθετήρια. Διαβάστε περισσότερα στον οδηγό webhooks. +defaulthooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούν ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ είναι προκαθορισμένα και θα αντιγραφούν σε όλα τα νέα αποθετήρια. Διαβάστε περισσότερα στον οδηγό webhooks. defaulthooks.add_webhook=Προσθήκη Προεπιλεγμένου Webhook defaulthooks.update_webhook=Ενημέρωση Προεπιλεγμένου Webhook systemhooks=Webhooks συστήματος -systemhooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούνται ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ θα ενεργούν σε όλα τα αποθετήρια του συστήματος, γι 'αυτό παρακαλώ εξετάστε τυχόν επιπτώσεις απόδοσης που μπορεί να έχει. Διαβάστε περισσότερα στον οδηγό webhooks. +systemhooks.desc=Τα Webhooks κάνουν αυτόματα αιτήσεις HTTP POST σε ένα διακομιστή όταν ενεργοποιούνται ορισμένα γεγονότα στο Gitea. Τα Webhooks που ορίζονται εδώ θα ενεργούν σε όλα τα αποθετήρια του συστήματος, γι 'αυτό παρακαλώ εξετάστε τυχόν επιπτώσεις απόδοσης που μπορεί να έχει. Διαβάστε περισσότερα στον οδηγό webhooks. systemhooks.add_webhook=Προσθήκη Webhook Συστήματος systemhooks.update_webhook=Ενημέρωση Webhook Συστήματος auths.auth_manage_panel=Διαχείριση πηγών ταυτοποίησης -auths.new=Προσθήκη Πηγής Ταυτοποίησης +auths.new=Προσθήκη πηγής ταυτοποίησης auths.name=Όνομα auths.type=Τύπος auths.enabled=Ενεργοποιημένο -auths.syncenabled=Ενεργοποίηση Συγχρονισμού Χρηστών +auths.syncenabled=Ενεργοποίηση συγχρονισμού χρηστών auths.updated=Ενημερώθηκε -auths.auth_type=Τύπος Ταυτοποίησης -auths.auth_name=Όνομα Ταυτοποίησης -auths.security_protocol=Πρωτόκολλο Ασφαλείας +auths.auth_type=Μέθοδος ταυτοποίησης +auths.auth_name=Όνομα ταυτοποίησης +auths.security_protocol=Πρωτόκολλο ασφαλείας auths.domain=Domain auths.host=Διακομιστής auths.port=Θύρα auths.bind_dn=Bind DN -auths.bind_password=Κωδικός Πρόσβασης Bind -auths.user_base=Search Base Χρήστη +auths.bind_password=Κωδικός πρόσβασης Bind +auths.user_base=Search base χρήστη auths.user_dn=DN Χρήστη -auths.attribute_username=Attribute Ονόματος Χρήστη +auths.attribute_username=Ιδιότητες ονόματος χρήστη auths.attribute_username_placeholder=Αφήστε κενό για να χρησιμοποιήσετε το όνομα χρήστη που πληκτρολογήσατε στο Forgejo. -auths.attribute_name=Χαρακτηριστικό Ονόματος -auths.attribute_surname=Χαρακτηριστικό Επωνύμου -auths.attribute_mail=Χαρακτηριστικό Email -auths.attribute_ssh_public_key=Χαρακτηριστικό Δημόσιου Κλειδιού SSH -auths.attribute_avatar=Χαρακτηριστικό Εικόνας +auths.attribute_name=Χαρακτηριστικό ονόματος +auths.attribute_surname=Χαρακτηριστικό επωνύμου +auths.attribute_mail=Χαρακτηριστικό email +auths.attribute_ssh_public_key=Χαρακτηριστικό δημόσιου κλειδιού SSH +auths.attribute_avatar=Χαρακτηριστικό εικόνας auths.attributes_in_bind=Λήψη χαρακτηριστικών μέσα στο πλαίσιο του Bind DN auths.allow_deactivate_all=Επιτρέψτε σε ένα κενό αποτέλεσμα αναζήτησης να απενεργοποιήσει όλους τους χρήστες -auths.use_paged_search=Χρήση Σελιδοποιημένης Αναζήτησης -auths.search_page_size=Μέγεθος Σελίδας -auths.filter=Φίλτρο Χρηστών -auths.admin_filter=Φίλτρο Διαχειριστών -auths.restricted_filter=Φίλτρο Περιορισμένων +auths.use_paged_search=Χρήση σελιδοποιημένης αναζήτησης +auths.search_page_size=Μέγεθος σελίδας +auths.filter=Φίλτρο χρηστών +auths.admin_filter=Φίλτρο διαχειριστών +auths.restricted_filter=Φίλτρο περιορισμένων auths.restricted_filter_helper=Αφήστε κενό για να μην περιορίσετε κανέναν χρήστη. Χρησιμοποιήστε έναν αστερίσκο ('*') για να περιορίσετε όλους τους χρήστες που δεν είναι διαχειριστές. auths.verify_group_membership=Επαλήθευση της συμμετοχής σε ομάδα στο LDAP (αφήστε το φίλτρο κενό για παράλειψη) -auths.group_search_base=DN Βάσης Αναζήτησης Ομάδων -auths.group_attribute_list_users=Χαρακτηριστικό Ομάδας Που Περιέχει Τη Λίστα Χρηστών -auths.user_attribute_in_group=Χαρακτηριστικό Χρήστη Στην Ομάδα +auths.group_search_base=DN βάσης αναζήτησης ομάδων +auths.group_attribute_list_users=Χαρακτηριστικό ομάδας που περιέχει τη λίστα χρηστών +auths.user_attribute_in_group=Χαρακτηριστικό χρήστη στην ομάδα auths.map_group_to_team=Αντιστοίχιση ομάδων LDAP σε ομάδες στους Οργανισμούς (αφήστε το πεδίο κενό για παράλειψη) auths.map_group_to_team_removal=Αφαίρεση χρηστών από τις συγχρονισμένες ομάδες αν ο χρήστης δεν ανήκει στην αντίστοιχη ομάδα LDAP auths.enable_ldap_groups=Ενεργοποίηση ομάδων LDAP -auths.ms_ad_sa=Χαρακτηριστικά Αναζήτησης Στο MS AD +auths.ms_ad_sa=Χαρακτηριστικά αναζήτησης στο MS AD auths.smtp_auth=Τύπος ταυτοποίησης SMTP auths.smtphost=Διακομιστής SMTP auths.smtpport=Θύρα SMTP -auths.allowed_domains=Επιτρεπόμενα Domains -auths.allowed_domains_helper=Αφήστε κενό για να επιτρέψετε όλα τα domains. Διαχωρίστε τα πολλαπλά domains με κόμμα (','). +auths.allowed_domains=Επιτρεπόμενα domains +auths.allowed_domains_helper=Αφήστε κενό για να επιτρέψετε όλα τα domains. Αν θέλετε να εισάγετε πολλαπλά domains, διαχωρίστε τα με κόμμα («,»). auths.skip_tls_verify=Παράλειψη επαλήθευσης TLS auths.force_smtps=Αναγκαστικό SMTPS auths.force_smtps_helper=Το SMTPS χρησιμοποιείται συνήθως στη θύρα 465. Ορίστε αυτό το πεδίο για χρήση του SMTPS σε άλλες θύρες. (Αλλιώς το STARTTLS θα χρησιμοποιηθεί σε άλλες θύρες αν υποστηρίζεται από τον διακομιστή.) auths.helo_hostname=Όνομα διακομιστή στο HELO auths.helo_hostname_helper=Όνομα διακομιστή που αποστέλλεται με το HELO. Αφήστε κενό για να στείλετε το τρέχον όνομα του διακομιστή. auths.disable_helo=Απενεργοποίηση HELO -auths.pam_service_name=Όνομα Υπηρεσίας PAM -auths.pam_email_domain=PAM Email Domain (προαιρετικό) +auths.pam_service_name=Όνομα υπηρεσίας PAM +auths.pam_email_domain=PAM email domain (προαιρετικό) auths.oauth2_provider=Πάροχος OAuth2 auths.oauth2_icon_url=URL Εικονιδίου auths.oauth2_clientID=Client ID (Κλειδί) @@ -3077,15 +3140,15 @@ auths.oauth2_emailURL=Email URL auths.skip_local_two_fa=Παράλειψη τοπικού 2FA auths.skip_local_two_fa_helper=Αφήνοντας μη-ορισμένο σημαίνει ότι οι τοπικοί χρήστες με ορισμένο 2FA θα πρέπει να πετύχουν το 2FA για να συνδεθούν auths.oauth2_tenant=Ένοικος -auths.oauth2_scopes=Πρόσθετα Scopes -auths.oauth2_required_claim_name=Απαιτούμενο Όνομα Claim +auths.oauth2_scopes=Πρόσθετα scopes +auths.oauth2_required_claim_name=Απαιτούμενο όνομα claim auths.oauth2_required_claim_name_helper=Ορίστε αυτό το όνομα για να περιορίσετε τη σύνδεση από αυτή την πηγή στους χρήστες με ένα claim με αυτό το όνομα -auths.oauth2_required_claim_value=Απαιτούμενη Τιμή Claim +auths.oauth2_required_claim_value=Απαιτούμενη τιμή claim auths.oauth2_required_claim_value_helper=Ορίστε αυτήν την τιμή για να περιορίσετε τη σύνδεση από αυτή την πηγή στους χρήστες με ένα claim με αυτό το όνομα και την τιμή -auths.oauth2_group_claim_name=Όνομα claim που παρέχει ονόματα ομάδων για αυτήν την πηγή. (Προαιρετικό) -auths.oauth2_admin_group=Τιμή Group Claim για διαχειριστές. (Προαιρετικό - απαιτεί όνομα claim παραπάνω) -auths.oauth2_restricted_group=Τιμή Group Claim για περιορισμένους χρήστες (Προαιρετικό - απαιτεί όνομα claim παραπάνω) -auths.oauth2_map_group_to_team=Αντιστοίχιση των απαιτούμενων ομάδων σε ομάδες Οργανισμού. (Προαιρετικό - απαιτείται το όνομα της απαίτησης παραπάνω) +auths.oauth2_group_claim_name=Όνομα claim που παρέχει ονόματα ομάδων για αυτήν την πηγή. (προαιρετικό) +auths.oauth2_admin_group=Τιμή Group Claim για διαχειριστές. (προαιρετικό - απαιτεί όνομα claim παραπάνω) +auths.oauth2_restricted_group=Τιμή Group Claim για περιορισμένους χρήστες (προαιρετικό - απαιτεί όνομα claim παραπάνω) +auths.oauth2_map_group_to_team=Αντιστοίχιση των απαιτούμενων ομάδων σε ομάδες οργανισμού. (προαιρετικό - απαιτείται το όνομα της απαίτησης παραπάνω) auths.oauth2_map_group_to_team_removal=Αφαίρεση χρηστών από τις συγχρονισμένες ομάδες, εάν ένας χρήστης δεν ανήκει στην αντίστοιχη ομάδα. auths.enable_auto_register=Ενεργοποίηση αυτόματης εγγραφής auths.sspi_auto_create_users=Αυτόματη δημιουργία χρηστών @@ -3112,16 +3175,16 @@ auths.tip.google_plus=Αποκτήστε τα διαπιστευτήρια πε auths.tip.openid_connect=Χρησιμοποιήστε το OpenID Connect Discovery URL (/.well known/openid-configuration) για να καθορίσετε τα τελικά σημεία auths.tip.twitter=Πηγαίνετε στο https://dev.twitter.com/apps, δημιουργήστε μια εφαρμογή και βεβαιωθείτε ότι η επιλογή “Allow this application to be used to Sign in with Twitter” είναι ενεργοποιημένη auths.tip.discord=Καταχωρήστε μια νέα εφαρμογή στο https://discordapp.com/developers/applications/me -auths.tip.gitea=Καταχωρήστε μια νέα εφαρμογή OAuth2. Μπορείτε να βρείτε τον οδηγό στο https://docs.gitea.com/development/oauth2-provider -auths.tip.yandex=`Δημιουργήστε μια νέα εφαρμογή στο https://oauth.yandex.com/client/new. Επιλέξτε τα ακόλουθα δικαιώματα από την ενότητα "Yandex.Passport API": "Access to email address", "Access to user avatar" και "Access to username, first name and surname, gender"` +auths.tip.gitea=Καταχωρήστε μια νέα εφαρμογή OAuth2. Μπορείτε να βρείτε τον οδηγό στο https://forgejo.org/docs/latest/user/oauth2-provider +auths.tip.yandex=Δημιουργήστε μια νέα εφαρμογή στο https://oauth.yandex.com/client/new. Επιλέξτε τα ακόλουθα δικαιώματα από την ενότητα "Yandex.Passport API": "Access to email address", "Access to user avatar" και "Access to username, first name and surname, gender"` auths.tip.mastodon=Εισαγάγετε ένα προσαρμομένο URL για την υπηρεσία mastodon με την οποία θέλετε να πιστοποιήσετε (ή να χρησιμοποιήσετε την προεπιλεγμένη) -auths.edit=Επεξεργασία Πηγής Ταυτοποίησης +auths.edit=Επεξεργασία πηγής ταυτοποίησης auths.activated=Αυτή η πηγή είναι ενεργοποιημένη auths.new_success=Το μέσο ταυτοποίησης «%s» προστέθηκε. auths.update_success=Η πηγή ενημερώθηκε. -auths.update=Ενημέρωση Πηγής Ταυτοποίησης -auths.delete=Διαγραφή Πηγής Ταυτοποίησης -auths.delete_auth_title=Διαγραφή Πηγής Ταυτοποίησης +auths.update=Ενημέρωση πηγής ταυτοποίησης +auths.delete=Διαγραφή πηγής ταυτοποίησης +auths.delete_auth_title=Διαγραφή πηγής ταυτοποίησης auths.delete_auth_desc=Αν διαγράψετε την πηγής ταυτοποίησης, οι χρήστες σας δεν θα μπορέσουν να τη χρησιμοποιήσουν πλέον για να συνδεθούν. Συνέχεια; auths.still_in_used=Η πηγή ταυτοποίησης βρίσκεται ακόμα σε χρήση. Για να συνεχίσετε, πρέπει πρώτα να διαγράψετε ή να μετατρέψετε τους χρήστες που χρησιμοποιούν αυτήν την πηγή. auths.deletion_success=Η πηγή ταυτοποίησης έχει διαγραφεί. @@ -3154,7 +3217,7 @@ config.ssh_enabled=Ενεργοποιημένο config.ssh_start_builtin_server=Χρήση ενσωματωμένου διακομιστή config.ssh_domain=Domain διακομιστή SSH config.ssh_port=Θύρα -config.ssh_listen_port=Θύρα Ακρόασης +config.ssh_listen_port=Θύρα ακρόασης (Listen port) config.ssh_root_path=Ριζική Διαδρομή config.ssh_key_test_path=Διαδρομή δοκιμής κλειδιού config.ssh_keygen_path=Διαδρομή keygen («ssh-keygen») @@ -3208,28 +3271,28 @@ config.mailer_enabled=Ενεργοποιημένο config.mailer_enable_helo=Ενεργοποίηση HELO config.mailer_name=Όνομα config.mailer_protocol=Πρωτόκολλο -config.mailer_smtp_addr=Διευθ SMTP +config.mailer_smtp_addr=Διεύθυνση SMTP config.mailer_smtp_port=Θύρα SMTP config.mailer_user=Χρήστης config.mailer_use_sendmail=Χρήση Sendmail -config.mailer_sendmail_path=Διαδρομή Sendmail +config.mailer_sendmail_path=Τοποθεσία Sendmail config.mailer_sendmail_args=Επιπλέον παράμετροι για το Sendmail -config.mailer_sendmail_timeout=Χρονικό Όριο Sendmail +config.mailer_sendmail_timeout=Χρονικό όριο Sendmail config.mailer_use_dummy=Ψεύτικο config.test_email_placeholder=Email (π.χ. test@example.com) -config.send_test_mail=Αποστολή Δοκιμαστικού Email +config.send_test_mail=Αποστολή δοκιμαστικού email config.send_test_mail_submit=Αποστολή config.test_mail_failed=Η αποστολή δοκιμαστικού email στο «%s» απέτυχε: %v config.test_mail_sent=Στάλθηκε ένα δοκιμαστικό email στο «%s». -config.oauth_config=Ρύθμιση Oauth +config.oauth_config=Ρυθμίσεις OAuth config.oauth_enabled=Ενεργό config.cache_config=Ρύθμιση προσωρινής αποθήκευσης config.cache_adapter=Προσαρμογέας προσωρινής αποθήκευσης config.cache_interval=Διάστημα προσωρινής αποθήκευσης -config.cache_conn=Σύνδεση Προσωρινής Αποθήκευσης -config.cache_item_ttl=TTL Στοιχείων Προσωρινής Αποθήκευσης +config.cache_conn=Σύνδεση προσωρινής αποθήκευσης +config.cache_item_ttl=TTL στοιχείων προσωρινής αποθήκευσης config.session_config=Ρυθμίσεις συνεδρίας config.session_provider=Πάροχος συνεδρίας @@ -3240,10 +3303,10 @@ config.session_life_time=Χρόνος ζωής συνεδρίας config.https_only=Μόνο HTTPS config.cookie_life_time=Χρόνος ζωής Cookie -config.picture_config=Ρυθμίσεις εικόνας και avatar +config.picture_config=Ρυθμίσεις εικόνας (προφίλ) config.picture_service=Υπηρεσία εικόνας config.disable_gravatar=Απενεργοποίηση Gravatar -config.enable_federated_avatar=Ενεργοποίηση αποκεντρωμένων avatar +config.enable_federated_avatar=Ενεργοποίηση αποκεντρωμένων εικόνων προφίλ config.git_config=Ρυθμίσεις Git config.git_disable_diff_highlight=Απενεργοποίηση επισήμανσης σύνταξης diff @@ -3292,29 +3355,29 @@ monitor.queue=Ουρά: %s monitor.queue.name=Όνομα monitor.queue.type=Τύπος monitor.queue.exemplar=Τύπος Υποδείγματος -monitor.queue.numberworkers=Αριθμός Εργατών -monitor.queue.activeworkers=Ενεργοί Εργάτες -monitor.queue.maxnumberworkers=Μέγιστος Αριθμός Εργατών -monitor.queue.numberinqueue=Πλήθος Ουράς -monitor.queue.review_add=Εξέταση / Προσθήκη Εργατών -monitor.queue.settings.title=Ρυθμίσεις Δεξαμενής +monitor.queue.numberworkers=Αριθμός εργατών +monitor.queue.activeworkers=Ενεργοί εργάτες +monitor.queue.maxnumberworkers=Μέγιστος αριθμός εργατών +monitor.queue.numberinqueue=Πλήθος ουράς +monitor.queue.review_add=Εξέταση / προσθήκη εργατών +monitor.queue.settings.title=Ρυθμίσεις δεξαμενής monitor.queue.settings.desc=Οι δεξαμενές αυξάνονται δυναμικά όταν υπάρχει φραγή της ουράς των εργατών τους. monitor.queue.settings.maxnumberworkers=Μέγιστος Αριθμός Εργατών monitor.queue.settings.maxnumberworkers.placeholder=Αυτή τη στιγμή %[1]d monitor.queue.settings.maxnumberworkers.error=Ο μέγιστος αριθμός εργατών πρέπει να είναι αριθμός -monitor.queue.settings.submit=Ενημέρωση Ρυθμίσεων -monitor.queue.settings.changed=Οι Ρυθμίσεις Ενημερώθηκαν +monitor.queue.settings.submit=Ενημέρωση ρυθμίσεων +monitor.queue.settings.changed=Οι ρυθμίσεις ενημερώθηκαν monitor.queue.settings.remove_all_items=Αφαίρεση όλων monitor.queue.settings.remove_all_items_done=Όλα τα αντικείμενα στην ουρά αφαιρέθηκαν. notices.system_notice_list=Ειδοποιήσεις συστήματος notices.view_detail_header=Προβολή λεπτομερειών ειδοποίησης notices.operations=Λειτουργίες -notices.select_all=Επιλογή Όλων -notices.deselect_all=Αποεπιλογή Όλων -notices.inverse_selection=Αντιστροφή Επιλογής -notices.delete_selected=Διαγραφή Επιλεγμένων -notices.delete_all=Διαγραφή Όλων Των Ειδοποιήσεων +notices.select_all=Επιλογή όλων +notices.deselect_all=Αποεπιλογή όλων +notices.inverse_selection=Αντιστροφή επιλογής +notices.delete_selected=Διαγραφή επιλεγμένων +notices.delete_all=Διαγραφή όλων των ειδοποιήσεων notices.type=Τύπος notices.type_1=Αποθετήριο notices.type_2=Εργασία @@ -3323,10 +3386,17 @@ notices.op=Λειτ. notices.delete_success=Οι ειδοποιήσεις του συστήματος έχουν διαγραφεί. self_check.no_problem_found = Μέχρι τώρα, δεν έχει βρεθεί κάποιο πρόβλημα. self_check = Αυτοέλεγχος -dashboard.sync_repo_tags = Συγχρονισμός tag από δεδομένα git στην βάση δεδομένων +dashboard.sync_repo_tags = Συγχρονισμός tag, χρησιμοποιώντας τα δεδομένα git στην βάση δεδομένων dashboard.sync_tag.started = Ο συγχρονισμός tag έχει ξεκινήσει self_check.database_inconsistent_collation_columns = Η βάση δεδομένων χρησιμοποιεί το collation %s, αλλά οι στήλες του χρησιμοποιούν collations που δεν αντιστοιχούν σε εκείνο το collation. Αυτό ενδέχεται να προκαλέσει μερικά απρόσμενα θέματα. self_check.database_fix_mysql = Για τους χρήστες του MySQL/MariaDB: Μπορείτε να χρησιμοποιήσετε την εντολή «git doctor convert» για να διορθώσετε το collation ή να το διορθώσετε χειροκίνητα με τις εντολές «ALTER ... COLLATE ...». +config_settings = Ρυθμίσεις +auths.tips.gmail_settings = Ρυθμίσεις Gmail: +config_summary = Περίληψη +config.open_with_editor_app_help = Οι επεξεργαστές κειμένου / κώδικα που εμφανίζονται στο μενού κλωνοποίησης σε ένα αποθετήριο. Αν το αφήσετε κενό, θα χρησιμοποιηθεί μία προεπιλεγμένη λίστα επεξεργαστών. Πατήστε για να δείτε την προεπιλογή. +auths.tip.gitlab_new = Μπορείτε να δημιουργήσετε μία νέα εφαρμογή στο https://gitlab.com/-/profile/applications +self_check.database_collation_mismatch = Η βάση δεδομένων αναμένεται να χρησιμοποιεί το collation: %s +self_check.database_collation_case_insensitive = Η βάση δεδομένων χρησιμοποιεί το collation %s, το οποίο δεν ξεχωρίζει κεφαλαία και πεζά γράμματα. Αν και το Forgejo μπορεί να δουλέψει με αυτό, ίσως να προκύψουν κάποιες σπάνιες περιπτώσεις όπου κάτι δεν θα δουλέψει όπως αναμένεται. [action] @@ -3415,8 +3485,8 @@ error.probable_bad_default_signature=ΠΡΟΣΟΧΗ! Αν και το προεπ [units] unit=Μονάδα -error.no_unit_allowed_repo=Δεν σας επιτρέπεται να έχετε πρόσβαση σε οποιαδήποτε ενότητα αυτού του αποθετηρίου. -error.unit_not_allowed=Δεν σας επιτρέπεται να έχετε πρόσβαση σε αυτήν την ενότητα αποθετηρίου. +error.no_unit_allowed_repo=Δεν σας επιτρέπεται η πρόσβαση σε οποιαδήποτε μονάδα του αποθετηρίου. +error.unit_not_allowed=Δεν σας επιτρέπεται η πρόσβαση σε αυτήν την μονάδα αποθετηρίου. [packages] title=Πακέτα @@ -3439,9 +3509,9 @@ dependencies=Εξαρτήσεις keywords=Λέξεις κλειδιά details=Λεπτομέρειες details.author=Συγγραφέας -details.project_site=Ιστοσελίδα Έργου -details.repository_site=Ιστοσελίδα Αποθετηρίου -details.documentation_site=Ιστοσελίδα Τεκμηρίωσης +details.project_site=Ιστοσελίδα έργου +details.repository_site=Ιστοσελίδα αποθετηρίου +details.documentation_site=Ιστοσελίδα τεκμηρίωσης details.license=Άδεια assets=Πόροι versions=Εκδόσεις @@ -3537,19 +3607,19 @@ settings.delete.notice=Πρόκειται να διαγράψετε το %s (%s) settings.delete.success=Το πακέτο έχει διαγραφεί. settings.delete.error=Αποτυχία διαγραφής του πακέτου. owner.settings.cargo.title=Ευρετήριο μητρώου Cargo -owner.settings.cargo.initialize=Αρχικοποίηση Ευρετηρίου +owner.settings.cargo.initialize=Αρχικοποίηση ευρετηρίου owner.settings.cargo.initialize.description=Απαιτείται ένα ειδικό αποθετήριο ευρετηρίου Git για τη χρήση του μητρώου Cargo. Χρησιμοποιώντας αυτή την επιλογή θα δημιουργηθεί ξανά το αποθετήριο και θα ρυθμιστεί αυτόματα. owner.settings.cargo.initialize.error=Αποτυχία αρχικοποίησης ευρετηρίου Cargo: %v owner.settings.cargo.initialize.success=Ο ευρετήριο Cargo δημιουργήθηκε με επιτυχία. -owner.settings.cargo.rebuild=Αναδημιουργία Ευρετηρίου +owner.settings.cargo.rebuild=Ανανέωση ευρετηρίου owner.settings.cargo.rebuild.description=Η ανοικοδόμηση μπορεί να είναι χρήσιμη εάν ο δείκτης δεν είναι συγχρονισμένος με τα αποθηκευμένα πακέτα Cargo. owner.settings.cargo.rebuild.error=Αποτυχία αναδόμησης του ευρετηρίου Cargo: %v owner.settings.cargo.rebuild.success=Το ευρετήριο Cargo αναδομήθηκε με επιτυχία. owner.settings.cleanuprules.title=Διαχείριση κανόνων εκκαθάρισης -owner.settings.cleanuprules.add=Προσθήκη Κανόνα Εκκαθάρισης -owner.settings.cleanuprules.edit=Επεξεργασία Κανόνα Εκκαθάρισης -owner.settings.cleanuprules.none=Δεν υπάρχουν διαθέσιμοι κανόνες εκκαθάρισης. Παρακαλούμε συμβουλευτείτε την τεκμηρίωση. -owner.settings.cleanuprules.preview=Προεπισκόπηση Κανόνα Εκκαθάρισης +owner.settings.cleanuprules.add=Προσθήκη κανόνα εκκαθάρισης +owner.settings.cleanuprules.edit=Επεξεργασία κανόνα εκκαθάρισης +owner.settings.cleanuprules.none=Δεν υπάρχουν ακόμα κάποιοι κανόνες εκκαθάρισης. +owner.settings.cleanuprules.preview=Προεπισκόπηση κανόνα εκκαθάρισης owner.settings.cleanuprules.preview.overview=%d πακέτα έχουν προγραμματιστεί να αφαιρεθούν. owner.settings.cleanuprules.preview.none=Ο κανόνας εκκαθάρισης δεν ταιριάζει με κανένα πακέτο. owner.settings.cleanuprules.enabled=Ενεργοποιημένο @@ -3567,8 +3637,9 @@ owner.settings.cleanuprules.success.update=Ο κανόνας καθαρισμο owner.settings.cleanuprules.success.delete=Ο κανόνας καθαρισμού διαγράφηκε. owner.settings.chef.title=Μητρώο Chef owner.settings.chef.keypair=Δημιουργία ζεύγους κλειδιών -owner.settings.chef.keypair.description=Ένα ζεύγος κλειδιών είναι απαραίτητο για ταυτοποίηση στο μητρώο Chef. Αν έχετε δημιουργήσει ένα ζεύγος κλειδιών πριν, η δημιουργία ενός νέου ζεύγους κλειδιών θα απορρίψει το παλιό ζεύγος κλειδιών. -rpm.repository.multiple_groups = Αυτό το πακέτο είναι διαθέσιμο σε διαφορετικά group. +owner.settings.chef.keypair.description=Ένα ζευγάρι κλειδιών είναι απαραίτητο για την ταυτοποίηση στο μητρώο Chef. Αν έχετε ήδη δημιουργήσει ένα ζευγάρι κλειδιών, η δημιουργία ενός νέου ζευγαριού κλειδιών θα αντικαταστήσει το παλιό ζευγάρι. +rpm.repository.multiple_groups = Αυτό το πακέτο είναι διαθέσιμο σε πολλαπλά group. +owner.settings.cargo.rebuild.no_index = Η ανανέωση δεν είναι δυνατή, επειδή το ευρετήριο δεν έρχει αρχικοποιηθεί. [secrets] secrets=Μυστικά @@ -3583,7 +3654,7 @@ deletion=Αφαίρεση μυστικού deletion.description=Η αφαίρεση ενός μυστικού είναι μόνιμη και δεν μπορεί να αναιρεθεί. Συνέχεια; deletion.success=Το μυστικό έχει αφαιρεθεί. deletion.failed=Αποτυχία αφαίρεσης μυστικού. -management=Διαχείριση Μυστικών +management=Διαχείριση μυστικών [actions] actions=Actions @@ -3601,7 +3672,7 @@ status.blocked=Αποκλείστηκε runners=Εκτελεστές runners.runner_manage_panel=Διαχείριση εκτελεστών -runners.new=Δημιουργία νέου Εκτελεστή +runners.new=Δημιουργία νέου εκτελεστή runners.new_notice=Πώς να ξεκινήσετε έναν εκτελεστή runners.status=Κατάσταση runners.id=ID @@ -3609,7 +3680,7 @@ runners.name=Όνομα runners.owner_type=Τύπος runners.description=Περιγραφή runners.labels=Ετικέτες -runners.last_online=Τελευταία Ώρα Σύνδεσης +runners.last_online=Τελευταία ώρα σύνδεσης runners.runner_title=Εκτελεστής runners.task_list=Πρόσφατες εργασίες στον εκτελεστή runners.task_list.no_tasks=Δεν υπάρχει καμία εργασία ακόμα. @@ -3619,7 +3690,7 @@ runners.task_list.repository=Αποθετήριο runners.task_list.commit=Υποβολή runners.task_list.done_at=Ολοκλήρωσε Στις runners.edit_runner=Επεξεργασία Εκτελεστή -runners.update_runner=Ενημέρωση Αλλαγών +runners.update_runner=Ενημέρωση αλλαγών runners.update_runner_success=Ο εκτελεστής ενημερώθηκε επιτυχώς runners.update_runner_failed=Αποτυχία ενημέρωσης εκτελεστή runners.delete_runner=Διαγραφή του εκτελεστή @@ -3636,7 +3707,7 @@ runners.version=Έκδοση runners.reset_registration_token=Επαναφορά διακριτικού εγγραφής runners.reset_registration_token_success=Επιτυχής επανέκδοση διακριτικού εγγραφής του εκτελεστή -runs.all_workflows=Όλες Οι Ροές Εργασίας +runs.all_workflows=Όλες οι ροές εργασίας runs.commit=Υποβολή runs.scheduled=Προγραμματισμένα runs.pushed_by=ωθήθηκε από @@ -3651,17 +3722,17 @@ runs.no_workflows=Δεν υπάρχουν ροές εργασίας ακόμα. runs.no_runs=Η ροή εργασίας δεν έχει τρέξει ακόμα. runs.empty_commit_message=(κενό μήνυμα υποβολής) -workflow.disable=Απενεργοποίηση Ροής Εργασιών +workflow.disable=Απενεργοποίηση ροής εργασίας workflow.disable_success=Η ροή εργασίας «%s» απενεργοποιήθηκε επιτυχώς. -workflow.enable=Ενεργοποίηση Ροής Εργασίας +workflow.enable=Ενεργοποίηση ροής εργασίας workflow.enable_success=Η ροή εργασίας «%s» ενεργοποιήθηκε επιτυχώς. workflow.disabled=Η ροή εργασιών είναι απενεργοποιημένη. need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork. variables=Μεταβλητές -variables.management=Διαχείριση Μεταβλητών -variables.creation=Προσθήκη Μεταβλητής +variables.management=Διαχείριση μεταβλητών +variables.creation=Προσθήκη μεταβλητής variables.none=Δεν υπάρχουν μεταβλητές ακόμα. variables.deletion=Αφαίρεση μεταβλητής variables.deletion.description=Η αφαίρεση μιας μεταβλητής είναι μόνιμη και δεν μπορεί να αναιρεθεί. Συνέχεια; @@ -3677,11 +3748,12 @@ variables.id_not_exist = Η μεταβλητή με id %d δεν υπάρχει. runs.no_workflows.documentation = Για περισσότερες πληροφορίες σχετικά με το Forgejo Actions, συμβουλευτείτε τον οδηγό. runs.no_workflows.quick_start = Δεν ξέρετε από που να πρωτοξεκινήσετε με το Forgejo Actions; Για μια γρήγορη αρχή, συμβουλευτείτε τον οδηγό μας. runs.workflow = Ροή εργασίας +runs.no_job_without_needs = Η ροή εργασίας πρέπει να περιέχει τουλάχιστον ένα έργο που δεν εξαρτάται από κάποιο άλλο έργο. [projects] -type-1.display_name=Ατομικό Έργο -type-2.display_name=Έργο Αποθετηρίου -type-3.display_name=Έργο Οργανισμού +type-1.display_name=Ατομικό έργο +type-2.display_name=Έργο αποθετηρίου +type-3.display_name=Έργο οργανισμού [git.filemode] changed_filemode=%[1]s → %[2]s @@ -3692,3 +3764,40 @@ executable_file=Εκτελέσιμο αρχείο symbolic_link=Symbolic link submodule=Υπομονάδα + + +[search] +code_search_unavailable = Η αναζήτηση κώδικα δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. +keyword_search_unavailable = Η αναζήτηση με την χρήση λέξεων-κλειδιών δεν είναι επί του παρόντος διαθέσιμη. Παρακαλώ επικοινωνήστε με τον διαχειριστή σας. +runner_kind = Αναζήτηση runner... +code_search_by_git_grep = Η αναζήτηση κώδικα εκτελείται με την χρήση της εντολής «git grep». Αν ο διαχειριστής σας ενεργοποιήσει ένα ευρετήριο για αποθετήρια («Repository Indexer»), ίσως να παρουσιαστούν καλύτερα αποτελέσματα. +package_kind = Αναζήτηση πακέτων... +project_kind = Αναζήτηση έργων... +branch_kind = Αναζήτηση κλάδων... +commit_kind = Αναζήτηση υποβολών... +no_results = Δεν βρέθηκαν κατάλληλα αποτελέσματα. +search = Αναζήτηση... +type_tooltip = Είδος αναζήτησης +fuzzy = Στο περίπου +fuzzy_tooltip = Να συμπεριληφθούν αποτελέσματα που μοιάζουν με τον όρο αναζήτησης +match = Ταιριαστά +match_tooltip = Να συμπεριληφθούν αποτελέσματα που ταιριάζουν με τον όρο αναζήτησης +repo_kind = Αναζήτηση αποθετηρίων... +user_kind = Αναζήτηση χρηστών... +org_kind = Αναζήτηση οργανισμών... +team_kind = Αναζήτηση ομαδών... +code_kind = Αναζήτηση κώδικα... + +[munits.data] +mib = MiB +gib = GiB +tib = TiB +pib = PiB +eib = EiB +b = B +kib = KiB + +[markup] +filepreview.line = Γραμμή %[1]d στο αρχείο %[2]s +filepreview.lines = Γραμμές %[1]d έως %[2]d στο αρχείο %[3]s +filepreview.truncated = Η προεπισκόπηση έχει περικοπεί \ No newline at end of file diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 1152ad28a9..7a8f19270c 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -64,7 +64,6 @@ new_org = New organization new_project = New project new_project_column = New column admin_panel = Site administration -account_settings = Account settings settings = Settings your_profile = Profile your_starred = Starred @@ -100,6 +99,7 @@ disabled = Disabled locked = Locked copy = Copy +copy_generic = Copy to clipboard copy_url = Copy URL copy_hash = Copy hash copy_content = Copy content @@ -171,7 +171,7 @@ org_kind = Search orgs... team_kind = Search teams... code_kind = Search code... code_search_unavailable = Code search is currently not available. Please contact the site administrator. -code_search_by_git_grep = Current code search results are provided by "git grep". There might be better results if site administrator enables Repository Indexer. +code_search_by_git_grep = Current code search results are provided by "git grep". There might be better results if site administrator enables code indexer. package_kind = Search packages... project_kind = Search projects... branch_kind = Search branches... @@ -347,7 +347,6 @@ password_algorithm = Password hash algorithm invalid_password_algorithm = Invalid password hash algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. enable_update_checker = Enable update checker -enable_update_checker_helper = Checks for new version releases periodically by connecting to gitea.io. env_config_keys = Environment Configuration env_config_keys_prompt = The following environment variables will also be applied to your configuration file: @@ -546,6 +545,12 @@ modify = Update [form] UserName = Username +FullName = Full name +Description = Description +Pronouns = Pronouns +Biography = Biography +Website = Website +Location = Location RepoName = Repository name Email = Email address Password = Password @@ -556,6 +561,8 @@ PayloadUrl = Payload URL TeamName = Team name AuthName = Authorization name AdminEmail = Admin email +To = Branch name +AccessToken = Access token NewBranchName = New branch name CommitSummary = Commit summary @@ -2144,7 +2151,7 @@ settings.sync_mirror = Synchronize now settings.pull_mirror_sync_in_progress = Pulling changes from the remote %s at the moment. settings.push_mirror_sync_in_progress = Pushing changes to the remote %s at the moment. settings.site = Website -settings.update_settings = Update settings +settings.update_settings = Save settings settings.update_mirror_settings = Update mirror settings settings.branches.switch_default_branch = Switch default branch settings.branches.update_default_branch = Update default branch @@ -2379,6 +2386,7 @@ settings.slack_token = Token settings.slack_domain = Domain settings.slack_channel = Channel settings.add_web_hook_desc = Integrate %s into your repository. +settings.graphql_url = GraphQL URL settings.web_hook_name_gitea = Gitea settings.web_hook_name_forgejo = Forgejo settings.web_hook_name_gogs = Gogs @@ -2398,10 +2406,10 @@ settings.packagist_api_token = API token settings.packagist_package_url = Packagist package URL settings.web_hook_name_sourcehut_builds = SourceHut Builds settings.sourcehut_builds.manifest_path = Build manifest path -settings.sourcehut_builds.graphql_url = GraphQL URL (e.g. https://builds.sr.ht/query) settings.sourcehut_builds.visibility = Job visibility settings.sourcehut_builds.secrets = Secrets settings.sourcehut_builds.secrets_helper = Give the job access to the build secrets (requires the SECRETS:RO grant) +settings.sourcehut_builds.access_token_helper = Access token that has JOBS:RW grant. Generate a builds.sr.ht token or a builds.sr.ht token with secrets access on meta.sr.ht. settings.deploy_keys = Deploy keys settings.add_deploy_key = Add deploy key settings.deploy_key_desc = Deploy keys have read-only pull access to the repository. @@ -2423,7 +2431,7 @@ settings.protected_branch.delete_rule = Delete rule settings.protected_branch_can_push = Allow push? settings.protected_branch_can_push_yes = You can push settings.protected_branch_can_push_no = You cannot push -settings.branch_protection = Branch protection rules for branch "%s" +settings.branch_protection = Protection rules for branch "%s" settings.protect_this_branch = Enable branch protection settings.protect_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch. settings.protect_disable_push = Disable push @@ -2508,6 +2516,8 @@ settings.thread_id = Thread ID settings.matrix.homeserver_url = Homeserver URL settings.matrix.room_id = Room ID settings.matrix.message_type = Message type +settings.matrix.access_token_helper = It is recommended to setup a dedicated Matrix account for this. The access token can be retrieved from the Element web client (in a private/incognito tab) > User menu (top left) > All settings > Help & About > Advanced > Access Token (right below the Homeserver URL). Close the private/incognito tab (logging out would invalidate the token). +settings.matrix.room_id_helper = The Room ID can be retrieved from the Element web client > Room Settings > Advanced > Internal room ID. Example: %s. settings.archive.button = Archive repo settings.archive.header = Archive this repo settings.archive.text = Archiving the repo will make it entirely read-only. It will be hidden from the dashboard. Nobody (not even you!) will be able to make new commits, or open any issues or pull requests. @@ -2596,7 +2606,7 @@ diff.comment.add_single_comment = Add single comment diff.comment.add_review_comment = Add comment diff.comment.start_review = Start review diff.comment.reply = Reply -diff.review = Review +diff.review = Finish review diff.review.header = Submit review diff.review.placeholder = Review comment diff.review.comment = Comment @@ -2658,6 +2668,8 @@ release.downloads = Downloads release.download_count_one = %s download release.download_count_few = %s downloads release.add_tag_msg = Use the title and content of release as tag message. +release.hide_archive_links = Hide automatically generated archives +release.hide_archive_links_helper = Hide automatically generated source code archives for this release. For example, if you are uploading your own. release.add_tag = Create Tag Only release.releases_for = Releases for %s release.tags_for = Tags for %s @@ -2997,7 +3009,7 @@ users.delete_account = Delete user account users.cannot_delete_self = You cannot delete yourself users.still_own_repo = This user still owns one or more repositories. Delete or transfer these repositories first. users.still_has_org = This user is a member of an organization. Remove the user from any organizations first. -users.purge = Purge User +users.purge = Purge user users.purge_help = Forcibly delete user and any repositories, organizations, and packages owned by the user. All comments and issues posted by this user will also be deleted. users.still_own_packages = This user still owns one or more packages, delete these packages first. users.deletion_success = The user account has been deleted. @@ -3007,14 +3019,14 @@ users.list_status_filter.reset = Reset users.list_status_filter.is_active = Active users.list_status_filter.not_active = Inactive users.list_status_filter.is_admin = Admin -users.list_status_filter.not_admin = Not Admin +users.list_status_filter.not_admin = Not admin users.list_status_filter.is_restricted = Restricted -users.list_status_filter.not_restricted = Not Restricted -users.list_status_filter.is_prohibit_login = Prohibit Login -users.list_status_filter.not_prohibit_login = Allow Login -users.list_status_filter.is_2fa_enabled = 2FA Enabled -users.list_status_filter.not_2fa_enabled = 2FA Disabled -users.details = User Details +users.list_status_filter.not_restricted = Not restricted +users.list_status_filter.is_prohibit_login = Prohibit login +users.list_status_filter.not_prohibit_login = Allow login +users.list_status_filter.is_2fa_enabled = 2FA enabled +users.list_status_filter.not_2fa_enabled = 2FA disabled +users.details = User details emails.email_manage_panel = Manage user emails emails.primary = Primary diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index 88bbb958bd..02542631eb 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -295,7 +295,6 @@ no_reply_address_helper = Retnomo kiu uziĝus por uzantoj kun kaŝita retpoŝtad enable_update_checker = Ŝalti novversian kontrolanton password_algorithm = Pasvorthaketiga algoritmo env_config_keys = Mediagordoj -enable_update_checker_helper = Foje kontrolas ĉu estas novaj versioj per konektiĝo al gitea.io. invalid_password_algorithm = Malvalida pasvorthakeita algoritmo password_algorithm_helper = Agordas la pasvorthaketigan algoritmon. Algoritmoj havas malsamajn postulojn kaj efikecojn. La algoritmo argon2 sufiĉe sekuras, sed postulas multan memoron kaj eble ne taŭgas por nepotencaj serviloj. internal_token_failed = Malsukcesis krei internan ĵetonon: %v diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 5ac709e875..7727e930a6 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -314,7 +314,6 @@ password_algorithm=Algoritmo Hash de Contraseña invalid_password_algorithm=Algoritmo hash de contraseña no válido password_algorithm_helper=Establece el algoritmo de hashing de contraseña. Los algoritmos tienen diferentes requisitos y fuerza. El algoritmo argon2 es bastante seguro, pero usa mucha memoria y puede ser inapropiado para sistemas pequeños. enable_update_checker=Activar comprobador de actualizaciones -enable_update_checker_helper=Comprueba el lanzamiento de nuevas versiones periódicamente en gitea.io. env_config_keys=Configuración del entorno env_config_keys_prompt=Las siguientes variables de entorno también se aplicarán a su archivo de configuración: allow_dots_in_usernames = Permite utilizar puntos en los nombres de usuario. No tiene efecto sobre cuentas existentes. @@ -3059,7 +3058,7 @@ auths.tip.google_plus=Obtener credenciales de cliente OAuth2 desde la consola AP auths.tip.openid_connect=Use el OpenID Connect Discovery URL (/.well-known/openid-configuration) para especificar los puntos finales auths.tip.twitter=Ir a https://dev.twitter.com/apps, crear una aplicación y asegurarse de que la opción "Permitir que esta aplicación sea usada para iniciar sesión con Twitter" está activada auths.tip.discord=Registrar una nueva aplicación en https://discordapp.com/developers/applications/me -auths.tip.gitea=Registrar una nueva aplicación OAuth2. Puede encontrar la guía en https://docs.gitea.com/development/oauth2-provider +auths.tip.gitea=Registrar una nueva aplicación OAuth2. Puede encontrar la guía en https://forgejo.org/docs/latest/user/oauth2-provider auths.tip.yandex=`Crear una nueva aplicación en https://oauth.yandex.com/client/new. Seleccione los siguientes permisos del "Yandex.Passport API": "Access to email address", "Access to user avatar" y "Access to username, first name and surname, gender"` auths.tip.mastodon=Introduzca una URL de instancia personalizada para la instancia mastodon con la que desea autenticarse (o utilice la predeterminada) auths.edit=Editar origen de autenticación diff --git a/options/locale/locale_fil.ini b/options/locale/locale_fil.ini index a6421b26ec..68dbab40a4 100644 --- a/options/locale/locale_fil.ini +++ b/options/locale/locale_fil.ini @@ -7,7 +7,7 @@ language = Wika mirrors = Mga Mirror forks = Mga Fork activities = Mga Aktibidad -pull_requests = Mga hiling sa aghatak +pull_requests = Mga hiling sa paghatak issues = Mga Isyu milestones = Mga Milestone ok = OK @@ -77,13 +77,13 @@ webauthn_error_unable_to_process = Hindi maproseso ng server ang iyong hiling. webauthn_error_duplicated = Hindi pinapayagan ang security key na ito para sa hiling na ito. Mangyaring siguraduhin na ang key na ito ay hindi ito nakarehistro na. webauthn_error_empty = Kailangan mong maglapat ng pangalan para sa key na ito. webauthn_reload = I-reload -repository = Repository +repository = Repositoryo organization = Organisasyon mirror = Salamin -new_repo = Bagong repository -new_migrate = Bagong migration +new_repo = Bagong repositoryo +new_migrate = Bagong paglipat new_mirror = Bagong salamin -new_fork = Bagong repository fork +new_fork = Bagong repositoryong fork new_org = Bagong organisasyon new_project = Bagong proyekto new_project_column = Bagong column @@ -91,7 +91,7 @@ admin_panel = Pangangasiwa ng site account_settings = Mga setting ng Account settings = Mga Setting your_profile = Profile -your_starred = Naka-bitwin +your_starred = Naka-bituin your_settings = Mga Setting all = Lahat go_back = Bumalik @@ -104,7 +104,7 @@ artifacts = Mga Artifact archived = Naka-archive concept_system_global = Global concept_user_individual = Indibidwal -concept_code_repository = Repository +concept_code_repository = Repositoryo concept_user_organization = Organisasyon show_timestamps = Ipakita ang mga timestamp show_log_seconds = Ipakita ang segundo @@ -113,7 +113,7 @@ download_logs = I-download ang mga log name = Pangalan value = Value filter = I-filter -filter.clear = I-clear ang Filter +filter.clear = I-clear ang mga filter filter.is_archived = Naka-archive filter.not_archived = Hindi naka-archive filter.is_fork = Naka-fork @@ -121,13 +121,13 @@ filter.not_fork = Hindi naka-fork filter.is_mirror = Naka-mirror filter.not_mirror = Hindi naka-mirror filter.is_template = Template -filter.not_template = Hindi Template +filter.not_template = Hindi template filter.public = Publiko filter.private = Pribado notifications = Mga Abiso active_stopwatch = Aktibong Tagasubaybay ng Oras locked = Naka-kandado -preview = I-preview +preview = Paunang tingnan confirm_delete_artifact = Sigurado ka bang gusto mong burahin ang artifact na "%s"? rerun_all = Patakbuhin muli ang lahat ng mga trabaho add_all = Idagdag lahat @@ -146,14 +146,14 @@ search_repos = Maghanap ng Repository… switch_dashboard_context = Palitan ang Dashboard Context show_only_unarchived = Pinapakita lang ang hindi naka-archive password_holder = Password -my_repos = Mga Repository -show_more_repos = Magpakita ng higit pang repository… -collaborative_repos = Mga Collaboritive Repository +my_repos = Mga Repositoryo +show_more_repos = Magpakita ng higit pang mga repositoryo… +collaborative_repos = Mga Collaboritive na Repositoryo my_orgs = Mga Organisasyon my_mirrors = Aking Mga Mirror view_home = Itignan ang %s filter = Iba pang Mga Filter -filter_by_team_repositories = I-filter sa mga team repository +filter_by_team_repositories = I-filter sa mga repositoryo ng koponan feed_of = Feed ng "%s" show_archived = Naka-archive show_both_archived_unarchived = Pinapakita ang naka-archive at hindi naka-archive @@ -162,13 +162,13 @@ show_private = Pribado show_both_private_public = Pinapakita ang publiko at pribado show_only_private = Pinapakita lang ang pribado show_only_public = Pinapakita lang ang publiko -issues.in_your_repos = Sa iyong mga repository +issues.in_your_repos = Sa iyong mga repositoryo uname_holder = Username o Email address [explore] organizations = Mga Organisasyon search.fuzzy.tooltip = Isali ang mga resulta na tumutugma sa search term nang malapit -repos = Mga Repository +repos = Mga Repositoryo users = Mga User search = Maghanap go_to = Pumunta sa @@ -182,14 +182,14 @@ user_no_results = Walang tugmang user na nahanap. org_no_results = Walang tugmang mga organisasyon na nahanap. code_search_results = Mga resulta ng paghahanap para sa "%s" code_last_indexed_at = Huling na-index %s -relevant_repositories_tooltip = Mga repository na isang fork o walang topic, icon, at deskripsyon ay nakatago. +relevant_repositories_tooltip = Mga repositoryo na isang fork o walang topic, icon, at deskripsyon ay nakatago. code_search_unavailable = Kasalukuyang hindi available ang code search. Mangyaring makipag-ugnayan sa site administrator. code_no_results = Walang source code na tumutugma sa iyong search term na nahanap. -relevant_repositories = Ang mga kaugnay na repository ay pinapakita, ipakita ang hindi naka-filter na resulta. +relevant_repositories = Ang mga kaugnay na repositoryo ay pinapakita, ipakita ang hindi naka-filter na resulta. stars_few = %d mga star -forks_one = %d fork +forks_one = %d tinidor forks_few = %d mga fork -stars_one = %d star +stars_one = %d bituin [aria] footer.software = Tungkol sa Software @@ -221,23 +221,23 @@ db_name = Pangalan ng database db_schema = Schema db_schema_helper = Iwanang walang laman para sa default ng database ("public"). ssl_mode = SSL -path = Path +path = Daanan sqlite_helper = File path para sa SQLite3 database.
    Maglagay ng absolute path kapag tinatakbo mo ang Forgejo bilang serbisyo. reinstall_confirm_check_3 = Kinukumprima mo na sigurado ka talaga na ang Forgejo na ito ay tumatakbo sa tamang app.ini na lokasyon at sigurado ka na kailangan mo mag-reinstall. Kinukumpirma mo na kilalanin ang mga panganib sa itaas. err_empty_db_path = Hindi maaring walang laman ang path ng SQLite database. no_admin_and_disable_registration = Hindi mo maaring i-disable ang user self-registration nang hindi gumawa ng isang tagapangasiwa na account. err_empty_admin_password = Hindi maaring walang laman ang password ng tagapangasiwa. err_empty_admin_email = Hindi maaring walang laman ang email ng tagapangasiwa. -err_admin_name_is_reserved = Hindi angkop ang Administrator Username, naka-reserve ang username -err_admin_name_is_invalid = Hindi angkop ang Administrator Username +err_admin_name_is_reserved = Hindi angkop ang Username ng Tagapangasiwa, naka-reserve ang username +err_admin_name_is_invalid = Hindi angkop ang Username ng Tagapangasiwa general_title = Mga General Setting app_name = Pamagat ng instansya app_name_helper = Maari mong ilagay ang pangalan ng iyong kompanya dito. repo_path_helper = Ang mga remote Git repository ay mase-save sa directory na ito. -repo_path = Root path ng Repository +repo_path = Root path ng Repositoryo lfs_path = Root path ng Git LFS run_user = User na tatakbuhin bilang -run_user_helper = Ang operating system username na ang Forgejo ay tatakbo bilang. Tandaan mo na ang user ay kailangang may access sa repository root path. +run_user_helper = Ang operating system username na ang Forgejo ay tatakbo bilang. Tandaan mo na ang user ay kailangang may access sa root path ng repositoryo. domain = Domain ng server domain_helper = Domain o host para sa server na ito. ssh_port = Port ng SSH Server @@ -245,8 +245,8 @@ http_port = HTTP listen port lfs_path_helper = Ang mga file na naka-track sa Git LFS ay ilalagay sa directory na ito. Iwanang walang laman para i-disable. reinstall_confirm_message = Ang pag-install muli na may umiiral na Forgejo database ay maaring magdulot ng mga problema. Sa karamihan ng mga kaso, dapat mong gamitin ang iyong umiiral na "app.ini" para patakbuhin ang Forgejo. Kung alam mo ang ginagawa mo, kumpirmahin ang mga sumusunod: reinstall_confirm_check_1 = Ang data na naka-encrypt sa pamamagitan ng SECRET_KEY sa app.ini ay maaring mawala: baka hindi maka-log in ang mga user gamit ng 2FA/OTP at ang mga mirror ay maaring hindi gumana mg maayos. Sa pamamagitan ng pag-check ng box na ito kinukumpirma mo na ang kasalukuyang app.ini file ay naglalaman ng tamang SECRET_KEY. -reinstall_confirm_check_2 = Ang mga repository at mga setting ay maaring kailangang i-resynchronize. Sa pamamagitan ng pag-check ng box na ito kinukumprima mo na ire-resynchronize mo ang mga hook para sa mga repository at authorized_keys ng mano-mano. Kinukumpirma mo na sisiguraduhin mo na tama ang mga setting ng repository at mirror. -err_admin_name_pattern_not_allowed = Hindi angkop ang administrator username, ang username ay tumutugma sa reserved pattern +reinstall_confirm_check_2 = Ang mga repositoryo at mga setting ay maaring kailangang i-resynchronize. Sa pamamagitan ng pag-check ng box na ito kinukumprima mo na ire-resynchronize mo ang mga hook para sa mga repositoryo at authorized_keys ng mano-mano. Kinukumpirma mo na sisiguraduhin mo na tama ang mga setting ng repositoryo at mirror. +err_admin_name_pattern_not_allowed = Hindi angkop ang username ng tagapangasiwa, ang username ay tumutugma sa reserved pattern ssh_port_helper = Numero ng port na gagamitin ng SSH server. Iwanang walang laman para i-disable ang SSH server. server_service_title = Mga setting ng server at third-party na serbisyo offline_mode = Paganahin ang local mode @@ -278,8 +278,8 @@ openid_signup_popup = I-enable ang OpenID-based na pansariling pagrehistro ng us enable_captcha = I-enable ang CAPTCHA sa pagrehistro enable_captcha_popup = Kailanganin ang CAPTCHA sa pansariling pagrehistro ng user. require_sign_in_view_popup = Limitahan ang access ng pahina sa mga naka-sign in na user. Makikita lang ng mga bisita ang sign-in at pagrehistro na mga pahina. -admin_title = Mga setting ng administrator account -admin_name = Username ng administrator +admin_title = Mga setting ng account ng tagapangasiwa +admin_name = Username ng tagapangasiwa admin_password = Password confirm_password = Kumpirmahin ang password admin_email = Email address @@ -288,19 +288,19 @@ install_btn_confirm = I-install ang Forgejo test_git_failed = Hindi masubukan ang "git" command: %v invalid_db_setting = Hindi angkop ang mga database setting: %v invalid_db_table = Hindi angkop ang database table na "%s": %v -invalid_repo_path = Hindi angkop ang repository root path: %v +invalid_repo_path = Hindi angkop ang root path ng repositoryo: %v invalid_app_data_path = Hindi angkop ang app data path: %v run_user_not_match = Ang "user na tatakbo bilang" na username ay hindi ang kasulukuyang username: %s -> %s internal_token_failed = Nabigong maka-generate ng internal token: %v secret_key_failed = Nabigong maka-generate ng secret key: %v save_config_failed = Nabigong i-save ang configuration: %v -invalid_admin_setting = Hindi angkop ang setting ng administrator account: %v +invalid_admin_setting = Hindi angkop ang setting ng account ng tagapangasiwa: %v invalid_log_root_path = Hindi angkop ang log path: %v default_keep_email_private = Itago ang mga email address bilang default default_keep_email_private_popup = Itago ang mga email address ng mga bagong user account bilang default. default_allow_create_organization_popup = Payagan ang mga bagong user account ng gumawa ng mga organisasyon bilang default. default_enable_timetracking = I-enable ang pagsubaybay ng oras bilang default -default_enable_timetracking_popup = I-enable ang pagsubaybay ng oras sa mga bagong repository bilang default. +default_enable_timetracking_popup = I-enable ang pagsubaybay ng oras sa mga bagong repositoryo bilang default. allow_dots_in_usernames = Payagan ang mga user na gamitin ang mga tuldok sa kanilang username. Hindi inaapektuhan ang mga umiiral na account. no_reply_address = Domain ng nakatagong email no_reply_address_helper = Domain name para sa mga user na may nakatagong email address. Halimbawa, ang username na "kita" ay mala-log sa Git bilang "kita@noreply.example.org" kapag ang nakatagong email domain ay nakatakda sa "noreply.example.org". @@ -315,10 +315,9 @@ require_sign_in_view = Kailanganin ang pag-sign in para tignan ang nilalaman ng enable_update_checker_helper_forgejo = Pansamantalang susuriin ito para sa mga bagong bersyon ng Forgejo sa pamamagitan ng pagsuri sa isang tala ng TXT DNS sa release.forgejo.org. sqlite3_not_available = Ang itong bersyon ng Forgejo ay hindi sinusuportahan ang SQLite3. Paki-download ang opisyal na bersyon ng binary sa %s (hindi ang "gobuild" na bersyon). default_allow_create_organization = Payagan ang paggawa ng mga organisasyon bilang default -disable_registration_popup = I-disable ang pansariling pagrehistro ng user. Ang mga administrator lamang ang makakagawa ng mga bagong user account. +disable_registration_popup = I-disable ang pansariling pagrehistro ng user. Ang mga tagapangasiwa lamang ang makakagawa ng mga bagong user account. disable_gravatar_popup = I-disable ang Gravatar at mga third-party na avatar source. Ang isang default na avatar ay gagamitin maliban kung maga-upload ng avatar ang user. -admin_setting_desc = Ang paggawa ng administrator account ay opsyonal. Ang pinakaunang nakarehistro na user ay awtomatikong magiging administrator. -enable_update_checker_helper = Tinitingnan para sa mga pagpalabas ng bagong bersyon sa pamamagitan ng pagkonekta sa gitea.io +admin_setting_desc = Ang paggawa ng administrator account ay opsyonal. Ang pinakaunang nakarehistro na user ay awtomatikong magiging tagapangasiwa. [heatmap] number_of_contributions_in_the_last_12_months = %s mga kontribusyon sa nakalipas na 12 buwan @@ -365,7 +364,7 @@ license_desc = Kunin ang %s: -repo.collaborator.added.text = Dinagdag ka bilang tagaambag sa repository: +repo.collaborator.added.text = Dinagdag ka bilang tagaambag sa repositoryo: activate_email.title = %s, paki-verify ang iyong email address issue.action.reject = Humingi ng mga pagbabago si @%[1]s sa pull request na ito. activate_account.title = %s, paki-activate ang iyong account register_notify.text_3 = Kung ginawa ng ibang tao ang account na ito para sa iyo, kailangan mong itakda ang iyong password muna. -issue_assigned.pull = Itinalaga ka ni @%[1]s sa pull request na %[2]s sa repository na %[3]s. +issue_assigned.pull = Itinalaga ka ni @%[1]s sa pull request na %[2]s sa repositoryo na %[3]s. issue.action.push_1 = Nag-push si @%[1]s ng %[3]d commit sa %[2]s issue.action.ready_for_review = Minarkahan ni @%[1]s ang pull request na ito bilang handa para suriin. release.new.text = Inilabas ni @%[1]s ang %[2]s sa %[3]s -repo.transfer.subject_to = Gusto ni %s na ilipat ang repository na "%s" sa %s +repo.transfer.subject_to = Gusto ni %s na ilipat ang repositoryo na "%s" sa %s team_invite.text_3 = Tandaan: Ang imbitasyong ito ay inilaan para sa %[1]s. Kung hindi mo inaasahan ang imbitasyong ito, maaari mong balewalain ang email na ito. [modal] @@ -506,7 +505,7 @@ modify = Baguhin [form] UserName = Username -RepoName = Pangalan ng repository +RepoName = Pangalan ng repositoryo Email = Email address Password = Password Retype = Kumpirmahin ang password @@ -521,7 +520,7 @@ CommitMessage = Mensahe ng commit CommitChoice = Pagpili ng commit TreeName = Path ng file Content = Nilalaman -SSPISeparatorReplacement = Separator +SSPISeparatorReplacement = Pang-hiwalay SSPIDefaultLanguage = Default na Wika CommitSummary = Pangkalahatang-ideya ng commit glob_pattern_error = ` hindi angkop ang glob pattern: %s` @@ -541,21 +540,21 @@ lang_select_error = Pumili ng wika sa listahan. username_been_taken = Kinuha na ang username. username_change_not_local_user = Ang mga hindi lokal na user ay hindi pinapayagang palitan ang kanilang username. username_has_not_been_changed = Hindi pinalitan ang username -repo_name_been_taken = Ginamit na ang pangalan ng repository. -repository_force_private = Naka-enable ang Force Private: hindi maaaring gawing pampubliko ang mga pribadong repository. -repository_files_already_exist = Ang mga file ay umiiral na sa repository na ito. Makipag-ugnayan sa system administrator. -repository_files_already_exist.delete = Ang mga file ay umiiral na sa repository na ito. Kailangan mo silang burahin. -repository_files_already_exist.adopt_or_delete = Umiiral na ang mga file para sa repository na ito. Alinman pagtibayin sila o burahin sila. +repo_name_been_taken = Ginamit na ang pangalan ng repositoryo. +repository_force_private = Naka-enable ang Force Private: hindi maaaring gawing pampubliko ang mga pribadong repositoryo. +repository_files_already_exist = Ang mga file ay umiiral na sa repositoryo na ito. Makipag-ugnayan sa tagapangaiswa ng sistema. +repository_files_already_exist.delete = Ang mga file ay umiiral na sa repositoryo na ito. Kailangan mo silang burahin. +repository_files_already_exist.adopt_or_delete = Umiiral na ang mga file para sa repositoryo na ito. Alinman pagtibayin sila o burahin sila. username_error_no_dots = ` maaari lamang maglaman ng mga alphanumeric na character ("0-9","a-z","A-Z"), gitling ("-") at underscore ("_"). Hindi ito maaaring magsimula o magtatapos sa mga hindi alphanumeric na character, at ipinagbabawal din ang magkakasunod na non-alphanumeric na character.` include_error = ` dapat maglaman ng substring na "%s".` regex_pattern_error = ` hindi angkop ang regex pattern: %s.` url_error = ` hindi angkop na URL ang "%s".` username_error = ` maaari lamang maglaman ng mga alphanumeric na character ("0-9","a-z","A-Z"), gitling ("-"), underscore ("_") at tuldok ("."). Hindi ito maaaring magsimula o magtatapos sa mga hindi alphanumeric na character, at ipinagbabawal din ang magkakasunod na non-alphanumeric na character.` -repository_files_already_exist.adopt = Umiiral na ang mga file para sa repository na ito at maaari lamang Pagtibayin. +repository_files_already_exist.adopt = Umiiral na ang mga file para sa repositoryo na ito at maaari lamang Pagtibayin. 2fa_auth_required = Kinailangan ng remote visit ng two factors authentication. org_name_been_taken = Kinuha na ang pangalan ng organisasyon. team_name_been_taken = Kinuha na ang pangalan ng koponan. -team_no_units_error = Payagan ang access sa kahit isang seksyon ng repository. +team_no_units_error = Payagan ang access sa kahit isang seksyon ng repositoryo. email_been_used = Ginamit na ang email address. email_invalid = Hindi angkop ang email address. openid_been_used = KInuha na ang OpenID address na "%s". @@ -565,7 +564,7 @@ password_lowercase_one = Kahit isang lowercase na character password_uppercase_one = Kahit isang uppercase na character password_digit_one = Kahit isang numero password_special_one = Kahit isang espesyal na character (bantas, mga bracket, mga quote, atbp.) -enterred_invalid_repo_name = Mali ang inalagay mong pangalan ng repository. +enterred_invalid_repo_name = Mali ang inalagay mong pangalan ng repositoryo. enterred_invalid_org_name = Mali ang inalagay mong pangalan ng organisasyon. enterred_invalid_owner_name = Hindi angkop ang pangalan ng bagong owner. enterred_invalid_password = Mali ang password na inilagay mo. @@ -583,45 +582,50 @@ invalid_ssh_principal = Hindi angkop ang principal: %s must_use_public_key = Ang key na ibinigay mo ay isang pribadong key. Huwag mong i-upload ang iyong pribadong key kahit saan. Gamitin ang iyong pampublikong key sa halip. unable_verify_ssh_key = Hindi ma-verify ang SSH key, tiyakin ulit para sa mga pagkakamali. auth_failed = Nabigo ang autentikasyon: %v -still_own_repo = Ang iyong account ay nagmamay-ari ng isa o higit pang mga repository, burahin o ilipat sila muna. +still_own_repo = Ang iyong account ay nagmamay-ari ng isa o higit pang mga repositoryo, burahin o ilipat sila muna. still_has_org = Ang iyong account ay isang miyembro ng isa o higit pang organisasyon, alisin muna sila. still_own_packages = Ang iyong account ay nagmamay-ari ng isa o higit pang package, burahin sila muna. -org_still_own_repo = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga repository, burahin o ilipat sila muna. +org_still_own_repo = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga repositoryo, burahin o ilipat sila muna. org_still_own_packages = Ang organisasyon na ito ay nagmamay-ari ng isa o higit pang mga package, burahin sila muna. target_branch_not_exist = Hindi umiiral ang target branch. admin_cannot_delete_self = Hindi mo maaring burahin ang sarili mo kapag isa kang tagapangasiwa. Paki-tanggal ang iyong pribilehiyong tagapangasiwa muna. +required_prefix = Ang input ay dapat magsimula sa "%s" +FullName = Buong pangalan +Description = Paglalarawan [user] joined_on = Sumali noong %s -repositories = Mga Repository +repositories = Mga Repositoryo activity = Pampublikong aktibidad followers_few = %d Mga tagasunod block_user = I-block ang user change_avatar = Palitan ang iyong avatar… block_user.detail = Pakiunawa na kung i-block mo ang user na ito, isasagawa ang iba pang mga aksyon. Gaya ng: block_user.detail_1 = Ina-unfollow ka sa user na ito. -block_user.detail_2 = Ang user na ito ay hindi maaaring makipag-ugnayan sa iyong mga repository, ginawang isyu at komento. +block_user.detail_2 = Ang user na ito ay hindi maaaring makipag-ugnayan sa iyong mga repositoryo, ginawang isyu at komento. block_user.detail_3 = Hindi ka maaaring idagdag ng user na ito bilang isang collaborator, at hindi mo rin sila maidaragdag bilang isang collaborator. follow_blocked_user = Hindi mo mapa-follow ang user na ito dahil na-block mo ang user na ito o na-block ka ng user na ito. -starred = Mga naka-star na repository -watched = Mga sinusubaybayan na repository +starred = Mga naka-bituin na repositoryo +watched = Mga sinusubaybayan na repositoryo code = Code projects = Mga Proyekto overview = Pangkalahatang Ideya following_few = %d Sinusundan follow = Sundan unfollow = I-unfollow -block = I-block +block = Harangan unblock = I-unblock user_bio = Byograpya email_visibility.limited = Ang iyong email address ay makikita ng lahat ng mga naka-authenticate na user -email_visibility.private = Makikita mo lang at mga administrator ang iyong email address +email_visibility.private = Makikita mo lang at mga tagapangasiwa ang iyong email address show_on_map = Ipakita ang lugar na ito sa mapa settings = Mga setting ng user form.name_pattern_not_allowed = Ang pattern na "%s" ay hindi pinapayagan sa username. disabled_public_activity = Hindi pinagana ng user na ito ang publikong kakayahang pagpakita ng aktibidad. form.name_reserved = Nakareserba ang username na "%s". form.name_chars_not_allowed = Naglalaman ng mga hindi angkop na character ang username. +followers_one = %d tagasunod +following_one = %d sinusundan [settings] profile = Profile @@ -633,7 +637,7 @@ avatar = Avatar ssh_gpg_keys = Mga SSH / GPG key applications = Mga Aplikasyon orgs = Pamahalaan ng mga organisasyon -repos = Mga Repository +repos = Mga Repositoryo delete = Burahin ang Account twofa = Authentikasyong two-factor (TOTP) account_link = Mga Naka-link na Account @@ -757,8 +761,8 @@ email_preference_set_success = Matagumpay na tinakda ang kagustuhan ng email. add_openid_success = Dinagdag na ang bagong OpenID address. keep_email_private = Itago ang email address openid_desc = Hinahayaan ka ng OpenID na mag-delegate ng pagpapatunay sa isang panlabas na tagabigay ng serbisyo. -ssh_desc = Ang mga pampublikong SSH key na ito ay nauugnay sa iyong account. Pinapayagan ng kaukulang pribadong key ang buong pag-access sa iyong mga repository. Ang mga SSH key na na-verify ay maaaring magamit upang mapatunayan ang mga naka-sign na Git commit sa pamamagitan ng SSH. -principal_desc = Ang mga SSH principal na ito ay nauugnay sa iyong account at pinapayagan ang buong pag-access sa iyong mga repository. +ssh_desc = Ang mga pampublikong SSH key na ito ay nauugnay sa iyong account. Pinapayagan ng kaukulang pribadong key ang buong pag-access sa iyong mga repositoryo. Ang mga SSH key na na-verify ay maaaring magamit upang mapatunayan ang mga naka-sign na Git commit sa pamamagitan ng SSH. +principal_desc = Ang mga SSH principal na ito ay nauugnay sa iyong account at pinapayagan ang buong pag-access sa iyong mga repositoryo. ssh_helper = Kailangan ng tulong? Tignan ang guide sa paggawa ng sarili mong mga SSH key o ilutas ang mga karaniwang problema na maaring moong matagpo gamit ng SSH. gpg_helper = Kailangan ng tulong? Tignan ang guide tungkol sa GPG. add_new_key = Magdagdag ng SSH key @@ -829,11 +833,11 @@ access_token_deletion = Burahin ang access token access_token_deletion_cancel_action = Kanselahin access_token_deletion_confirm_action = Burahin access_token_deletion_desc = Ang pagbura ng isang token ay babawiin ang pag-access sa iyong account para sa mga application gamit ito. Ang gawaing ito ay hindi pwedeng baguhin. Magpatuloy? -repo_and_org_access = Access sa Repository at Organisasyon +repo_and_org_access = Access sa Repositoryo at Organisasyon permissions_public_only = Publiko lamang permissions_access_all = Lahat (publiko, pribado, at limitado) select_permissions = Pumili ng mga pahintulot -permission_no_access = Walang Access +permission_no_access = Walang access permission_read = Basahin permission_write = Pagbasa at pagbago at_least_one_permission = Kailangan mong pumili ng kahit isang pahintulot para gumawa ng token @@ -859,8 +863,8 @@ oauth2_application_name = Pangalan ng application oauth2_redirect_uris = Mga Redirect URI. Pakigamit ng bagong linya para sa bawat URI. save_application = I-save oauth2_client_id = ID ng Kliyente -oauth2_client_secret = Sikreto ng Kliyente -oauth2_regenerate_secret = I-regenerate ang Sikreto +oauth2_client_secret = Sikreto ng kliyente +oauth2_regenerate_secret = I-regenerate ang sikreto oauth2_regenerate_secret_hint = Nawala mo ang iyong sikreto? oauth2_client_secret_hint = Ang sikreto ay hindi ipapakita muli pagkatapos umalis ka o i-refresh ang page na ito. Mangyaring siguraduhin na na-save mo iyan. oauth2_application_edit = I-edit @@ -872,7 +876,7 @@ twofa_scratch_token_regenerate = I-regenerate ang isang-beses na paggamit na rec twofa_enroll = Mag-enroll sa authentikasyong two-factor twofa_disable_note = Maari mong i-disable ang authentikasyong two-factor kapag kinakailangan. twofa_disabled = Na-disable na ang authentikasyong two-factor. -scan_this_image = I-scah ang image na ito gamit ng iyong aplikasyong pang-authentikasyon: +scan_this_image = I-scan ang image na ito gamit ng iyong aplikasyong pang-authentikasyon: or_enter_secret = O ilagay ang sikreto: %s then_enter_passcode = At ilagay ang passcode na pinapakita sa aplikasyon: passcode_invalid = Mali ang passcode. Subukan muli. @@ -890,12 +894,12 @@ visibility = Visibility ng user visibility.public = Publiko email_notifications.enable = I-enable ang mga email notification email_notifications.onmention = Mag-email lamang kapag nabanggit -repos_none = Hindi ka nagmamay-ari ng anumang mga repository. +repos_none = Hindi ka nagmamay-ari ng anumang mga repositoryo. blocked_users_none = Walang mga na-block na user. authorized_oauth2_applications = Mga pinahintulutang OAuth2 application authorized_oauth2_applications_description = Pinayagan mo ang pag-access ng iyong personal na Forgejo account sa mga third-party na application na ito. Mangyaring bawiin ang access para sa mga application na hindi mo na ginagamit. revoke_key = Bawiin -revoke_oauth2_grant = Bawiin ang Access +revoke_oauth2_grant = Bawiin ang access revoke_oauth2_grant_description = Ang pagbawi ng access para sa third party application na ito ay mapipigilang ma-access ng application ang iyong data. Sigurado ka ba? revoke_oauth2_grant_success = Matagumpay na binawi ang pag-access. webauthn_nickname = Palayaw @@ -918,46 +922,49 @@ remove_account_link_success = Tinanggal na ang naka-link na account. visibility.limited_tooltip = Makikita lamang ng mga naka-authenticate na user webauthn_delete_key_desc = Kapag magtanggal ka ng security key hindi ka na makaka-sign in gamit niyan. Magpatuloy? manage_account_links_desc = Ang mga panlabas na account na ito ay naka-link sa iyong Forgejo account. -hooks.desc = Magdagdag mg mga webhook na mati-trigger para sa lahat ng mga repository na minamay-ari mo. +hooks.desc = Magdagdag mg mga webhook na mati-trigger para sa lahat ng mga repositoryo na minamay-ari mo. orgs_none = Hindi ka isang miyembro ng anumang mga organisasyon. oauth2_application_create_description = Ang mga OAuth2 application ay pinapayagan ang mga third-party na aplikasyon na i-access ang mga user account sa instansya na ito. oauth2_application_locked = Ang Forgejo ay pini-pre register ang ibang mga OAuth2 application sa startup kapag naka-enable sa config. Para iwasan ang hindi inaasahang gawain, hindi ito maaring i-edit o tanggalin. Mangyaring sumangguni sa dokumentasyon ng OAuth2 para sa karagdagang impormasyon. remove_account_link_desc = Ang pagtanggal ng naka-link na account ay babawiin ang pag-access nito sa iyong Forgejo account. Magpatuloy? visibility.public_tooltip = Makikita ng lahat hints = Mga Pahiwatig -additional_repo_units_hint_description = Mag-display ng "Magdagdag pa ng mga unit..." na button para sa mga repository na hindi naka-enable ang lahat ng mga available na unit. -additional_repo_units_hint = Hikayatin ang pag-enable ng karagdagang mga repository unit +additional_repo_units_hint_description = Magpakita ng "Magdagdag pa ng mga unit..." na button para sa mga repositoryo na hindi naka-enable ang lahat ng mga available na unit. +additional_repo_units_hint = Hikayatin ang pag-enable ng karagdagang mga unit ng repositoryo update_hints = I-update ang mga pahiwatig update_hints_success = Na-update na ang mga pahiwatig. +pronouns_custom = Pasadya +pronouns_unspecified = Hindi natakda +pronouns = Mga panghalip [repo] -template_description = Ang mga template repository ay pinapayagan ang mga gumagamit na mag-generate ng mga bagong repository na may magkatulad na istraktura ng direktoryo, mga file, at opsyonal na mga setting. +template_description = Ang mga template na repositoryo ay pinapayagan ang mga gumagamit na mag-generate ng mga bagong repositoryo na may magkatulad na istraktura ng direktoryo, mga file, at opsyonal na mga setting. clone_helper = Kailangan ng tulong sa pagpili? Bisitahin ang Tulong. -admin.failed_to_replace_flags = Nabigong palitan ang mga repository flag +admin.failed_to_replace_flags = Nabigong palitan ang mga flag ng repositoryo rss.must_be_on_branch = Kailangang nasa branch ka para magkaroon ng RSS feed. -new_repo_helper = Ang isang repository ay naglalaman ng lahat ng file ng proyekto, kasama ang kasaysayan ng rebisyon. Nagho-host ka na sa ibang lugar? Mag-migrate ng repository +new_repo_helper = Ang isang repositoryo ay naglalaman ng lahat ng file ng proyekto, kasama ang kasaysayan ng rebisyon. Nagho-host ka na sa ibang lugar? Mag-migrate ng repositoryo admin.manage_flags = Ipamahala ang mga flag -admin.enabled_flags = Mga flag na naka-enable para sa repository: +admin.enabled_flags = Mga flag na naka-enable para sa repositoryo: admin.update_flags = I-update ang mga flag -admin.flags_replaced = Napalitan ang mga repository flag +admin.flags_replaced = Napalitan ang mga flag ng repositoryo owner = May-ari -owner_helper = Maaring hindi mapapakita ang ibang organisasyon sa dropdown dahil sa pinakamataas na bilang ng repository na limit. -repo_name = Pangalan ng repository -repo_name_helper = Ang mga magandang pangalan ng repository ay gumagamit ng maliit, makakaalala, at unique na mga keyword. -repo_size = Laki ng Repository +owner_helper = Maaring hindi mapapakita ang ibang organisasyon sa dropdown dahil sa pinakamataas na bilang ng repositoryo na limitasyon. +repo_name = Pangalan ng repositoryo +repo_name_helper = Ang mga magandang pangalan ng repositoryo ay gumagamit ng maliit, makakaalala, at unique na mga keyword. +repo_size = Laki ng Repositoryo template = Template template_select = Pumili ng template. -template_helper = Gawing template ang repository -visibility = Visibility +template_helper = Gawing template ang repositoryo +visibility = Kakayahang pagpakita visibility_description = Ang owner o ang mga miyembro ng organisasyon kung may karapatan sila, ay makakakita nito. -visibility_helper = Gawing pribado ang repository -visibility_helper_forced = Ang iyong tagapangasiwa ng site ay pinipilit na pribado ang mga bagong repository. +visibility_helper = Gawing pribado ang repositoryo +visibility_helper_forced = Ang iyong tagapangasiwa ng site ay pinipilit na pribado ang mga bagong repositoryo. visibility_fork_helper = (Ang pag-palit nito ay maapektuhan ang visibility ng lahat ng mga fork.) -fork_repo = I-fork ang repository +fork_repo = I-fork ang repositoryo fork_from = I-fork mula sa already_forked = Na-fork mo na ang %s fork_to_different_account = Mag-fork sa ibang account -fork_visibility_helper = Ang visibility ng isang naka-fork na repository ay hindi maaring baguhin. +fork_visibility_helper = Ang visibility ng isang naka-fork na repositoryo ay hindi maaring baguhin. open_with_editor = Buksan gamit ang %s download_bundle = I-download ang BUNDLE repo_gitignore_helper_desc = Piliin kung anong mga file na hindi susubaybayin sa listahan ng mga template para sa mga karaniwang wika. Ang mga tipikal na artifact na ginagawa ng mga build tool ng wika ay kasama sa .gitignore ng default. @@ -967,8 +974,8 @@ readme_helper_desc = Ito ang lugar kung saan makakasulat ka ng kumpletong deskri trust_model_helper_collaborator_committer = Katulong+Committer: I-trust ang mga signature batay sa mga katulong na tumutugma sa committer mirror_interval = Interval ng mirror (ang mga wastong unit ng oras ay "h", "m", "s"). 0 para i-disable ang periodic sync. (Pinakamababang interval: %s) transfer.reject_desc = Kanselahin ang pag-transfer mula sa "%s" -mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang matukoy ang LFS server. Maari ka rin tumukoy ng isang custom na endpoint kapag ang repository LFS data ay nilalagay sa ibang lugar. -adopt_search = Ilagay ang username para maghanap ng mga unadopted repository... (iwanang walang laman para hanapin lahat) +mirror_lfs_endpoint_desc = Ang sync ay susubukang gamitin ang clone url upang matukoy ang LFS server. Maari ka rin tumukoy ng isang custom na endpoint kapag ang LFS data ng repositoryo ay nilalagay sa ibang lugar. +adopt_search = Ilagay ang username para maghanap ng mga unadopted na repositoryo... (iwanang walang laman para hanapin lahat) object_format = Format ng object readme_helper = Pumili ng README file template. default_branch_helper = Ang default branch ay ang base branch para sa mga pull request at mga commit ng code. @@ -982,27 +989,27 @@ desc.sha256 = SHA256 template.items = Mga template item template.git_content = Nilalaman ng Git (Default na branch) reactions_more = at %d pa -unit_disabled = Na-disable ng tagapangasiwa ng site ang itong seksyon ng repository. -create_repo = Gumawa ng Repository +unit_disabled = Na-disable ng tagapangasiwa ng site ang itong seksyon ng repositoryo. +create_repo = Gumawa ng Repositoryo generate_from = I-generate mula sa repo_desc = Deskripsyon fork_branch = Branch na mako-clone sa fork all_branches = Lahat ng mga branch -fork_no_valid_owners = Hindi mapo-fork ang repository dahil walang mga wastong may-ari. +fork_no_valid_owners = Hindi mapo-fork ang repositoryo dahil walang mga wastong may-ari. use_template = Gamitin ang template na ito download_zip = I-download ang ZIP download_tar = I-download ang TAR.GZ issue_labels = Mga label ng isyu -generate_repo = I-generate ang repository +generate_repo = I-generate ang repositoryo repo_desc_helper = Maglagay ng maikling deskripsyon (opsyonal) repo_lang = Wika issue_labels_helper = Pumili ng label set ng isyu. license = Lisensya license_helper = Pumili ng file ng lisensya. license_helper_desc = Ang lisensya ay namamahala kung ano ang pwede at hindi pwedeng gawin ng mga ibang tao sa iyong code. Hindi sigurado kung alin ang wasto para sa iyong proyekto? Tignan ang Pumili ng lisensya. -object_format_helper = Object format ng repository. Hindi mababago mamaya. Ang SHA1 ang pinaka-compatible. +object_format_helper = Object format ng repositoryo. Hindi mababago mamaya. Ang SHA1 ang pinaka-compatible. readme = README -auto_init = I-initialize ang repository (Nagdadagdag ng .gitignore, Lisensya, at README) +auto_init = I-initialize ang repositoryo (Nagdadagdag ng .gitignore, Lisensya, at README) trust_model_helper = Pumili ng trust model para sa signature verification. Ang mga posibleng opsyon ay: trust_model_helper_collaborator = Katulong: I-trust ang mga signature batay sa mga katulong trust_model_helper_committer = Commiter: I-trust ang mga signature na tumutugma sa mga commiter @@ -1022,11 +1029,11 @@ mirror_password_blank_placeholder = (Hindi tinakda) mirror_password_help = Palitan ang username para burahin ang na-store na password. watchers = Mga nanonood stargazers = Mga Stargazer -stars_remove_warning = Tatanggalin nito ang lahat ng mga star sa repository na ito. +stars_remove_warning = Tatanggalin nito ang lahat ng mga bitwin sa repositoryo na ito. forks = Mga fork language_other = Iba adopt_preexisting_label = Mag-adopt ng mga file -adopt_preexisting_content = Gumawa ng repository mula sa %s +adopt_preexisting_content = Gumawa ng repositoryo mula sa %s transfer.accept = Tanggapin ang paglipat transfer.accept_desc = Ilipat sa "%s" transfer.reject = Tanggihan ang paglipat @@ -1039,8 +1046,8 @@ desc.internal = Internal template.git_hooks = Mga hook ng Git delete_preexisting_label = Burahin stars = Mga bitwin -migrate_options_mirror_helper = Magiging salamin ang [repositoryong] ito -migrate_options_lfs_endpoint.description.local = Sinusuporta rin ang daanang [local] sa serbiro. +migrate_options_mirror_helper = Magiging salamin ang repositoryong ito +migrate_options_lfs_endpoint.description.local = Sinusuporta rin ang lokal na server path. editor.this_file_locked = Nakakandado ang file editor.filename_cannot_be_empty = Hindi maaring walang laman ang pangalan ng file. commits.message = Mensahe @@ -1049,52 +1056,379 @@ commits.date = Petsa projects.description_placeholder = Paglalarawan projects.close = Isara projects.open = Buksan -editor.preview_changes = Paunang tingnan ang mga pagbago +editor.preview_changes = I-preview ang mga pagbago editor.edit_this_file = Baguhin ang file commits.author = May-akda commits.older = Mas luma editor.add_tmpl = Idagdag ang "" -delete_preexisting = Burahin ang [pre-]umiiral na mga file +delete_preexisting = Burahin ang mga dating umiiral na file delete_preexisting_content = Burahin ang mga file sa %s -tree_path_not_found_commit = Hindi umiiral ang daanang %[1]s sa [commit] %[2]s -tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa [branch] %[2]s -migrate_items_pullrequests = Mga pull request +tree_path_not_found_commit = Hindi umiiral ang path na %[1]s sa commit %[2]s +tree_path_not_found_branch = Hindi umiiral ang daanang %[1]s sa branch %[2]s +migrate_items_pullrequests = Mga hiling sa pagtulak archive.pull.nocomment = Naka-archive ang repo na ito. Hindi ka makakakomento sa mga pull request. archive.title = Naka-archive ang repo na ito. Maari mong itignan ang mga file at i-clone ito, pero hindi makaka-push o magbukas ng mga isyu o mga pull request. -archive.title_date = Naka-archive ang repo na ito noong %s. Maari mong itignan ang mga file at i-clone ito, pero hindi makaka-push o magbukas ng mga isyu o mga pull request. -pulls = Mga kahilingan sa paghatak -activity.merged_prs_count_n = Naisamang mga [pull request] +archive.title_date = Naka-archive ang repositoryo na ito noong %s. Maari mong itignan ang mga file at i-clone ito, pero hindi makaka-push o magbukas ng mga isyu o mga pull request. +pulls = Mga hiling sa paghatak +activity.merged_prs_count_n = Mga naisamang hiling sa paghatak wiki.last_updated = Huling binago %s file.title = %s sa %s -file_view_raw = Tingnan ang hilaw +file_view_raw = Tingnan ng raw editor.new_file = Bagong file editor.edit_file = Baguhin ang file -commit_graph.hide_pr_refs = Itago ang mga kahilingan sa paghatak +commit_graph.hide_pr_refs = Itago ang mga hiling sa paghatak editor.or = o editor.cancel_lower = kanselahin issues.filter_sort.latest = Pinakabago issues.filter_sort.oldest = Pinakaluma issues.filter_sort.recentupdate = Huling nabago -issues.action_open = Nakabukas +issues.action_open = Buksan issues.closed_title = Sarado issues.reopen_issue = Buksang muli pulls.merged = Naisama na -pulls.merged_info_text = Maari nang burahin ang [branch] %s. +pulls.merged_info_text = Maari nang burahin ang branch %s. milestones.update_ago = Binago %s activity.closed_issue_label = Sarado -activity.merged_prs_label = Naisama na +activity.merged_prs_label = Naisama editor.delete_this_file = Burahin ang file editor.file_delete_success = Nabura na ang file na "%s". tree = Puno issues.filter_sort = Isaayos ayon sa -activity.title.issues_closed_from = Naisara ang %s mula sa %s +activity.title.issues_closed_from = Sinara ang %s mula sa %s pulls.merged_success = Matagumpay na naisama at sinara ang [pull request] activity.title.prs_merged_by = Sinama ang %s ni/ng %s -find_tag = Hanapin ang [tag] +find_tag = Maghanap ng tag issues.label.filter_sort.reverse_by_size = Pinakamalaki issues.label.filter_sort.by_size = Pinakamaliit -blame.ignore_revs = [Binabaliwala] ang mga pagbabago sa .git-blame-ignore-revs. Pindutin dito upang [bypass] at ipakita ang [regular] na panigin ng [blame]. +blame.ignore_revs = Hindi pinapansin ang mga pagbabago sa .git-blame-ignore-revs. Pindutin dito upang i-bypass at ipakita ang regular na panigin ng blame. author_search_tooltip = Ipinapakita ang hindi hihigit sa 30 mga tagagamit +template.avatar = Avatar +migrate_options = Mga opsyon sa paglipat +migrate.clone_address_desc = Ang HTTP(S) o Git "clone" URL ng umiiral na repositoryo +need_auth = Awtorisasyon +migrate.github_token_desc = Maari kang maglagay ng isa o higit pang mga token na hinihiwalay ng kuwit dito upang gawing mas-mabilis ang pagmigrate dahil sa rate limit ng GitHub API. BABALA: Ang pagabuso ng feature na ito ay maaring maglabag sa patakaran ng tagapagbigay ng serbisyo at maaring magdulot ng pag-block ng account. +template.invalid = Kailangang pumili ng kahit isang template na repositoryo +migrate_options_lfs_endpoint.description = Susubukan ng migration na gamitin ang iyong Git remote upang matukoy ang LFS server. Maari mong magtiyak ng custom na endpoint kapag ang LFS data ng repositoryo ay nakalagay sa ibang lugar. +blame.ignore_revs.failed = Nabigong hindi pansinin ang mga rebisyon sa .git-blame-ignore-revs. +tree_path_not_found_tag = Hindi umiiral ang path na %[1]s sa tag %[2]s +form.reach_limit_of_creation_n = Naabot na ng may-ari ang limitasyon na %d mga repositoryo. +form.reach_limit_of_creation_1 = Naabot na ng may-ari ang limitasyon na %d repositoryo. +form.name_reserved = Nakareserba ang pangalan ng repositoryo na "%s". +form.name_pattern_not_allowed = Hindi pinapayagan ang pattern na "%s" sa pangalan ng repositoryo. +migrate_options_lfs = I-migrate ang mga LFS file +migrate_options_lfs_endpoint.label = Endpoint ng LFS +migrate_options_lfs_endpoint.placeholder = Kung iwanang blangko, ang endpoint ay magmumula sa clone URL +migrate_items_milestones = Mga milestone +migrate_items_labels = Mga label +migrate_items_issues = Mga isyu +migrate_items_merge_requests = Mga merge request +migrate.clone_address = Magmigrate / Mag-clone mula sa URL +archive.issue.nocomment = Naka-archive ang repo na ito. Hindi ka makakakomento sa mga isyu. +migrate_items = Mga item sa pagmigrate +migrate_items_releases = Mga paglabas +migrate_repo = I-migrate ang repositoryo +size_format = %[1]s: %[2]s, %[3]s: %[4]s +template.git_hooks_tooltip = Kasalukuyang hindi mo mababago o matatanggal ang mga hook ng Git kapag nadagdag. Piliin lang ito kapag pinagkakatiwalaan mo ang template na repositoryo. +template.webhooks = Mga webhook +template.topics = Mga paksa +template.issue_labels = Mga label ng isyu +template.one_item = Kailangang pumili ng kahit isang template item +migrate_items_wiki = Wiki +migrate.clone_local_path = o isang lokal na path ng server +adopt_preexisting_success = Pinagtibay ang mga file at ginawa ang repositoryo mula sa %s +delete_preexisting_success = Burahin ang mga hindi pinatibay na file sa %s +blame_prior = Tignan ang blame bago ang pagbabago na ito +migrate.permission_denied = Hindi ka pinapayagang mag-import ng mga lokal na repositoryo. +migrate.permission_denied_blocked = Hindi ka maaring mag-import mula sa mga hindi pinapayagang host, magyaring magtanong sa pangangasiwa na suriin ang ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS na mga setting. +migrate.invalid_local_path = Hindi wasto ang lokal na path. Hindi ito umiiral o hindi isang direktoryo. +migrate.invalid_lfs_endpoint = Hindi wasto ang LFS endpoint. +migrate.migrating_failed = Nabigo ang pag-migrate mula sa %s. +migrate.migrating_failed_no_addr = Nabigo ang pagmigrate. +migrate.github.description = Magmigrate ng data mula sa github.com o GitHub Enterprise server. +migrate.git.description = Mag-migrate lang ng repositoryo mula sa anumang serbisyo ng Git. +migrate.gitlab.description = Mag-migrate ng data mula sa gitlab.com o iba pang mga GitLab na instansya. +migrate.forgejo.description = Mag-migrate ng data mula sa codeberg.org o iba pang mga Forgejo na instansya. +migrate.gitea.description = Mag-migrate ng data mula sa gitea.com o iba pang mga Gitea na instansya. +migrate.gogs.description = Mag-migrate ng data mula sa notabug.org o iba pang mga Gogs na instansya. +migrate.onedev.description = Mag-migrate ng data mula sa code.onedev.io o iba pang mga OneDev na instansya. +migrate.codebase.description = Mag-migrate ng data mula sa codebasehq.com. +migrate.gitbucket.description = Mag-migrate ng data mula sa mga GitBucket na instansya. +migrate.migrating_topics = Nililipat ang mga paksa +migrate.migrating_milestones = Nililipat ang mga milestone +migrate.migrating_releases = Nililipat ang mga paglabas +migrate.migrating_pulls = Nililipat ang mga pull request +migrate.cancel_migrating_title = Kanselahin ang pag-migrate +migrate.cancel_migrating_confirm = Gusto mo bang kanselahin ang migration na ito? +mirror_from = mirror ng +forked_from = na-fork mula sa +generated_from = na-generate mula sa +fork_guest_user = Mag-sign in para i-fork ang repositoryo na ito. +watch_guest_user = Mag-sign in para panoorin an repositoryo na ito. +star_guest_user = Mag-sign in para i-bitwin ang repositoryo na ito. +watch = Panoorin +unstar = I-unstar +star = I-star +fork = I-fork +download_archive = I-download ang repositoryo +more_operations = Higit pang mga operasyon +no_desc = Walang deskripsyon +push_exist_repo = Pag-push ng umiiral na repositoryo mula sa command line +empty_message = Ang repositoryong ito ay hindi naglalaman ng anumang nilalaman. +code.desc = I-access ang source code, mga file, mga commit, at mga branch. +branch = Branch +branches = Mga branch +tags = Mga tag +org_labels_desc = Mga label sa antas ng organisasyon na maaaring gamitin sa lahat ng mga repositoryo sa ilalim ng organisasyong ito +commit = Commit +release = Release +releases = Mga Release +released_this = nilabas ito +file_raw = Hilaw +file_follow = Sundan ang symlink +file_view_source = Tignan ang source +file_view_rendered = Tignan ng naka-render +ambiguous_runes_header = `Naglalaman ng file na ito ng mga hindi tiyak na Unicode character` +ambiguous_runes_description = `Ang file na ito ay naglalaman ng mga Unicode character na maaring malilito sa ibang mga character. Kung sa tingin mo ay sinasadya ito, maari mong ligtas na hindi pansinin ang babala ito. Gamitin ang I-escape na button para ipakita sila.` +file_copy_permalink = Kopyahin ang permalink +view_git_blame = Tignan ang git blame +video_not_supported_in_browser = Hindi sinusuportahan ng inyong browser ang HTML5 "video" tag. +audio_not_supported_in_browser = Hindi sinusuportahan ng inyong browser ang HTML5 "audio" tag. +stored_lfs = Nilagay gamit ng Git LFS +symbolic_link = Symbolic link +executable_file = Executable na file +generated = Na-generate +commit_graph = Graph ng commit +commit_graph.select = Pumili ng mga branch +commit_graph.monochrome = Mono +commit_graph.color = Kulay +commit.contained_in = Ang commit na ito ay nakalagay sa: +commit.load_referencing_branches_and_tags = I-load ang mga branch at tag na sumasangguni sa commit na ito +blame = Blame +download_file = I-download ang file +normal_view = Normal na Paningin +line = linya +lines = mga linya +from_comment = (komento) +editor.add_file = Magdagdag ng file +editor.upload_file = Mag-upload ng file +editor.cannot_edit_lfs_files = Hindi mababago ang mga LFS file sa web interface. +migrate.migrating_issues = Nililipat ang mga isyu +fork_from_self = Hindi ka makaka-fork ng repositoryo na minamay-ari mo. +broken_message = Ang Git data na pinagbabatayan sa repositoryo na ito ay hindi mabasa. Makipag-ugnayan sa tagapangasiwa ng instansya na ito o burahin ang repositoryo na ito. +file_history = Kasaysayan +invisible_runes_header = `Nalalaman ng file na ito ng mga hindi nakikitang Unicode character` +file_too_large = Masyadong malaki ang file para ipakita. +invisible_runes_description = `Ang file na ito ay naglalaman ng mga hindi nakikitang Unicode character na hindi nakikilala ng mga tao ngunit maaring maproseso ng ibang paraan ng isang computer. Kung sa tingin mo ay sinasadya ito, maari mong ligtas na hindi pansinin ang babala na ito. Gamitin ang I-escape na button para ipakita sila.` +commit.contained_in_default_branch = Ang commit na ito ay bahagi ng default na branch +migrate.migrating_labels = Nililipat ang mga label +filter_branch_and_tag = I-filter ang branch o tag +clear_ref = `Burahin ang kasalukuyang sanggunian` +actions = Mga Aksyon +issues = Mga Isyu +project_board = Mga Proyekto +packages = Mga Package +labels = Mga Label +org_labels_desc_manage = ipamahala +milestones = Mga Milestone +commits = Mga Commit +file_permalink = Permalink +migrate.migrating_failed.error = Nabigong magmigrate: %s +unwatch = I-unwatch +n_commit_one = %s commit +n_commit_few = %s mga commit +n_branch_one = %s branch +n_branch_few = %s mga branch +n_tag_one = %s tag +n_tag_few = %s mga tag +editor.cannot_edit_non_text_files = Hindi mababago ang mga binary file sa web interface. +migrated_from = Na-migrate mula sa %[2]s +migrated_from_fake = Na-migrate mula sa %[1]s +migrate.migrate = Magmigrate mula sa %s +migrate.failed = Nabigo ang migration: %v +migrate.migrate_items_options = Kinakailangan ang token sa pag-access para i-migrate ang mga karagdagang item +migrate.migrating = Nagma-migrate mula sa %s ... +quick_guide = Mabilis na gabay +clone_this_repo = I-clone ang repositoryo na ito +cite_this_repo = Banggitin ang repositoryo na ito +create_new_repo_command = Paggawa ng bagong repositoryo sa command line +code = Code +ambiguous_character = `Ang %[1]c [U+%04[1]X] ay maaring malito sa %[2]c [U+%04[2]X]` +escape_control_characters = I-escape +unescape_control_characters = I-unescape +invisible_runes_line = `Ang linya na ito ay may mga hindi nakikitang unicode character` +ambiguous_runes_line = `Ang linya na ito ay may mga hindi tiyak na unicode character` +tag = Tag +migrate.migrating_git = Nililipat ang Git data +vendored = Naka-vendor +editor.delete = Burahin ang %s +editor.add = Idagdag ang %s +editor.must_be_on_a_branch = Dapat nasa branch ka upang gumawa o magmunkahi ng mga pagbabago sa file na ito. +editor.new_branch_name_desc = Bagong pangalan ng branch… +editor.cancel = Kanselahin +issues.role.member = Miyembro +issues.remove_request_review_block = Hindi maaring tanggalin ang hiling sa pagsuri +issues.edit = Baguhin +issues.cancel = Kanselahin +issues.save = IImbak +issues.label_title = Pangalan +issues.delete.title = Burahin ang isyung ito? +settings.pulls_desc = Paganahin ang mga hiling sa paghatak para sa repositoryo +editor.branch_does_not_exist = Walang natagpuang [branch] na "%s" sa [repository] na ito. +commits.nothing_to_compare = Magkapareho ang mga branch na ito. +commits.search_all = Lahat na mga branch +editor.file_deleting_no_longer_exists = Walang natagpuang binuburang file na "%s" sa repositoryo na ito. +issues.role.owner_helper = May-ari ng repositoryo ang tagagamit na ito. +issues.remove_request_review = Tanggalin ang hiling sa pagsuri +issues.force_push_compare = Ikumpara +editor.propose_file_change = Magmunkahi ng mga pagbabago sa file +issues.author = May-akda +issues.re_request_review = Hilingin muli ang pagsusuri +issues.lock.reason = Dahilan sa pagkandado +issues.action_close = Isara +issues.label_description = Paglalarawan +find_file.go_to_file = "Pumunta sa file" +projects.deletion = Burahin ang proyekto +issues.filter_project_all = Lahat ng mga proyekto +issues.filter_project_none = Walang proyekto +issues.ref_from = `mula %[1]s` +issues.due_date_form_remove = Tanggalin +issues.due_date_form_edit = I-edit +issues.lock_with_reason = kinandado bilang %s at nilimitahan ang paguusap sa mga katulong %s +issues.lock_no_reason = kinandado at nilimitahan ang paguusap sa mga katulong %s +commit.revert = Ibalik +commit.revert-header = Ibalik: %s +projects.title =Pamagat +projects.create_success = Ginawa na ang proyektong "%s". +projects.modify = I-edit ang proyekto +issues.delete_comment_confirm = Gusto mo bang burahin ang komento na ito? +issues.context.edit = I-edit +issues.lock.title = Kandaduhin ang paguusap sa isyung ito. +issues.unlock.title = I-unlock ang paguusap sa isyung ito. +pulls.nothing_to_compare_and_allow_empty_pr = Magkapareho ang mga branch na ito. Magiging walang laman ang PR na ito. +pulls.has_pull_request = +issues.delete = Burahin +issues.choose.open_external_link = Buksan +issues.deleted_project = `(binura)` +issues.self_assign_at = `itinalaga sa sarili ang %s` +issues.filter_poster_no_select = Lahat ng may-akda +issues.filter_type = Uri +issues.filter_type.assigned_to_you = Itinalaga sa iyo +issues.filter_type.all_issues = Lahat ng mga isyu +issues.author_helper = May-akda ang tagagamit na ito. +issues.role.owner = May-ari +activity.title.prs_n = %d mga kahilingan sa paghatak +issues.label_color = Kulay +pulls.nothing_to_compare = Magkapareho ang mga branch na ito. Hindi na kailangang gumawa ng hiling sa paghatak. +projects.column.assigned_to = Itinalaga sa/kay +issues.new_label_desc_placeholder = Paglalarawan +issues.next = Susunod +issues.previous = Nakaraan +issues.filter_project = Proyekto +issues.filter_poster = May-akda +issues.filter_type.review_requested = Hiniling ang pagsuri +issues.filter_type.created_by_you = Ginawa mo +issues.opened_by = binuksan noong %[1]s ng/ni %[3]s +issues.open_title = Nakabukas +issues.filter_type.mentioning_you = Binabanggit sa iyo +issues.filter_type.reviewed_by_you = Sinusuri mo +issues.context.delete = Burahin +issues.no_content = Walang nakalaang paglalarawan. +issues.close = Isara ang isyu +editor.revert = Ibalik ang %s sa: +commits.signed_by = Nilagdaan ni/ng +commits.signed_by_untrusted_user = Nilagdaan ng hindi pinagkakatiwalaan na tagagamit +projects.description = Paglalarawan (opsyonal) +projects.create = Gumawa ng proyekto +projects.new = Bagong proyekto +projects.edit = Baguhin ang proyekto +editor.filename_help = Idagdag ang direktoryo sa pamamagitan ng pag-type ng pangalan at isunod ang slash ("/"). Tanggalin ang direktoryo sa pamamagitan ng pag-type ng backspace sa simula ng field ng pasukan. +editor.new_branch_name = Ipangalan ang bagong branch para sa commit na ito +issues.cancel_tracking = Pagpaliban +issues.new.open_projects = Mga nakabukas na proyekto +issues.review.remove_review_request_self = tinanggihan na suriin ang %s +issues.review.reviewers = Mga tagasuri +editor.signoff_desc = Idagdag ang Signed-off-by trailer ng taga-commit sa dulo ng log message ng commit. +editor.commit_message_desc = Magdagdag ng opsyonal na pinahabang paglalarawan… +issues.new.closed_projects = Mga saradong proyekto +projects = Mga proyekto +projects.deletion_success = Nabura na ang proyekto. +issues.add_time_short = Idagdag ang oras +editor.push_rejected_summary = Buong mensahe ng pagtanggi: +commitstatus.success = Tagumpay +commitstatus.failure = Nabigo +projects.type.none = Wala +issues.label_edit = +issues.deleted_milestone = `(binura)` +issues.add_time_hours = Oras +projects.column.color = Kulay +projects.card_type.images_and_text = Mga larawan at teksto +projects.card_type.text_only = Teksto lamang +issues.create = Gumawa ng isyu +commits.gpg_key_id = ID ng susi ng GPG +editor.no_changes_to_show = Walang maipapakitang pagbabago. +editor.name_your_file = Ipangalan ang iyong file… +pulls.new = Bagong hiling sa paghatak +issues.ref_reopened_from = `binuksang muli ang isyung %[4]s %[2]s` +settings.event_issues_desc = Binuksan, sinara, muling binuksan, o binago ang isyu. +activity.new_issue_label = Nabuksan +activity.merged_prs_count_1 = Naisamang hiling sa paghatak +activity.opened_prs_count_1 = Inimungkahing hiling sa paghatak +activity.opened_prs_label = Inimungkahi +pulls.reopened_at = `nabuksang muli ang hiling sa paghatak na %[2]s` +issues.opened_by_fake = binuksan ang %[1]s ni/ng %[2]s +pulls.reopen_failed.base_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil hindi na umiiral ang base branch. +issues.reopened_at = `binuksang muli ang isyung ito %[2]s` +pulls.reopen_failed.head_branch = Hindi mabuksang muli ang [pull request] na ito dahil nabura ang punong [branch]. +settings.event_pull_request_desc = Binuksan, sinara, muling binuksan, o binago ang hiling sa paghatak. +activity.opened_prs_count_n = Mga inimungkahing hiling sa paghatak +editor.filename_is_invalid = Hindi wasto ang pangalan ng file: "%s". +activity.title.prs_opened_by = %s inimungkahi ni/ng %s +pulls.cant_reopen_deleted_branch = Hindi mabuksang muli ang hiling sa paghatak na ito dahil nabura ang branch. +issues.new = Bagong isyu +issues.commented_at = `iniwan ang komento %s` +editor.patch = Ilapat ang Patch +editor.new_patch = Bagong Patch +editor.create_new_branch = Gumawa ng bagong branch para sa commit na ito at simulan ang hiling sa paghatak. +editor.create_new_branch_np = Gumawa ng bagong branch para sa commit na ito. +editor.invalid_commit_mail = Hindi wastong mail para sa paggawa ng commit. +issues.filter_sort.leastupdate = Unang nabago +editor.fork_before_edit = Kailangan mong i-fork ang repositoryo na ito upang gumawa o magmungkahi ng mga pagbabago sa file na ito. +editor.must_have_write_access = Kailangang may access ka sa pagbabago upang gumawa o magmungkahi ng mga pagbabago sa file na ito. +editor.commit_signed_changes = I-commit ang mga naka-sign na pagbabago +editor.commit_changes = I-commit ang mga pagbabago +editor.update = I-update ang %s +activity.title.issues_created_by = %s ginawa ni/nina %s +settings.event_create_desc = Ginawa na ang branch o tag. +issues.content_history.created = ginawa +editor.patching = Pina-patch: +editor.fail_to_apply_patch = Hindi malapat ang patch na "%s" +settings.danger_zone = Mapanganib na lugar +issues.closed_at = `isinara ang isyung %[2]s` +settings.collaboration.admin = Tagapangasiwa +settings.admin_settings = Mga setting ng tagapangasiwa +issues.start_tracking_history = `sinimulan ang trabaho %s` +milestones.close = Isara +wiki.save_page = IImbak ang pahina +wiki.page = Pahina +wiki.page_content = Nilalaman ng pahina +wiki.new_page = Bagong pahina +wiki.page_title = Pamagat ng pahina +issues.lock_confirm = Kandaduhin +issues.stop_tracking_history = `itinigil ang trabaho %s` +issues.label_delete = Burahin +milestones.closed = Isinara %s +issues.unlock_confirm = I-unlock +milestones.open = Buksan +issues.content_history.delete_from_history = Burahin mula sa kasaysayan +issues.content_history.delete_from_history_confirm = Burahin mula sa kasaysayan? +issues.content_history.options = Mga pagpipilian +wiki.edit_page_button = Baguhin +wiki.new_page_button = Bagong pahina +wiki.delete_page_button = Burahin ang pahina +milestones.title = Pamagat +milestones.desc = paglalarawan +pulls.blocked_by_user = Hindi ka makakagawa ng [pull request] sa [repository] na ito dahil hinarang ka ng may-ari ng [repository]. +pulls.no_merge_access = Hindi ka pinapayagang isali ang [pull request] na ito. [search] commit_kind = Maghanap ng mga commit... @@ -1116,11 +1450,398 @@ project_kind = Maghanap ng mga proyekto... branch_kind = Maghanap ng mga branch... runner_kind = Maghanap ng mga runner... no_results = Walang mga tumutugma na resulta na nahanap. +code_search_by_git_grep = Ang kasalukuyang mga resulta ng paghahanap ng code ay ibinibigay ng "git grep*. Maaring may mga mas magandang resulta kapag na-enable ng tagapangasiwa ng site ang Indexer ng Repositoryo. [admin] auths.updated = Nabago -emails.updated = Napalitan na ang [email] -emails.not_updated = Nabigong baguhin ang hinihiling na [email address]: %v +emails.updated = Napalitan na ang email +emails.not_updated = Nabigong baguhin ang hinihiling na email address: %v +monitor.next = Susunod na oras +monitor.last_execution_result = Resulta +dashboard.last_gc_time = Oras noong huling GC +users.last_login = Huling nag sign-in +first_page = Una +last_page = Huli +monitor.previous = Huling oras +users.created = Ginawa +users.new_success = Ginawa na ang account na "%s". +dashboard.task.unknown = Hindi kilalang utos: %[1]s +dashboard.task.finished = Utos: %[1]s na sinumulan ni %[2]s ay natapos na +users.edit_account = I-edit ang user account +users.update_profile_success = Na-update na ang user account. +users.still_own_packages = Ang user na ito ay nagmamay-ari ng isa o higit pang mga package, burahin muna ang mga package. +users.reset_2fa = I-reset ang 2FA +users.purge = I-purge ang user +users.purge_help = Piliting burahin ang user at anumang mga repositoryo, organisasyon, at package na minamay-ari ng user na ito. Ang mga komento at isyu na na-post ng user ay buburahin din. +config_settings = Mga setting +dashboard.statistic = Buod +dashboard.task.error = Error sa Utos: %[1]s: %[3]s +users.full_name = Buong pangalan +users.list_status_filter.menu_text = Isaasyos +users.list_status_filter.not_2fa_enabled = Naka-disable ang 2FA +users.never_login = Hindi nag-sign in kailanman +dashboard.system_status = Status ng sistema +dashboard.operation_switch = Palitan +dashboard.clean_unbind_oauth = Linisin ang mga unbound na koneksyon ng OAuth +dashboard.task.process = Utos: %[1]s +dashboard.task.started = Sinimulan ang Utos: %[1]s +dashboard.task.cancelled = Utos: %[1]s ay kinansela: %[3]s +dashboard.cron.started = Sinumulan ang Cron: %[1]s +dashboard.cron.process = Cron: %[1]s +dashboard.cron.cancelled = Cron: %[1]s ay kinansela: %[3]s +dashboard.cron.error = Error sa Cron: %[3]s +dashboard.cron.finished = Cron: natapos na ang %[1]s +dashboard.delete_inactive_accounts = Burahin ang lahat ng mga hindi na-activate na account +dashboard.delete_repo_archives.started = Nasimulan na ang utos na burahin ang lahat ng mga archive ng repositoryo. +dashboard.delete_missing_repos = Burahin ang lahat ng mga repositoryo na nawawalan ng kanilang mga Git file +dashboard.delete_generated_repository_avatars = Burahin ang mga na-generate na avatar ng repositoryo +dashboard.archive_cleanup = Burahin ang mga lumang archive ng repositoryo +dashboard.deleted_branches_cleanup = Linisin ang mga binurang branch +dashboard.update_migration_poster_id = I-update ang mga migration poster ID +dashboard.git_gc_repos = I-garbage collect ang lahat ng mga repositoryo +dashboard.resync_all_sshprincipals = I-update ang ".ssh/authorized_principals" file sa mga principal ng Forgejo SSH. +dashboard.resync_all_hooks = I-resychronize ang mga pre-receive, update at post-receive hook para sa lahat ng mga repositoryo +dashboard.cleanup_hook_task_table = Linisin ang hook_task table +dashboard.cleanup_packages = Linisin ang mga na-expire na package +dashboard.cleanup_actions = Linisin ang mga na-expire na log at artifact mula sa mga aksyon +dashboard.server_uptime = Uptime ng server +dashboard.current_goroutine = Mga kasalukuyang goroutine +dashboard.total_memory_allocated = Kabuuan na na-allocate na memory +dashboard.memory_obtained = Mga nakuhang memory +dashboard.pointer_lookup_times = Oras ng pointer lookup +dashboard.memory_free_times = Mga memory free +dashboard.current_heap_usage = Kasalukuyang paggamit ng heap +dashboard.heap_memory_obtained = Nakuhang heap memory +dashboard.heap_memory_idle = Idle ng heap memory +dashboard.heap_objects = Mga heap object +dashboard.bootstrap_stack_usage = Paggamit ng bootstrap stack +dashboard.stack_memory_obtained = Nakuhang stack memory +dashboard.profiling_bucket_hash_table_obtained = Mga nakuhang profiling bucket hash table +dashboard.gc_metadata_obtained = Nakuhang GC metadata +dashboard.delete_old_actions = Burahin ang lahat ng mga lumang aksyon mula sa database +dashboard.stop_endless_tasks = Tigilan ang mga hindi natatapos na task +dashboard.cancel_abandoned_jobs = Kanselahin ang mga naiwang job +dashboard.start_schedule_tasks = Simulan ang mga iskedyul task +dashboard.sync_branch.started = Nasimulan ang pag-sync ng mga branch +dashboard.rebuild_issue_indexer = Gawin muli ang indexef ng isyu +users.restricted = Pinaghihigpitan +users.2fa = 2FA +users.repos = Mga Repo +users.send_register_notify = Ipadala ang notification ng pagrehistro ng user +users.is_admin = Ay tagapangasiwa +users.is_restricted = Ay pinaghihigpitan +users.allow_import_local = Maaring mag-import ng mga lokal na repositoryo +users.allow_create_organization = Makakagawa ng mga organisasyon +users.update_profile = I-update ang user account +users.delete_account = Burahin ang user account +users.cannot_delete_self = Hindi mo maaring burahin ang sarili mo +users.still_own_repo = Ang user na ito ay nagmamay-ari pa ng isa o higit pang mga repositoryo. Burahin o ilipat sila muna. +users.list_status_filter.is_active = Aktibo +users.list_status_filter.not_active = Hindi aktibo +users.list_status_filter.is_admin = Tagapangasiwa +users.list_status_filter.not_admin = Hindi tagapangasiwa +users.list_status_filter.is_restricted = Pinaghihigpitan +users.list_status_filter.is_prohibit_login = Pinagbawalan ang pag-login +users.list_status_filter.not_prohibit_login = Pinapayagan ang pag-login +users.list_status_filter.is_2fa_enabled = Naka-enable ang 2FA +users.details = Mga detalye ng user +dashboard.memory_allocate_times = Mga allocation ng memory +users.edit = I-edit +users = Mga user account +organizations = Mga organisasyon +assets = Mga code asset +repositories = Mga repositoryo +hooks = Mga webhook +integrations = Mga pagsasama +authentication = Mga source ng authentikasyon +emails = Mga email ng user +config = Pagsasaayos +notices = Mga paunawa ng sistema +monitor = Pag-monitor +settings = Mga setting ng tagapangasiwa +users.activated = Naka-activate +users.admin = Tagapangasiwa +users.bot = Bot +users.remote = Remote +users.local = Lokal +users.auth_source = Source ng authentikasyon +users.name = Username +total = Total: %d +dashboard.operation_name = Pangalan ng Operasyon +dashboard.clean_unbind_oauth_success = Binura na ang lahat ng mga unbound na koneksyon ng OAuth. +dashboard.resync_all_sshkeys = I-update ang ".ssh/authorized_keys" file sa mga key ng Forgejo SSH. +dashboard.sync_tag.started = Nasimulan ang pag-sync ng mga tag +users.reserved = Nakareserba +dashboard.delete_inactive_accounts.started = Sinumulan na ang utos na burahin ang lahat ng mga hindi na-activate na account. +dashboard.delete_missing_repos.started = Nasimula na ang utos na burahin ang lahat ng mga repositoryo na nawawalan ng kanilang mga Git file. +dashboard.reinit_missing_repos = Muling pagsasaayos ng lahat ng nawawalang mga repositori ng Git kung saan umiiral ang mga tala +users.allow_git_hook_tooltip = Ang mga Git hook ay tinatakbo bilang OS user na tinatakbo ang Forgejo at may katulad na level ng pag-access ng host. Bilang isang resulta, ang mga gumagamit na may espesyal na pribilehiyo ng Git hook na ito ay maaaring ma-access at baguhin ang lahat ng mga repositori ng Forgejo pati na rin ang database na ginamit ng Forgejo. Dahil dito nakakamit din nila ang mga pribilehiyo ng Forgejo Administrator. +dashboard.delete_repo_archives = Burahin ang lahat ng mga archive ng mga repositoryo (ZIP, TAR.GZ, atbp..) +dashboard.sync_external_users = I-synchronize ang panlabas na user data +dashboard.heap_memory_released = Mga na-release na heap memory +dashboard.other_system_allocation_obtained = Ibang allocation ng sistema na nakuha +users.allow_git_hook = Makakagawa ng mga Git hook +dashboard.current_memory_usage = Kasalukuyang paggamit ng memory +dashboard.gc_times = Mga oras ng GC +users.list_status_filter.reset = I-reset +users.list_status_filter.not_restricted = Hindi pinaghihigpitan +config_summary = Buod +dashboard.new_version_hint = Available na ang Forgejo %s, tumatakbo ka ng %s. Suriin ang blog para sa karagdagang detalye. +dashboard.operations = Mga operasyon ng pagpapanatili +dashboard.operation_run = Patakbuhin +dashboard = Dashboard +identity_access = Pagkakakilanlan at pag-access +dashboard.sync_repo_branches = I-sync ang mga nawawalang branch mula sa Git data sa database +dashboard.sync_repo_tags = I-sync ang mga tag mula sa Git data sa database +dashboard.update_mirrors = I-update ang mga mirror +dashboard.repo_health_check = Suriin ang kalusugan ng lahat ng mga repositoryo +dashboard.check_repo_stats = Suriin ang lahat ng istatistika ng repositoryo +dashboard.mspan_structures_usage = Paggamit ng MSpan structure +dashboard.mspan_structures_obtained = Mga nakuhang MSpan structure +dashboard.mcache_structures_usage = Paggamit ng MCache structure +dashboard.next_gc_recycle = Susunod na GC recycle +dashboard.mcache_structures_obtained = Mga nakuhang MCache structure +dashboard.delete_old_actions.started = Nasimula na ang burahin ang lahat ng mga lumang aksyon mula sa database. +dashboard.update_checker = Tagasuri ng update +dashboard.delete_old_system_notices = Burahin ang lahat ng mga lumang paunawa ng sistema mula sa database +dashboard.gc_lfs = I-garbage collect ang mga LFS meta object +dashboard.stop_zombie_tasks = Tigilan ang mga zombie task +users.user_manage_panel = Ipamahala ang mga user account +users.new_account = Gumawa ng User Account +users.auth_login_name = Pangalan ng sign-in authentication +users.password_helper = Iwanang walang laman ang password upang panatilihing hindi nabago +users.max_repo_creation = Pinakamataas na numero ng mga repositoryo +users.max_repo_creation_desc = (Ilagay ang -1 para gamitin ang global na default na limitasyon.) +users.is_activated = Naka-activate ang User Account +users.prohibit_login = I-disable ang pag-sign in +emails.email_manage_panel = Ipamahala ang mga email ng user +self_check = Pansariling pagsusuri +dashboard.total_gc_pause = Kabuuang GC pause +dashboard.last_gc_pause = Huling GC pause +users.still_has_org = Ang user na ito ay isang miyembro ng isang organisasyon. Tanggalin ang user sa anumang mga organisasyon muna. +users.deletion_success = Binura na ang user account. +dashboard.heap_memory_in_use = Ginagamit na heap memory +emails.filter_sort.name = Username +emails.primary = Pauna +emails.filter_sort.email = Email +orgs.name = Pangalan +emails.activated = Naka-activate +emails.duplicate_active = Ang email address na ito ay aktibo na para sa ibang user. +emails.change_email_header = I-update ang Ari-arian ng Email +emails.filter_sort.email_reverse = Email (pabaligtad) +emails.filter_sort.name_reverse = Username (pabaligtad) +orgs.org_manage_panel = Ipamahala ang mga organisasyon +orgs.teams = Mga koponan +orgs.members = Mga miyembro +emails.change_email_text = Sigurado kang gusto mong i-update ang email address na ito? +config.app_ver = Bersyon ng Forgejo +config.git_version = Bersyon ng Git +packages.creator = Gumawa +defaulthooks.add_webhook = Magdagdag ng Default Webhook +auths.auth_manage_panel = Ipamahala ang mga source ng authentikasyon +auths.auth_name = Pangalan ng authentikasyon +auths.security_protocol = Protocol ng seguridad +auths.domain = Domain +auths.host = Host +packages.total_size = Kabuuang Laki: %s +auths.attribute_avatar = Attribute ng avatar +auths.enabled = Naka-enable +auths.syncenabled = I-enable ang user synchronization +auths.auth_type = Uri ng authentikasyon +auths.port = Port +auths.bind_dn = Bind DN +auths.bind_password = Password ng bind +auths.attribute_ssh_public_key = Attribute ng Publikong SSH key +repos.name = Pangalan +repos.private = Pribado +repos.issues = Mga isyu +repos.size = Laki +packages.type = Uri +packages.repository = Repositoryo +packages.size = Laki +auths.new = Magdagdag ng source ng authentikasyon +auths.attribute_surname = Attribute ng surname +packages.version = Bersyon +systemhooks.add_webhook = Magdagdag ng Sistemang Webhook +systemhooks.desc = Awtomatikong gumagawa ang mga Webhook ng mga HTTP POST request sa isang server kapag nag-trigger ang ilang partikular na kaganapan sa Forgejo. Ang mga webhook na tinukoy dito ay kikilos sa lahat ng mga repositoryo sa system, kaya mangyaring isaalang-alang ang anumang mga implikasyon ng performance na maaaring mayroon ito. Magbasa pa sa guide ng mga webhook. +packages.cleanup.success = Matagumpay na nalinis ang na-expire na data +defaulthooks.desc = Awtomatikong gumagawa ang mga Webhook ng mga HTTP POST request sa isang server kapag nag-trigger ang ilang partikular na kaganapan sa Forgejo. Ang mga webhook na tinukoy dito ay mga default at makokopya sa lahat ng mga bagong repositoryo. Magbasa pa sa guide ng mga webhook. +packages.published = Na-publish +defaulthooks = Mga default webhook +systemhooks.update_webhook = I-update ang Sistemang Webhook +auths.name = Pangalan +auths.type = Uri +defaulthooks.update_webhook = I-update ang Default Webhook +systemhooks = Mga sistemang webhook +auths.user_base = Base ng paghahanap ng user +auths.user_dn = DN ng User +auths.attribute_username = Attribute ng username +auths.attribute_username_placeholder = Iwanang walang laman para gamitin ang username na inilagay sa Forgejo. +auths.attribute_name = Attribute ng unang pangalan +packages.unreferenced_size = Walang Sangguniang Laki: %s +packages.owner = May-ari +packages.name = Pangalan +packages.cleanup = Linisin ang na-expire na data +orgs.new_orga = Bagong organisasyon +repos.repo_manage_panel = Ipamahala ang mga repositoryo +repos.unadopted = Mga unadopted na repositoryo +repos.unadopted.no_more = Wala nang mga unadopted na repositoryo na nahanap +repos.owner = May-ari +repos.lfs_size = Laki ng LFS +packages.package_manage_panel = Ipamahala ang mga package +auths.attribute_mail = Attribute ng email [org] repo_updated = Binago +team_access_desc = Access ng repositoryo +team_permission_desc = Pahintulot +org_desc = Deskripsyon +org_name_holder = Pangalan ng organisasyon +org_full_name_holder = Buong pangalan ng organisasyon +org_name_helper = Ang mga pangalan ng organisasyon ay dapat maikli at makakaalala. +create_org = Gumawa ng organisasyon +teams = Mga koponan +lower_members = mga miyembro +lower_repositories = mga repositoryo +create_new_team = Bagong koponan +create_team = Gumawa ng koponan +team_name = Pangalan ng koponan +team_desc = Deskripsyon +team_name_helper = Ang mga pangalan ng koponan ay dapat na maikli at makakaalala. +team_desc_helper = Ilarawan ang layunin ng koponan. +members = Mga miyembro +code = Code + + +[packages] +alpine.repository.branches = Mga branch +owner.settings.cargo.initialize.success = Matagumpay na nagawa ang Cargo index. +details = Mga detalye +empty = Wala pang anumang mga package. +filter.container.tagged = Naka-tag +filter.container.untagged = Hindi naka-tag +filter.type = Uri +filter.type.all = Lahat +filter.no_result = Walang resulta ang iyong filter. +about = Tungkol sa package na ito +installation = Pag-install +details.repository_site = Website ng repositoryo +details.documentation_site = Website ng dokumentasyon +alpine.repository.repositories = Mga Repositoryo +alpine.repository.architectures = Mga architechture +chef.install = Para i-install ang package na ito, patakbuhin ang sumusunod na command: +composer.registry = I-setup ang registry na ito sa iyong ~/.composee/config.json file: +composer.install = Para i-install ang package gamit ang Composer, patakbuhin ang sumusunod na command: +empty.repo = Nag-upload ka ba ng package, ngunit hindi pinapakita dito? Pumunta sa mga setting ng package at i-link iyan sa repo na ito. +keywords = Mga keyword +versions = Mga bersyon +title = Mga package +desc = Ipamahala ang mga package ng repositoryo. +registry.documentation = Para sa higit pang impormasyon tungkol sa %s registry, tignan ang dokumentasyon. +published_by = Na-publish ang %[1]s ni/ng %[3]s +requirements = Mga kinakailangan +dependencies = Mga dependency +details.author = Autor +details.project_site = Website ng proyekto +details.license = Lisensya +versions.view_all = Tignan lahat +dependency.id = ID +dependency.version = Bersyon +alpine.registry = I-setup ang registry na ito sa pamamagitan ng pagdagdag ng url sa iyong /etc/apk/repositories file: +alpine.registry.info = Pumili ng $branch at $repository mula sa listahan sa ibaba. +alpine.install = Para i-install ang package, patakbuhin ang sumusunod na command: +alpine.repository = Info ng Repositoryo +cargo.registry = I-setup ang registry na ito sa Cargo configuration file (halimbawa ~/.cargo/config.toml): +chef.registry = I-setup ang registry na ito sa iyong ~/.chef/config.rb file: +composer.dependencies = Mga dependency +composer.dependencies.development = Mga dependency ng pag-develop +conan.details.repository = Repositoryo +conan.registry = I-setup ang registry na ito mula sa command line: +assets = Mga asset +empty.documentation = Para sa higit pang impormasyon sa package registry, tignan ang dokumentasyon. +cargo.install = Para i-install ang package gamit ang Cargo, patakbuhin ang sumusunod na command: +published_by_in = Na-publish ang %[1]s ni %[3]s sa %[5]s +alpine.registry.key = I-download ang registry public RSA key sa /etc/apk/keys folder para i-verify ang index signature: + +[actions] +runners.last_online = Huling oras na online +runs.no_workflows.quick_start = Hindi alam kung paano magsimula gamit ang Forgejo Actions? Tingnan ang gabay sa mabilis na pagsisimula. +runs.no_workflows.documentation = Para sa higit pang impormasyon tungkol sa Forgejo Actions, tingnan ang Dokumentasyon. + +[action] +commit_repo = itinulak sa %[3]s sa %[4]s +create_issue = `binuksan ang isyu %[3]s#%[2]s` +comment_issue = `nagkomento sa isyu %[3]s#%[2]s` +reopen_pull_request = `binuksang muli ang hiling sa paghatak %[3]s#%[2]s` +comment_pull = `nagiwan ng komento sa hiling sa paghatak %[3]s#%[2]s` +reopen_issue = `binuksang muli ang isyu %[3]s#%[2]s` +create_pull_request = `ginawa ang hiling sa paghatak %[3]s#%[2]s` +create_branch = ginawa ang branch na %[3]s sa %[4]s +create_repo = ginawa ang repositoryo na %s +starred_repo = na-star ang %[2]s +watched_repo = ay sinimulang panoorin ang %[2]s + +[tool] +1m = 1 minuto +1h = 1 oras +1d = 1 araw +1s = 1 segundo +now = ngayon +future = sa madaling panahon +months = %d buwan +1w = 1 linggo +1mon = 1 buwan +1y = 1 taon +seconds = %d segundo +minutes = %d minuto +hours = %d oras +days = %d araw +weeks = %d linggo +years = %d taon +raw_seconds = segundo +raw_minutes = minuto + +[munits.data] +mib = MiB +gib = GiB +b = B +kib = KiB +tib = TiB +pib = PiB +eib = EiB + +[gpg] +error.not_signed_commit = Hindi isang naka-sign na commit +error.probable_bad_signature = BABALA! Bagaman na may key na may ID na ito sa database hindi nito pinapatunayan ang commit na ito! Ang commit na ito ay KAHINA-HINALA. +error.extract_sign = Nabigong i-extract ang signature +error.no_committer_account = Walang account na naka-link sa email address ng committer +error.no_gpg_keys_found = Walang kilalang key na nahanap para sa signature na ito sa database +default_key = Naka-sign gamit ang default key +error.generate_hash = Nabigong i-generate ang hash ng commit +error.failed_retrieval_gpg_keys = Nabigong kumuha ng anumang key na naka-attach sa account ng committer +error.probable_bad_default_signature = BABALA! Bagaman na ang default key ay may ID na ito hindi nito pinapatunayan ang commit na ito! Ang commit na ito ay KAHINA-HINALA. + +[notification] +unread = Hindi nabasa +read = Nabasa +no_unread = Walang mga hindi nabasang notification. +notifications = Mga abiso +no_read = Walang mga nabasang notification. +pin = I-pin ang notification +mark_as_read = Markahan bilang nabasa +mark_as_unread = Markahan bilang hindi nabasa +subscriptions = Mga subscription +watching = Pinapanood +no_subscriptions = Walang mga subscription +mark_all_as_read = Markahan lahat bilang nabasa + +[units] +error.no_unit_allowed_repo = Hindi ka pinapayagang ma-access ang anumang seksyon ng repositoryong ito. +unit = Yunit +error.unit_not_allowed = Hindi ka pinapayagang ma-access ang seksyon ng repositoryong ito. + +[dropzone] +default_message = I-drop ang mga file o mag-click dito para mag-upload. +invalid_input_type = Hindi ka maaring mag-upload ng mga file sa uri na ito. +file_too_big = Ang laki ng file ({{filesize}}) MB) ay lumalagpas sa pinakamataas na size na ({{maxFilesize}} MB). +remove_file = Tanggalin ang file \ No newline at end of file diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 4840f1e478..03100f722a 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -322,7 +322,6 @@ password_algorithm=Algorithme de hachage du mot de passe invalid_password_algorithm=Algorithme de hachage du mot de passe invalide password_algorithm_helper=Définissez l’algorithme de hachage du mot de passe. Les algorithmes ont des exigences matérielles et une résistance différentes. L’algorithme argon2 est bien sécurisé mais utilise beaucoup de mémoire et peut être inapproprié pour les systèmes limités en ressources. enable_update_checker=Activer la vérification des mises-à-jour -enable_update_checker_helper=Vérifie les mises à jour régulièrement en se connectant à gitea.io. env_config_keys=Configuration de l'environnement env_config_keys_prompt=Les variables d'environnement suivantes seront également ajoutées à votre fichier de configuration : enable_update_checker_helper_forgejo = Vérifie la disponibilité de nouvelles versions de Forgejo en interrogeant l'enregistrement DNS TXT de release.forgejo.org. @@ -633,6 +632,7 @@ admin_cannot_delete_self = Vous ne pouvez supprimer votre compte lorsque vous di admin_cannot_delete_self=Vous ne pouvez pas vous supprimer vous-même lorsque vous êtes admin. Veuillez d’abord supprimer vos privilèges d’administrateur. unsupported_login_type = Ce type de compte ne peut être supprimé. unset_password = L'utilisateur connecté n'a pas de mot de passe. +required_prefix = Le texte entré doit commencer par "%s" [user] change_avatar=Changer votre avatar… @@ -666,6 +666,8 @@ block_user.detail_3 = Cet utilisateur ne peut pas vous ajouter en tant que colla follow_blocked_user = Vous ne pouvez pas suivre cet utilisateur parce vous avez bloqué cet utilisateur ou bien cet utilisateur vous a bloqué. block = Bloquer unblock = Débloquer +following_one = Suit %d personnes +followers_one = Suivi par %d [settings] profile=Profil @@ -989,6 +991,9 @@ additional_repo_units_hint_description = Afficher un bouton "Ajouter plus d'unit additional_repo_units_hint = Encourager l'ajout de nouvelles unités pour le dépôt update_hints = Mettre à jour les suggestions update_hints_success = Les suggestions ont été mises à jour. +pronouns_custom = Sur mesure +pronouns = Qualités +pronouns_unspecified = Non spécifié [repo] new_repo_helper=Un dépôt contient tous les fichiers d’un projet, ainsi que l’historique de leurs modifications. Vous avez déjà ça ailleurs ? Migrez-le ici. @@ -1928,7 +1933,7 @@ milestones.filter_sort.least_issues=Le moins de tickets signing.will_sign=Cette révision sera signée avec la clé « %s ». signing.wont_sign.error=Impossible de vérifier la signature de la révision. -signing.wont_sign.nokey=Aucune clé n’est disponible pour signer cette révision. +signing.wont_sign.nokey=Aucune clé n’est disponible sur cette instance pour signer cette révision. signing.wont_sign.never=Les révisions ne sont jamais signées. signing.wont_sign.always=Les révisions sont toujours signées. signing.wont_sign.pubkey=La révision ne sera pas signée car vous votre compte ne possède pas de clé publique. @@ -2285,15 +2290,15 @@ settings.event_pull_request_assign=Demande d'ajout assignée settings.event_pull_request_assign_desc=Demande d'ajout assignée ou non assignée. settings.event_pull_request_label=Demande d'ajout étiquetée settings.event_pull_request_label_desc=Label attribué ou retiré. -settings.event_pull_request_milestone=Jalon +settings.event_pull_request_milestone=Demande d'ajout jalonnée settings.event_pull_request_milestone_desc=Demande d'ajout jalonnée ou dé-jalonnée. -settings.event_pull_request_comment=Commentaire +settings.event_pull_request_comment=Commentaire sur une demande d'ajout settings.event_pull_request_comment_desc=Commentaire créé, modifié ou supprimé. -settings.event_pull_request_review=Évaluation +settings.event_pull_request_review=Demande d'ajout évaluée settings.event_pull_request_review_desc=Demande d’ajout approuvée, rejetée ou commentée. -settings.event_pull_request_sync=Synchronisation +settings.event_pull_request_sync=Demande d'ajout synchronisée settings.event_pull_request_sync_desc=Demande d'ajout synchronisée. -settings.event_pull_request_review_request=Demande d’évaluation +settings.event_pull_request_review_request=Requête d’évaluation d'une demande d'ajout settings.event_pull_request_review_request_desc=Création ou suppresion de demandes d’évaluation. settings.event_pull_request_approvals=Approbations de demande d'ajout settings.event_pull_request_merge=Fusion de demande d'ajout @@ -2308,9 +2313,9 @@ settings.active_helper=Les informations sur les événements déclenchés seront settings.add_hook_success=Nouveau Webhook ajouté. settings.update_webhook=Actualiser le déclencheur settings.update_hook_success=Déclencheur Web actualisé. -settings.delete_webhook=Retirer le Webhook +settings.delete_webhook=Retirer le webhook settings.recent_deliveries=Livraisons récentes -settings.hook_type=Type de Hook +settings.hook_type=Type de déclencheur settings.slack_token=Jeton settings.slack_domain=Domaine settings.slack_channel=Canal @@ -2343,7 +2348,7 @@ settings.deploy_key_content=Contenu settings.key_been_used=Une clef de déploiement identique est déjà en cours d'utilisation. settings.key_name_used=Une clef de déploiement du même nom existe déjà. settings.add_key_success=La clé de déploiement "%s" a été ajoutée. -settings.deploy_key_deletion=Supprimer une clef de déploiement +settings.deploy_key_deletion=Supprimer une clé de déploiement settings.deploy_key_deletion_desc=La suppression d'une clef de déploiement révoque son accès à ce dépôt. Continuer ? settings.deploy_key_deletion_success=La clé de déploiement a été supprimée. settings.branches=Branches @@ -2373,7 +2378,7 @@ settings.protect_merge_whitelist_committers=Activer la liste blanche pour la fus settings.protect_merge_whitelist_committers_desc=N'autoriser que les utilisateurs et les équipes en liste blanche d'appliquer les demandes de fusion sur cette branche. settings.protect_merge_whitelist_users=Utilisateurs en liste blanche de fusion : settings.protect_merge_whitelist_teams=Équipes en liste blanche de fusion : -settings.protect_check_status_contexts=Activer le Contrôle Qualité +settings.protect_check_status_contexts=Activer le contrôle qualité settings.protect_status_check_patterns=Motifs de vérification des statuts : settings.protect_status_check_patterns_desc=Entrez des motifs pour spécifier quelles vérifications doivent réussir avant que des branches puissent être fusionnées. Un motif par ligne. Un motif ne peut être vide. settings.protect_check_status_contexts_desc=Exiger le status « succès » avant de fusionner. Quand activée, une branche protégée ne peux accepter que des soumissions ou des fusions ayant le status « succès ». Lorsqu'il n'y a pas de contexte, la dernière révision fait foi. @@ -2429,10 +2434,10 @@ settings.tags.protection.allowed=Autorisé settings.tags.protection.allowed.users=Utilisateurs autorisés settings.tags.protection.allowed.teams=Équipes autorisées settings.tags.protection.allowed.noone=Personne -settings.tags.protection.create=Protéger l'étiquette +settings.tags.protection.create=Ajouter une règle settings.tags.protection.none=Il n'y a pas d'étiquettes protégées. settings.tags.protection.pattern.description=Vous pouvez utiliser au choix un nom unique, un motif de glob ou une expression régulière qui correspondra à plusieurs étiquettes. Pour plus d’informations, consultez le guide sur les étiquettes protégées. -settings.bot_token=Jeton de Bot +settings.bot_token=Jeton de bot settings.chat_id=ID de conversation settings.thread_id=ID du fil settings.matrix.homeserver_url=URL du serveur d'accueil @@ -2489,11 +2494,11 @@ diff.browse_source=Parcourir la source diff.parent=Parent diff.commit=révision diff.git-notes=Notes -diff.data_not_available=Contenu de la comparaison indisponible -diff.options_button=Option de Diff -diff.show_diff_stats=Voir les Statistiques -diff.download_patch=Télécharger le Fichier Patch -diff.download_diff=Télécharger le Fichier des Différences +diff.data_not_available=Contenu de la comparaison (diff) indisponible +diff.options_button=Option de comparaison (diff) +diff.show_diff_stats=Voir les statistiques +diff.download_patch=Télécharger le patch en tant que fichier +diff.download_diff=Télécharger le fichier diff diff.show_split_view=Vue séparée diff.show_unified_view=Vue unifiée diff.whitespace_button=Espace @@ -2515,7 +2520,7 @@ diff.file_suppressed=Fichier diff supprimé car celui-ci est trop grand diff.file_suppressed_line_too_long=Diff de fichier supprimé car une ou plusieurs lignes sont trop longues diff.too_many_files=Certains fichiers ne sont pas affichés car ce diff contient trop de modifications diff.show_more=Voir plus -diff.load=Voir la Diff +diff.load=Voir la diff diff.generated=générée diff.vendored=externe diff.comment.add_line_comment=Commenter cette ligne @@ -2593,7 +2598,7 @@ release.tags_for=Étiquettes pour %s branch.name=Nom de la branche branch.already_exists=Une branche nommée "%s" existe déjà. branch.delete_head=Supprimer -branch.delete=`Supprimer la branche "%s"` +branch.delete=Supprimer la branche "%s" branch.delete_html=Supprimer la branche branch.delete_desc=La suppression d’une branche est permanente. Bien qu’une branche supprimée puisse temporairement subsister, elle NE PEUT PAS être facilement restaurée. Continuer ? branch.deletion_success=La branche "%s" a été supprimée. @@ -2610,9 +2615,9 @@ branch.restore_success=La branche "%s" a été restaurée. branch.restore_failed=Impossible de restaurer la branche "%s". branch.protected_deletion_failed=La branche "%s" est protégé. Elle ne peut pas être supprimée. branch.default_deletion_failed=La branche "%s" est la branche par défaut. Elle ne peut pas être supprimée. -branch.restore=`Restaurer la branche "%s"` -branch.download=`Télécharger la branche "%s"` -branch.rename=`Renommer la branche "%s"` +branch.restore=Restaurer la branche "%s" +branch.download=Télécharger la branche "%s" +branch.rename=Renommer la branche "%s" branch.search=Rechercher une branche branch.included_desc=Cette branche fait partie de la branche par défaut branch.included=Incluses @@ -2717,6 +2722,22 @@ n_branch_few = %s branches n_tag_one = %s étiquettes editor.push_out_of_date = Le push semble obsolète. issues.num_participants_one = %d participant +issues.archived_label_description = (Archivé) %s +settings.add_webhook.invalid_path = L'emplacement ne peut pas contenir ni ".", ni "..", ni être vide, et ne peut pas commencer ou se terminer par un slash. +settings.sourcehut_builds.secrets_helper = Permettre au job d'accéder aux secrets de build (nécessite la permission SECRETS:RO) +size_format = %[1]s : %[2]s ; %[3]s : %[4]s +settings.sourcehut_builds.visibility = Visibilité du job +settings.sourcehut_builds.secrets = Secrets +settings.sourcehut_builds.manifest_path = Construire le chemin du manifeste +settings.sourcehut_builds.graphql_url = URL GraphQL (e.g. https://builds.sr.ht/query) +release.download_count_one = %s téléchargement +release.download_count_few = %s téléchargements +release.system_generated = Cet attachement a été généré automatiquement. +settings.enforce_on_admins_desc = Les administrateurs du dépôt ne peuvent pas contourner cette règle. +settings.web_hook_name_sourcehut_builds = SourceHut Builds +settings.enforce_on_admins = Contraindre les administrateurs du dépôt par cette règle +settings.rename_branch_failed_protected = Impossible de renommer la branche %s car il s'agit d'une branche protégée. +settings.event_pull_request_enforcement = Amélioration [graphs] component_loading=Chargement de %s… @@ -2745,7 +2766,7 @@ team_name_helper=Le nom d'équipe doit être court et mémorable. team_desc_helper=Décrire le but ou le rôle de l’équipe. team_access_desc=Accès au dépôt team_permission_desc=Autorisation -team_unit_desc=Permettre l’accès aux Sections du dépôt +team_unit_desc=Permettre l’accès aux sections du dépôt team_unit_disabled=(Désactivé) form.name_reserved=Le nom d'organisation "%s" est réservé. @@ -2754,7 +2775,7 @@ form.create_org_not_allowed=Vous n'êtes pas autorisé à créer une organisatio settings=Paramètres settings.options=Organisation -settings.full_name=Nom Complet +settings.full_name=Nom complet settings.email=Courriel de contact settings.website=Site Web settings.location=Localisation @@ -2764,7 +2785,7 @@ settings.visibility=Visibilité settings.visibility.public=Public settings.visibility.limited=Limité (Visible uniquement aux utilisateurs authentifiés) settings.visibility.limited_shortname=Limité -settings.visibility.private=Privé (Visible uniquement aux membres de l’organisation) +settings.visibility.private=Privé (visible uniquement aux membres de l’organisation) settings.visibility.private_shortname=Privé settings.update_settings=Appliquer les paramètres @@ -2810,15 +2831,15 @@ teams.read_access=Lecture teams.read_access_helper=Les membres peuvent voir et cloner les dépôts de l'équipe. teams.write_access=Écriture teams.write_access_helper=Les membres peuvent voir et pousser dans les dépôts de l'équipe. -teams.admin_access=Accès Administrateur +teams.admin_access=Accès administrateur teams.admin_access_helper=Les membres peuvent tirer et pousser des modifications vers les dépôts de l'équipe, et y ajouter des collaborateurs. teams.no_desc=Aucune description teams.settings=Paramètres teams.owners_permission_desc=Les propriétaires ont un accès complet à tous les dépôts et disposent d'un accès administrateur de l'organisation. -teams.members=Membres de L'Équipe +teams.members=Membres de L'équipe teams.update_settings=Appliquer les paramètres teams.delete_team=Supprimer l'équipe -teams.add_team_member=Ajouter un Membre +teams.add_team_member=Ajouter un membre teams.invite_team_member=Inviter à %s teams.invite_team_member.list=Invitations en attente teams.delete_team_title=Supprimer l'équipe @@ -2828,7 +2849,7 @@ teams.read_permission_desc=Cette équipe permet l'accès en lectureécriture : les membres peuvent participer à ses dépôts. teams.admin_permission_desc=Cette équipe permet l'accès administrateur : les membres peuvent voir, participer et ajouter des collaborateurs à ses dépôts. teams.create_repo_permission_desc=De plus, cette équipe accorde la permission Créer un dépôt : les membres peuvent créer de nouveaux dépôts dans l'organisation. -teams.repositories=Dépôts de l'Équipe +teams.repositories=Dépôts de l'équipe teams.search_repo_placeholder=Rechercher dans le dépôt… teams.remove_all_repos_title=Supprimer tous les dépôts de l'équipe teams.remove_all_repos_desc=Ceci supprimera tous les dépôts de l'équipe. @@ -2861,9 +2882,9 @@ repositories=Dépôts hooks=Déclencheurs web integrations=Intégrations authentication=Sources d'authentification -emails=Emails de l'utilisateur +emails=Courriels de l'utilisateur config=Configuration -notices=Informations +notices=Informations système monitor=Surveillance first_page=Première last_page=Dernière @@ -2959,7 +2980,7 @@ dashboard.rebuild_issue_indexer=Reconstruire l’indexeur des tickets users.user_manage_panel=Gestion du compte utilisateur users.new_account=Créer un compte users.name=Nom d'utilisateur -users.full_name=Nom Complet +users.full_name=Nom complet users.activated=Activé users.admin=Administrateur users.restricted=Restreint @@ -2984,9 +3005,9 @@ users.max_repo_creation=Nombre maximal de dépôts users.max_repo_creation_desc=(Mettre à -1 pour utiliser la limite globale par défaut.) users.is_activated=Ce compte est activé users.prohibit_login=Désactiver la connexion -users.is_admin=Est Administrateur +users.is_admin=Est administrateur users.is_restricted=Est restreint -users.allow_git_hook=Autoriser la création de Git Hooks +users.allow_git_hook=Autoriser la création de déclencheurs Git users.allow_git_hook_tooltip=Les Déclencheurs Git sont exécutés par le même utilisateur que Forgejo, qui a des privilèges systèmes élevés. Les utilisateurs ayant ce droit peuvent altérer touts les dépôts, compromettre la base de données applicative, et se promouvoir administrateurs de Forgejo. users.allow_import_local=Autoriser l'importation de dépôts locaux users.allow_create_organization=Autoriser la création d'organisations @@ -3034,7 +3055,7 @@ orgs.members=Membres orgs.new_orga=Nouvelle organisation repos.repo_manage_panel=Gestion des dépôts -repos.unadopted=Dépôts dépossédés +repos.unadopted=Dépôts non adoptés repos.unadopted.no_more=Aucun dépôt dépossédé trouvé. repos.owner=Propriétaire repos.name=Nom @@ -3084,8 +3105,8 @@ auths.domain=Domaine auths.host=Hôte auths.port=Port auths.bind_dn=Bind DN -auths.bind_password=Bind mot de passe -auths.user_base=Utilisateur Search Base +auths.bind_password=Mot de passe Bind +auths.user_base=Utilisateur search base auths.user_dn=Utilisateur DN auths.attribute_username=Attribut nom d'utilisateur auths.attribute_username_placeholder=Laisser vide afin d'utiliser le nom d'utilisateur spécifié dans Forgejo. @@ -3171,7 +3192,7 @@ auths.tip.google_plus=Obtenez des identifiants OAuth2 sur la console API de Goog auths.tip.openid_connect=Utilisez l'URL de découvert OpenID (/.well-known/openid-configuration) pour spécifier les points d'accès auths.tip.twitter=Rendez-vous sur https://dev.twitter.com/apps, créez une application et assurez-vous que l'option "Autoriser l'application à être utilisée avec Twitter Connect" est activée auths.tip.discord=Enregistrer une nouvelle application sur https://discordapp.com/developers/applications/me -auths.tip.gitea=Enregistrez une nouvelle application OAuth2. Le guide peut être trouvé sur https://docs.gitea.com/development/oauth2-provider +auths.tip.gitea=Enregistrez une nouvelle application OAuth2. Le guide peut être trouvé sur https://forgejo.org/docs/latest/user/oauth2-provider auths.tip.yandex=`Créez une nouvelle application sur https://oauth.yandex.com/client/new. Sélectionnez les autorisations suivantes dans la section "Yandex API passport" : "Accès à l'adresse e-mail", "Accès à l'avatar de l'utilisateur" et "Accès au nom d'utilisateur, prénom et prénom, genre"` auths.tip.mastodon=Entrez une URL d'instance personnalisée pour l'instance mastodon avec laquelle vous voulez vous authentifier (ou utiliser celle par défaut) auths.edit=Mettre à jour la source d'authentification @@ -3381,7 +3402,7 @@ notices.desc=Description notices.op=Opération notices.delete_success=Les informations systèmes ont été supprimées. self_check = Auto vérification -dashboard.sync_repo_tags = Synchorniser les étiquettes depuis git vers la base de donnée +dashboard.sync_repo_tags = Synchroniser les étiquettes depuis Git vers la base de donnée dashboard.sync_tag.started = La synchronisation des étiquettes a commencé self_check.no_problem_found = Aucun problème n'a encore été trouvé. self_check.database_collation_mismatch = La base de donnée devrait utiliser la collation %s @@ -3394,6 +3415,8 @@ self_check.database_collation_mismatch=Exige que la base de données utilise la self_check.database_collation_case_insensitive=La base de données utilise la collation %s, insensible à la casse. Bien que Gitea soit compatible, il peut y avoir quelques rares cas qui ne fonctionnent pas comme prévu. self_check.database_inconsistent_collation_columns=La base de données utilise la collation %s, mais ces colonnes utilisent des collations différentes. Cela peut causer des problèmes imprévus. self_check.database_fix_mysql=Pour les utilisateurs de MySQL ou MariaDB, vous pouvez utiliser la commande « gitea doctor convert » dans un terminal ou exécuter une requête du type « ALTER … COLLATE ... » pour résoudre les problèmes de collation. +config_settings = Paramètres +config_summary = Résumé [action] create_repo=a créé le dépôt %s diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index ee367101a3..2af6019eae 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -51,7 +51,7 @@ webauthn_reload=Ricarica repository=Repository organization=Organizzazione mirror=Mirror -new_repo=Nuovo progetto +new_repo=Nuovo repository new_migrate=Nuova migrazione new_mirror=Nuovo mirror new_fork=Nuovo fork @@ -73,7 +73,7 @@ forks=Fork activities=Attivitá pull_requests=Pull request -issues=Problemi +issues=Segnalazioni milestones=Milestones ok=OK @@ -82,7 +82,7 @@ save=Salva add=Aggiungi add_all=Aggiungi tutti remove=Rimuovi -remove_all=Rimuovi tutti +remove_all=Rimuovi Tutti edit=Modifica enabled=Attivo @@ -117,7 +117,7 @@ concept_user_organization=Organizzazione name=Nome value=Valore enable_javascript = Questo sito richiede JavaScript. -tracked_time_summary = Resoconto del tempo tracciato in base ai filtri dell'elenco dei problemi +tracked_time_summary = Resoconto del tempo cronometrato in base ai filtri dell'elenco delle segnalazioni retry = Riprova rerun = Ri-esegui rerun_all = Ri-esegui tutti gli incarichi @@ -143,18 +143,20 @@ confirm_delete_selected = Confermare l'eliminazione di tutti gli elementi selezi sign_in_with_provider = Accedi con %s new_project_column = Nuova colonna toggle_menu = Mostra/Nascondi Menu -filter.not_fork = Non derivato +filter.not_fork = Non da fork filter = Filtro -filter.clear = Azzera filtro +filter.clear = Rimuovi filtri filter.is_archived = Archiviato filter.not_archived = Non archiviato -filter.is_fork = Derivato -filter.is_mirror = Specchiato -filter.not_mirror = Non specchiato -filter.is_template = Modello base -filter.not_template = Non modello base +filter.is_fork = Da Fork +filter.is_mirror = Mirror +filter.not_mirror = Non mirror +filter.is_template = Modello +filter.not_template = Non modello filter.public = Pubblico filter.private = Privato +more_items = Più elementi +invalid_data = Dati non validi: %v [aria] footer.links = Collegamenti @@ -167,9 +169,9 @@ more = Più contributions_zero = Nessun contributo less = Meno number_of_contributions_in_the_last_12_months = %s contributi negli ultimi 12 mesi -contributions_format = {contributions} il {day} {month} {year} -contributions_one = contribuzione -contributions_few = contribuzioni +contributions_format = {contributions} il {day}/{month}/{year} +contributions_one = contributo +contributions_few = contributi [editor] buttons.heading.tooltip = Aggiungi intestazione @@ -181,9 +183,9 @@ buttons.link.tooltip = Aggiungi un collegamento buttons.list.unordered.tooltip = Aggiungi un elenco puntato buttons.list.ordered.tooltip = Aggiungi un elenco numerato buttons.list.task.tooltip = Aggiungi un elenco di attività -buttons.mention.tooltip = Menziona un utente o gruppo +buttons.mention.tooltip = Menziona un utente o team buttons.ref.tooltip = Fai riferimento ad un problema o pull request -buttons.switch_to_legacy.tooltip = Passa all'editor legacy +buttons.switch_to_legacy.tooltip = Passa all'editor classico buttons.enable_monospace_font = Attiva font monospace buttons.disable_monospace_font = Disattiva font monospace @@ -197,8 +199,8 @@ missing_csrf=Richiesta errata: nessun token CSRF presente invalid_csrf=Richiesta errata: token CSRF non valido not_found=Il bersaglio non è stato trovato. network_error=Errore di rete -report_message = Se credi che questo sia un bug di Forgejo, per favore verifica i problemi su Codeberg o pubblica un nuovo problema se necessario. -server_internal = Errore Interno del Server +report_message = Se credi che questo sia un errore di Forgejo, per favore controlla le segnalazioni su Codeberg o aprine una nuova se necessario. +server_internal = Errore interno del server [startpage] app_desc=Un servizio auto-ospitato per Git pronto all'uso @@ -228,7 +230,7 @@ ssl_mode=SSL path=Percorso sqlite_helper=Percorso file del database SQLite3.
    Inserisci un percorso assoluto se stai usando Forgejo come servizio. reinstall_error=Stai cercando di installare in un database Forgejo esistente -reinstall_confirm_message=La reinstallazione con un database Forgejo esistente può causare problemi multipli. Nella maggior parte dei casi, dovresti usare il tuo "app.ini" esistente per eseguire Forgejo. Se sai cosa stai facendo, confermi quanto segue: +reinstall_confirm_message=La reinstallazione con una base dati Forgejo esistente può causare vari problemi. Nella maggior parte dei casi, dovresti usare il tuo "app.ini" per eseguire Forgejo. Se sai cosa stai facendo, conferma quanto segue: reinstall_confirm_check_1=I dati crittografati da SECRET_KEY nell'app. ni potrebbe essere perso: gli utenti potrebbero non essere in grado di accedere con 2FA/OTP & mirror potrebbe non funzionare correttamente. Selezionando questa casella confermi che il file attuale app.ini contiene il corretto SECRET_KEY. reinstall_confirm_check_2=I repository e le impostazioni potrebbero avere bisogno di essere ri-sincronizzati. Selezionando questa casella confermi che potrai risincronizzare manualmente gli hook per i repository e il file authorized_keys. Confermi che assicurerai che le impostazioni del repository e del mirror siano corrette. reinstall_confirm_check_3=Confermi di essere assolutamente sicuro che questo Forgejo è in esecuzione con l'app corretta. ni posizione e che sei sicuro di dover reinstallare. Confermi di aver riconosciuto i rischi di cui sopra. @@ -243,7 +245,7 @@ err_admin_name_is_invalid=Il nome utente Administrator non è valido general_title=Impostazioni generali app_name=Titolo dell'istanza app_name_helper=Qui puoi inserire il nome della tua società. -repo_path=Percorso radice del progetto +repo_path=Percorso radice dei repository repo_path_helper=Le Remote Git repositories saranno salvate in questa directory. lfs_path=Percorso radice di git LFS lfs_path_helper=I file trovati da Git LFS saranno salvati in questa directory. Lasciare vuoto per disattivare. @@ -251,7 +253,7 @@ run_user=Nome utente col quale eseguire domain=Dominio server domain_helper=Dominio o indirizzo host per il server. ssh_port=Porta server SSH -ssh_port_helper=Numero della porta che verrà usata dal server SSH, Lasciare vuoto per disattivare. +ssh_port_helper=Numero della porta che verrà usata dal server SSH. Lascia vuoto per disattivare il server SSH. http_port=Porta in ascolto HTTP http_port_helper=Numero della porta che sarà usata dal server web Forgejo. app_url=URL di base @@ -267,7 +269,7 @@ smtp_from=Invia e-mail come smtp_from_helper=Indirizzo Email che Forgejo utilizzerà. Inserisci un indirizzo email o usa il formato "Name" . mailer_user=Nome utente SMTP mailer_password=Password SMTP -register_confirm=Richiedere conferma e-mail per registrarsi +register_confirm=Richiedi conferma e-mail durante la registrazione mail_notify=Attiva le notifiche e-mail server_service_title=Impostazioni server e servizi di terze parti offline_mode=Attiva modalità in locale @@ -298,7 +300,7 @@ sqlite3_not_available=Questa versione di Forgejo non supporta SQLite3. Si prega invalid_db_setting=Le impostazioni del database sono invalide: %v invalid_repo_path=Il percorso radice del Repository è invalido: %v invalid_app_data_path=Il percorso dati dell'app non è valido: %v -run_user_not_match=Il nome utente "utente con cui eseguire" non è il nome utente attuale: %s -> %s +run_user_not_match=Il nome "utente con cui eseguire" non è il nome utente attuale: %s -> %s internal_token_failed=Generazione del token interno non riuscita: %v secret_key_failed=Generazione della chiave segreta non riuscita: %v save_config_failed=Salvataggio della configurazione non riuscito: %v @@ -313,22 +315,21 @@ default_enable_timetracking_popup=Attiva il cronografo per le nuove repositories no_reply_address=Dominio e-mail nascosto no_reply_address_helper=Nome di dominio per utenti con un indirizzo email nascosto. Ad esempio, il nome utente "joe" accederà a Git come "joe@noreply.example.org" se il dominio email nascosto è impostato a "noreply.example.org". password_algorithm=Algoritmo per hash delle password -smtp_from_invalid = L'indirizzo "Invia Email come" non è valido +smtp_from_invalid = L'indirizzo "Invia e-mail come" non è valido enable_update_checker_helper_forgejo = Verificherà periodicamente nuove versioni di Forgejo controllando il record DNS TXT di release.forgejo.org. invalid_db_table = La tabella del database "%s" non è valida: %v invalid_password_algorithm = Algoritmo di hash della password non valido enable_update_checker = Attiva il controllo degli aggiornamenti -enable_update_checker_helper = Verifica periodicamente la presenza di nuove versioni tramite gitea.io. env_config_keys = Configurazione Ambiente env_config_keys_prompt = Le seguenti variabili di ambiente saranno anche applicate al tuo file di configurazione: -run_user_helper = Il nome utente del sistema operativo con il quale Forgejo viene eseguito. Questo utente deve avere accesso alla cartella principale delle repository. +run_user_helper = Il nome utente del sistema operativo con il quale Forgejo viene eseguito. Questo utente deve avere accesso alla cartella principale dei repository. password_algorithm_helper = Imposta l'algoritmo di hashing della password. Gli algoritmi hanno requisiti e punti di forza diversi. L'algoritmo argon2 è relativamente sicuro ma usa un sacco di memoria e potrebbe non essere appropriato a piccoli sistemi. require_sign_in_view_popup = Limita l'accesso ad utenti autenticati. I visitatori vedranno solo le pagine di accesso e registrazione. allow_dots_in_usernames = Consenti l'uso del punto nel nome utente. Non impatta i profili già esistenti. config_location_hint = Queste opzioni di configurazione saranno salvate in: [home] -uname_holder=Nome utente o indirizzo Email +uname_holder=Nome utente o indirizzo e-mail password_holder=Password switch_dashboard_context=Cambia Dashboard Context my_repos=Repositories @@ -397,7 +398,7 @@ allow_password_change=Richiede all'utente di cambiare la password (scelta consig reset_password_mail_sent_prompt=Una email di conferma è stata inviata a %s. Per favore controlla la tua posta in arrivo nelle prossime %s per completare il processo di reset della password. active_your_account=Attiva il tuo Account account_activated=L'account è stato attivato -prohibit_login=Accedere è proibito +prohibit_login=L'accesso è proibito resent_limit_prompt=Hai già richiesto un'e-mail d'attivazione recentemente. Si prega di attenere 3 minuti e poi riprovare. has_unconfirmed_mail=Ciao %s, hai un indirizzo di posta elettronica non confermato (%s). Se non hai ricevuto una e-mail di conferma o vuoi riceverla nuovamente, fare clic sul pulsante qui sotto. resend_mail=Clicca qui per inviare nuovamente l'e-mail di attivazione @@ -442,16 +443,16 @@ authorization_failed=Autorizzazione fallita sspi_auth_failed=Autenticazione SSPI fallita password_pwned_err=Impossibile completare la richiesta a HaveIBeenPwned authorization_failed_desc = L'autorizzazione è fallita perchè abbiamo rilevato una richiesta non valida. Contatta il gestore dell'app che hai provato ad autorizzare. -change_unconfirmed_email = Se hai fornito l'indirizzo email sbagliato durante la registrazione, puoi cambiarlo sotto, e una mail di conferma sarà inviata al nuovo indirizzo. -change_unconfirmed_email_error = Impossibile cambiare l'indirizzo email: %v +change_unconfirmed_email = Se hai fornito l'indirizzo e-mail sbagliato durante la registrazione, puoi cambiarlo sotto, e una mail di conferma sarà inviata al nuovo indirizzo. +change_unconfirmed_email_error = Impossibile cambiare l'indirizzo e-mail: %v invalid_code_forgot_password = Il tuo codice di conferma non è valido oppure è scaduto. Clicca qui per avviare una nuova sessione. remember_me.compromised = Il token di login non è più valido, il che potrebbe indicare un profilo compromesso. Verifica la presenza di attività insolite dal tuo profilo. sign_up_successful = Il profilo è stato creato con successo. Benvenuto! -change_unconfirmed_email_summary = Modifica l'indirizzo email a cui deve essere inviata la mail di attivazione. +change_unconfirmed_email_summary = Modifica l'indirizzo e-mail a cui deve essere inviata la mail di attivazione. invalid_password = La tua password non combacia con la password usata in fase di creazione del profilo. reset_password_wrong_user = Hai eseguito l'accesso come %s, ma il link per il ripristino del profilo è destinato a %s last_admin = Non puoi rimuovere l'ultimo amministratore. Deve esserci almeno un amministratore. -prohibit_login_desc = Al tuo proflio non è consentito effettuare il login, contatta l'amministratore del sito. +prohibit_login_desc = Al tuo profilo non è consentito effettuare l'accesso, contatta l'amministratore del sito. openid_signin_desc = Inserisci il tuo URI OpenID. Per esempio: alice.openid.example.org o https://openid.example.org/alice. password_pwned = La password che hai scelto è in un elenco di password rubate precedentemente esposte a violazioni di dati pubblici. Riprova con una password diversa e valuta di modificare questa password anche altrove. tab_signup = Registrati @@ -546,7 +547,7 @@ TeamName=Nome Team AuthName=Nome autorizzazione AdminEmail=Email dell'Admin -NewBranchName=Nuovo nome del branch +NewBranchName=Nuovo nome del ramo CommitSummary=Riepilogo dei commit CommitMessage=Messaggio di commit CommitChoice=Scelta di commit @@ -607,7 +608,7 @@ invalid_ssh_principal=Principal non valido: %s auth_failed=Autenticazione non riuscita: %v -target_branch_not_exist=Il ramo (branch) di destinazione non esiste. +target_branch_not_exist=Il ramo di destinazione non esiste. org_still_own_packages = Questa organizzazione è ancora proprietaria di uno o più pacchetti, devi prima eliminarli. org_still_own_repo = Questa organizzazione è ancora proprietaria di una o più repository, devi prima eliminarle o trasferirle. still_own_packages = Il tuo profilo è ancora proprietario di uno o più pacchetti, devi prima eliminarli. @@ -618,15 +619,24 @@ username_error = ` può solo contenere caratteri alfanumerici ("0-9","a-z","A-Z" invalid_group_team_map_error = ` mappatura non valida: %s` organization_leave_success = Hai lasciato con successo l'organizzazione %s. unable_verify_ssh_key = Non è stato possibile verificare la chiave SSH, ricontrollala per eventuali errori. -admin_cannot_delete_self = Non puoi eliminare il tuo account mentre sei un amministratore. Devi prima abbandonare i tuoi privilegi di amministratore. +admin_cannot_delete_self = Non puoi eliminare il tuo profilo mentre sei un amministratore. Devi prima abbandonare i tuoi privilegi di amministratore. username_error_no_dots = ` può solo contenere caratteri alfanumerici ("0-9","a-z","A-Z"), trattini ("-") e underscore ("_"). Non può iniziare o finire con caratteri non-alfanumerici, e sono vietati anche più caratteri non-alfanumerici consecutivi.` username_has_not_been_changed = Il nome utente non è stato cambiato must_use_public_key = La chiave che hai fornito è una chiave privata. Non caricare la tua chiave privata da nessuna parte. Usa invece la tua chiave pubblica. still_own_repo = Il tuo profilo è ancora proprietario di una o più repository, devi prima eliminarle o trasferirle. duplicate_invite_to_team = L'utente è già stato invitato ad essere un membro del team. still_has_org = Il tuo profilo è ancora membro di una o più organizzazioni, devi prima abbandonarle. -unsupported_login_type = Il tipo di accesso non è supportato per cancellare il profilo. +unsupported_login_type = Il tipo di accesso non è supportato per l'eliminazione del profilo. unset_password = L'utente non ha impostato la password. +required_prefix = L'input deve iniziare con "%s" +Description = Descrizione +Pronouns = Pronomi +Biography = Biografia +Website = Sito web +Location = Posizione +AccessToken = Token di accesso +FullName = Nome e cognome +To = Nome del ramo [user] @@ -646,7 +656,7 @@ disabled_public_activity=L'utente ha disabilitato la vista pubblica dell'attivit joined_on = Membro dal %s block_user = Blocca utente block_user.detail_1 = Questo utente non ti seguirà più. -block_user.detail_2 = Questo utente non potrà interagire con le tue repository, con i problemi che hai creato o con i tuoi commenti. +block_user.detail_2 = Quest'utente non potrà interagire con le tue repository, con le segnalazioni che hai aperto né con i tuoi commenti. block_user.detail_3 = Questo utente non ti potrà aggiungere come un collaboratore, né potrai tu aggiungerlo come un collaboratore. code = Codice block = Blocca @@ -660,6 +670,8 @@ form.name_chars_not_allowed = Il nome utente "%s" contiene caratteri non validi. block_user.detail = Tieni presente che se blocchi questo utente, verranno eseguite altre azioni. Per esempio: form.name_pattern_not_allowed = La sequenza "%s" non è consentita in un nome utente. follow_blocked_user = Non puoi seguire questo utente perchè hai bloccato questo utente o perchè questo utente ha bloccato te. +followers_one = %d seguace +following_one = %d seguito [settings] @@ -767,8 +779,8 @@ manage_gpg_keys=Gestisci chiavi GPG add_key=Aggiungi chiave ssh_desc=Queste chiavi SSH pubbliche sono associate al tuo profilo. Le corrispondenti chiavi private consentono l'accesso completo ai tuoi progetti. Le chiavi SSH che sono state verificate possono essere usate per verificare commit Git firmati tramite SSH. principal_desc=Questi certificati SSH principali sono associati al tuo account e permettono l'accesso completo alle tue repository. -gpg_desc=Queste chiavi GPG pubbliche sono associate con il tuo profilo e sono usate per verificare i tuoi commit. Proteggi le tue chiavi private perché permettono di firmare i commit con la tue identità. -ssh_helper= Hai bisogno di aiuto? Dai un'occhiata alla guida di GitHub percrea le tue chiavi SSH o risolvere problemi comuni che potresti trovare utilizzando SSH. +gpg_desc=Queste chiavi GPG pubbliche sono associate con il tuo profilo e sono usate per verificare i tuoi commit. Proteggi le tue chiavi private perché permettono di firmare i commit con la tua identità. +ssh_helper= Hai bisogno di aiuto? Dài un'occhiata alla guida percreare le tue chiavi SSH o risolvere quei problemi comuni in cui potresti imbatterti utilizzando SSH. gpg_helper=Hai bisogno di aiuto? Dai un'occhiata alla guida di GitHub riguardo il GPG. add_new_key=Aggiungi chiave SSH add_new_gpg_key=Aggiungi chiave GPG @@ -844,7 +856,7 @@ access_token_deletion_cancel_action=Annulla access_token_deletion_confirm_action=Elimina access_token_deletion_desc=L'eliminazione di un token annullerà l'accesso al tuo account per le applicazioni che lo utilizzano. Questo non può essere annullato. Continuare? delete_token_success=Il token è stato eliminato. Le applicazioni che lo utilizzavano non hanno più accesso al tuo account. -permission_no_access=Nessun Accesso +permission_no_access=Nessun accesso permission_read=Lette manage_oauth2_applications=Gestisci applicazioni OAuth2 @@ -858,7 +870,7 @@ create_oauth2_application_button=Crea applicazione oauth2_application_name=Nome applicazione save_application=Salva oauth2_client_id=Client ID -oauth2_client_secret=Client Secret +oauth2_client_secret=Segreto client oauth2_regenerate_secret=Rigenera codice segreto oauth2_regenerate_secret_hint=Perso il codice segreto? oauth2_application_edit=Modifica @@ -896,18 +908,18 @@ manage_account_links=Gestisci gli account collegati manage_account_links_desc=Questi account esterni sono collegati al tuo account Forgejo. account_links_not_available=Attualmente non è collegato alcun account esterno al tuo account Forgejo. link_account=Collega Account -remove_account_link=Rimuovi account collegato -remove_account_link_desc=Rimuovere un account collegato ne revoca l'accesso al tuo account Forgejo. Continuare? -remove_account_link_success=L'account collegato è stato rimosso. +remove_account_link=Rimuovi Profilo Collegato +remove_account_link_desc=Rimuovere un profilo collegato ne revoca l'accesso al tuo profilo Forgejo. Continuare? +remove_account_link_success=Il profilo collegato è stato rimosso. orgs_none=Non sei membro di alcuna organizzazione. -delete_account=Elimina account +delete_account=Elimina il tuo profilo delete_prompt=Questa operazione eliminerà permanentemente il tuo account utente. NON PUÒ essere annullata. delete_with_all_comments=Il tuo account è più recente di %s giorni. Per evitare commenti fantasma, tutti i commenti relativi a issue/PR verranno eliminati con esso. confirm_delete_account=Conferma eliminazione -delete_account_title=Elimina account utente +delete_account_title=Elimina profilo utente delete_account_desc=Sei sicuro di voler rimuovere questo account utente permanentemente? email_notifications.enable=Abilita notifiche email @@ -950,11 +962,11 @@ access_token_desc = I permessi token selezionati limitano l'autorizzazione solo create_oauth2_application_success = Hai correttamente creato una nuova applicazione OAuth2. update_oauth2_application_success = Hai correttamente aggiornato l'applicazione OAuth2. oauth2_redirect_uris = URI per la reindirizzazione. Usa una nuova riga per ogni URI. -authorized_oauth2_applications_description = Hai consentito l'accesso al tuo account personale Forgejo a queste applicazioni di terze parti. Per favore, revoca l'accesso alle applicazioni che non sono più in uso. +authorized_oauth2_applications_description = Hai consentito l'accesso al tuo profilo personale Forgejo a queste applicazioni di terze parti. Puoi revocare l'accesso alle applicazioni che non sono più in uso. revoke_oauth2_grant_success = Accesso revocato correttamente. -twofa_recovery_tip = Se perdi il tuo dispositivo potrai usare una chiave di recupero monouso per riottenere l'accesso al tuo account. +twofa_recovery_tip = Se perdi il tuo dispositivo potrai usare una chiave di recupero monouso per riottenere l'accesso al tuo profilo. twofa_scratch_token_regenerated = La tua chiave di recupero monouso è ora %s. Conservala in un posto sicuro dato che non verrà mostrata nuovamente. -webauthn_key_loss_warning = Se perdi la tua chiave di sicurezza perderai accesso al tuo account. +webauthn_key_loss_warning = Se perdi la tua chiave di sicurezza, perderai l'accesso al tuo profilo. webauthn_alternative_tip = Potresti voler configurare un metodo di autenticazione aggiuntivo. visibility.public_tooltip = Visibile a tutti visibility.limited_tooltip = Visibile solo agli utenti autenticati @@ -964,21 +976,29 @@ user_unblock_success = L'utente è stato bloccato correttamente. user_block_success = L'utente è stato bloccato correttamente. at_least_one_permission = Devi selezionare almeno un permesso per creare un token oauth2_confidential_client = Client confidenziale. Seleziona per applicazioni che tengono il segreto confidenziale, come le applicazioni web. Non selezionare per applicazioni native incluse quelle desktop e mobile. -hidden_comment_types.issue_ref_tooltip = Commenti in cui l'utente ha cambiato la branch/tag associata con l'issue +hidden_comment_types.issue_ref_tooltip = Commenti in cui l'utente ha cambiato il ramo/etichetta associati con la segnalazione add_key_success = La chiave SSH "%s" è stata aggiunta. add_gpg_key_success = La chiave GPG "%s" è stata aggiunta. add_principal_success = Il certificato principale SSH "%s" è stato aggiunto. repo_and_org_access = Accesso al progetto e all'organizzazione permissions_access_all = Tutto (publico, privato e limitato) oauth2_client_secret_hint = Il segreto non verrà mostrato nuovamente dopo che lasci o ricarichi questa pagina. Assicurati di averlo salvato. -oauth2_application_remove_description = Rimuovere un applicazione OAuth2 gli impedirà di accedere ad account utenti autorizzati su questa istanza. Continuare? +oauth2_application_remove_description = Rimuovere un'applicazione OAuth2 le impedirà di accedere a profili utenti autorizzati su questa istanza. Continuare? oauth2_application_locked = Forgejo preregistra alcune applicazioni OAuth2 all'avvio, se abilitato nella configurazione. Per prevenire comportamenti imprevisti, queste non possono essere né modificate né rimosse. Fai riferimento alla documentazione di OAuth2 per ulteriori informazioni. hooks.desc = Aggiungi richiami HTTP che saranno innescati per tutti i progetti che possiedi. repos_none = Non possiedi alcun progetto. blocked_users_none = Non ci sono utenti bloccati. -keep_email_private_popup = Questo nasconderà il tuo indirizzo email nel tuo profilo, nelle pull request e quando modifichi un file usando l'interfaccia web. I commit inoltrati non saranno modificati. Usa %s nei commit per associarli al tuo profilo. +keep_email_private_popup = Questo nasconderà il tuo indirizzo e-mail nel tuo profilo, nelle pull request e quando modifichi un file usando l'interfaccia web. I commit inoltrati non saranno modificati. Usa %s nei commit per associarli al tuo profilo. verify_gpg_key_success = La chiave GPG "%s" è stata verificata. added_on = Aggiunto su %s +additional_repo_units_hint = Incoraggia l'attivazione di sezioni aggiuntive nelle repository +update_hints = Aggiorna suggerimenti +update_hints_success = I suggerimenti sono stati aggiornati. +additional_repo_units_hint_description = Mostra un pulsante "Aggiungi più sezioni..." per le repository che non hanno tutte le sezioni disponibili aggiunte. +hints = Suggerimenti +pronouns = Pronomi +pronouns_custom = Personalizzato +pronouns_unspecified = Non specificato [repo] owner=Proprietario @@ -1013,7 +1033,7 @@ repo_lang=Lingua repo_gitignore_helper=Seleziona i template di .gitignore. repo_gitignore_helper_desc=Scegli di quali file non tenere traccia da un elenco di modelli per le lingue comuni. Gli artefatti tipici generati dagli strumenti di build di ogni lingua sono inclusi su .gitignore per impostazione predefinita. issue_labels=Etichette segnalazioni -issue_labels_helper=Seleziona un set di etichette per problemi. +issue_labels_helper=Seleziona una raccolta di etichette per le segnalazioni. license=Licenza license_helper=Seleziona un file di licenza. license_helper_desc=Una licenza governa ciò che gli altri possono e non possono fare con il tuo codice. Non sei sicuro di chi è giusto per il tuo progetto? Vedi Scegli una licenza. @@ -1028,7 +1048,7 @@ trust_model_helper_collaborator_committer=Collaboratore+Committer: Fidati delle trust_model_helper_default=Predefinito: utilizzare il modello di trust predefinito per questa installazione create_repo=Crea progetto default_branch=Ramo predefinito -default_branch_helper=Il ramo predefinito è il ramo base per le richieste di pull e i commit di codice. +default_branch_helper=Il ramo predefinito è il ramo base per le richieste di modifica e i commit. mirror_prune=Rimuovi mirror_prune_desc=Rimuovi i riferimenti di puntamento-remoto obsoleti mirror_interval=Intervallo di specchio (le unità di tempo valide sono "h", "m", "s"). 0 per disabilitare la sincronizzazione periodica. (Intervallo minimo: %s) @@ -1084,7 +1104,7 @@ template.issue_labels=Etichette segnalazioni template.one_item=Deve selezionare almeno un elemento del modello template.invalid=Devi selezionare un modello di repository -archive.issue.nocomment=Questo repository è archiviato. Non puoi commentare i problemi. +archive.issue.nocomment=Questo repository è archiviato. Non puoi commentare le segnalazioni. archive.pull.nocomment=Questo repository è archiviato. Non puoi commentare le richieste di pull. form.reach_limit_of_creation_1=Hai già raggiunto il tuo limite di %d repository. @@ -1161,15 +1181,15 @@ empty_message=Questo repository non contiene alcun contenuto. broken_message=I dati Git sottostanti a questo repository non possono essere letti. Contattare l'amministratore di questa istanza o eliminare questo repository. code=Codice -code.desc=Accedi al codice sorgente, file, commits e branches. -branch=Ramo (Branch) +code.desc=Accedi al codice sorgente, ai file, ai commit e ai rami. +branch=Ramo tree=Albero (Tree) clear_ref=`Cancella il riferimento corrente` -filter_branch_and_tag=Filtra per branch o tag +filter_branch_and_tag=Filtra per ramo o etichetta find_tag=Trova etichetta -branches=Rami (Branch) +branches=Rami tags=Tag -issues=Problemi +issues=Segnalazioni pulls=Richieste di modifica project_board=Progetti packages=Pacchetti @@ -1188,7 +1208,7 @@ file.title=%s a %s file_raw=Originale file_history=Cronologia file_view_source=Visualizza sorgente -file_view_rendered=Visualizza Renderizzato +file_view_rendered=Visualizza renderizzato file_view_raw=Vedi originale file_permalink=Permalink file_too_large=Il file è troppo grande per essere visualizzato. @@ -1224,7 +1244,7 @@ editor.cannot_edit_lfs_files=I file LFS non possono essere modificati nell'inter editor.cannot_edit_non_text_files=I file binari non possono essere modificati tramite interfaccia web. editor.edit_this_file=Modifica file editor.this_file_locked=Il file è bloccato -editor.must_be_on_a_branch=È necessario essere in un branch per eseguire o proporre modifiche su questo file. +editor.must_be_on_a_branch=È necessario essere in un ramo per eseguire o proporre modifiche su questo file. editor.fork_before_edit=È necessario effettuare il fork di questo repository per eseguire o proporre modifiche su questo file. editor.delete_this_file=Elimina file editor.must_have_write_access=È necessaria l'autorizzazione di scrittura per eseguire o proporre modifiche su questo file. @@ -1233,21 +1253,21 @@ editor.filename_help=Aggiungi una directory digitando il suo nome seguito da un editor.or=o editor.cancel_lower=Annulla editor.commit_signed_changes=Conferma modifiche firmate -editor.commit_changes=Apporta le modifiche +editor.commit_changes=Conferma le modifiche editor.add_tmpl=Aggiungi "" editor.patch=Applica Patch editor.patching=Patching: editor.new_patch=Nuova Patch editor.commit_message_desc=Aggiungi una descrizione estesa facoltativa… editor.signoff_desc=Aggiungi "firmato da" dal committer alla fine del messaggio di log di commit. -editor.commit_directly_to_this_branch=Impegnarsi direttamente con il %s branch. -editor.create_new_branch=Creare un nuovo branch per questo commit e inizia una pull request. +editor.commit_directly_to_this_branch=Fai un commit direttamente sul ramo %s. +editor.create_new_branch=Crea un nuovo ramo per questo commit e avvia una richiesta di modifica. editor.create_new_branch_np=Crea un nuovo ramo per questo commit. editor.propose_file_change=Proponi la modifica del file -editor.new_branch_name_desc=Nome del nuovo branch… +editor.new_branch_name_desc=Nome del nuovo ramo… editor.cancel=Cancellare editor.filename_cannot_be_empty=Il nome del file non può essere vuoto. -editor.file_changed_while_editing=I contenuti di questo file hanno subito dei cambiamenti da quando hai iniziato la modifica. Clicca qui per visualizzarli o Committa nuovamente i Cambiamenti per sovrascriverli. +editor.file_changed_while_editing=I contenuti di questo file hanno subito dei cambiamenti da quando hai iniziato la modifica. Clicca qui per visualizzarli o Conferma nuovamente le modifiche per sovrascriverli. editor.commit_empty_file_header=Commit di un file vuoto editor.commit_empty_file_text=Il file che stai per effettuare il commit è vuoto. Procedere? editor.no_changes_to_show=Non ci sono cambiamenti da mostrare. @@ -1256,9 +1276,9 @@ editor.push_rejected_no_message=La modifica è stata rifiutata dal server senza editor.push_rejected=La modifica è stata rifiutata dal server. Controlla Git hooks. editor.push_rejected_summary=Messaggio Di Rifiuto Completo: editor.add_subdir=Aggiungi una directory… -editor.no_commit_to_branch=Impossibile effettuare il commit direttamente sul branch perché: -editor.user_no_push_to_branch=L'utente non può effettuare il push sul branch -editor.require_signed_commit=Il branch richiede un commit firmato +editor.no_commit_to_branch=Impossibile effettuare il commit direttamente sul ramo perché: +editor.user_no_push_to_branch=L'utente non può immettere nel ramo +editor.require_signed_commit=Il ramo richiede un commit firmato editor.cherry_pick=Cherry-pick %s suto: editor.revert=Ripristina %s su: @@ -1267,7 +1287,7 @@ commits.commits=Commit commits.nothing_to_compare=Questi rami sono uguali. commits.search=Ricerca commits… commits.find=Cerca -commits.search_all=Tutti i branch +commits.search_all=Tutti i Rami commits.author=Autore commits.message=Messaggio commits.date=Data @@ -1281,10 +1301,10 @@ commits.ssh_key_fingerprint=Impronta chiave SSH commit.revert=Ripristina commit.revert-header=Ripristina: %s -commit.revert-content=Selezionare il ramo su cui ripristinare: +commit.revert-content=Seleziona il ramo sul cui ripristinare: commit.cherry-pick=Cherry-pick commit.cherry-pick-header=Cherry-pick: %s -commit.cherry-pick-content=Seleziona il ramo su cui scegliere: +commit.cherry-pick-content=Seleziona il ramo su cui fare una selezione selettiva: commitstatus.error=Errore commitstatus.pending=In sospeso @@ -1293,7 +1313,7 @@ ext_issues=Accesso a segnalazioni esterne ext_issues.desc=Collegamento al puntatore di una issue esterna. projects=Progetti -projects.desc=Gestisci problemi e pull nelle schede di progetto. +projects.desc=Gestisci segnalazioni e richieste di modifica nelle schede di progetto. projects.description=Descrizione (opzionale) projects.description_placeholder=Descrizione projects.create=Crea progetto @@ -1304,11 +1324,11 @@ projects.deletion=Elimina progetto projects.deletion_desc=Eliminare un progetto lo rimuove fra tutte le issue relative. Continuare? projects.deletion_success=Il progetto è stato cancellato. projects.edit=Modifica progetto -projects.edit_subheader=I progetti organizzano i problemi e monitorano i progressi. +projects.edit_subheader=I progetti servono a coordinare le segnalazioni e a monitorare i progressi. projects.modify=Aggiorna progetto projects.type.none=Nessuno projects.type.basic_kanban=Kanban semplice -projects.type.bug_triage=Bug Triage +projects.type.bug_triage=Bug triage projects.template.desc=Modello projects.template.desc_helper=Seleziona un modello di progetto per iniziare projects.type.uncategorized=Senza categoria @@ -1348,7 +1368,7 @@ issues.choose.get_started=Inizia issues.choose.open_external_link=Apri issues.choose.blank=Default issues.choose.blank_about=Crea un problema dal modello predefinito. -issues.no_ref=Nessun ramo/etichetta specificato +issues.no_ref=Nessun ramo/etichetta specificati issues.create=Crea segnalazione issues.new_label=Nuova etichetta issues.new_label_placeholder=Nome etichetta @@ -1379,7 +1399,7 @@ issues.change_title_at=`Titolo modificato da %s a %s< issues.change_ref_at=`ha cambiato il riferimento da %s a %s %s` issues.remove_ref_at=`riferimento rimosso %s %s` issues.add_ref_at=`aggiunto riferimento %s %s` -issues.delete_branch_at=`branch %s eliminato %s` +issues.delete_branch_at=`ramo %s eliminato %s` issues.filter_label=Etichetta issues.filter_label_exclude=`Usa alt + click/enter per escludere le etichette` issues.filter_label_no_select=Tutte le etichette @@ -1392,7 +1412,7 @@ issues.filter_assginee_no_assignee=Nessun assegnatario issues.filter_poster=Autore issues.filter_poster_no_select=Tutti gli autori issues.filter_type=Tipo -issues.filter_type.all_issues=Tutti i problemi +issues.filter_type.all_issues=Tutte le segnalazioni issues.filter_type.assigned_to_you=Assegnati a te issues.filter_type.created_by_you=Creati da te issues.filter_type.mentioning_you=Che ti riguardano @@ -1457,7 +1477,7 @@ issues.re_request_review=Revisione ri-richiesta issues.is_stale=Ci sono stati cambiamenti a questa PR da questa revisione issues.remove_request_review=Elimina richiesta revisione issues.remove_request_review_block=Impossibile rimuovere la richiesta di revisione -issues.dismiss_review=Respingi Recensione +issues.dismiss_review=Respingi recensione issues.dismiss_review_warning=Sei sicuro di voler respingere questa recensione? issues.sign_in_require_desc=Effettua l'accesso per partecipare alla conversazione. issues.edit=Modifica @@ -1467,7 +1487,7 @@ issues.label_title=Nome etichetta issues.label_description=Descrizione etichetta issues.label_color=Colore etichetta issues.label_count=%d etichette -issues.label_open_issues=%d problemi aperti +issues.label_open_issues=%d segnalazioni/richieste aperte issues.label_edit=Modifica issues.label_delete=Elimina issues.label_modify=Modifica etichetta @@ -1552,10 +1572,10 @@ issues.dependency.remove=Rimuovi issues.dependency.remove_info=Rimuovi questa dipendenza issues.dependency.added_dependency=`ha aggiunto una nuova dipendenza %s` issues.dependency.removed_dependency=`ha rimosso una dipendenza %s` -issues.dependency.pr_closing_blockedby=La chiusura di questa pull request è bloccata dai seguenti problemi -issues.dependency.issue_closing_blockedby=La chiusura di questo problema è bloccata dai seguenti problemi -issues.dependency.issue_close_blocks=Questo problema impedisce la chiusura dei seguenti problemi -issues.dependency.pr_close_blocks=Questa richiesta di pull impedisce la chiusura dei seguenti problemi +issues.dependency.pr_closing_blockedby=Questa richiesta di modifica non può essere chiusa per via delle seguenti segnalazioni +issues.dependency.issue_closing_blockedby=Questa segnalazione non può essere chiusa per via delle seguenti segnalazioni +issues.dependency.issue_close_blocks=Questa segnalazione impedisce la chiusura delle seguenti segnalazioni +issues.dependency.pr_close_blocks=Questa richiesta di modifica impedisce la chiusura delle seguenti segnalazioni issues.dependency.issue_close_blocked=Devi chiudere tutte le anomalie che bloiccano questo problema prima di chiudelo. issues.dependency.pr_close_blocked=Chiudere tutte le anomalie che bloccano la richiesta di pull prima di effettaure il merge. issues.dependency.blocks_short=Blocchi @@ -1568,8 +1588,8 @@ issues.dependency.add_error_same_issue=Non si può fare dipendere un problema da issues.dependency.add_error_dep_issue_not_exist=Il problema dipendente non esiste. issues.dependency.add_error_dep_not_exist=La dipendenza non esiste. issues.dependency.add_error_dep_exists=La dipendenza esiste già. -issues.dependency.add_error_cannot_create_circular=Non puoi creare una dipendenza con due problemi che si bloccano a vicenda. -issues.dependency.add_error_dep_not_same_repo=Entrambi i problemi devono essere nello stesso repository. +issues.dependency.add_error_cannot_create_circular=Non puoi creare una dipendenza con due segnalazioni che si bloccano a vicenda. +issues.dependency.add_error_dep_not_same_repo=Entrambe le segnalazioni devono essere nello stesso repository. issues.review.self.approval=Non puoi approvare la tua pull request. issues.review.self.rejection=Non puoi richiedere modifiche sulla tua pull request. issues.review.approve=hanno approvato queste modifiche %s @@ -1612,9 +1632,9 @@ pulls.new=Nuova richiesta di modifica pulls.view=Visualizza richiesta di modifica pulls.compare_changes=Nuova richiesta di modifica pulls.allow_edits_from_maintainers=Consenti modifiche dai manutentori -pulls.allow_edits_from_maintainers_desc=Gli utenti con accesso in scrittura al ramo base possono anche inviare a questo ramo +pulls.allow_edits_from_maintainers_desc=Gli utenti con accesso di scrittura al ramo base possono anche immettere in questo ramo pulls.allow_edits_from_maintainers_err=Aggiornamento non riuscito -pulls.compare_changes_desc=Selezione il branch su cui eseguire il merge e il branch da cui eseguire il pull. +pulls.compare_changes_desc=Seleziona il ramo su cui fondere e il ramo da cui prelevare. pulls.has_viewed_file=Visualizzato pulls.has_changed_since_last_review=Modificato dalla tua ultima recensione pulls.viewed_files_label=%[1]d / %[2]d file visti @@ -1622,20 +1642,20 @@ pulls.compare_base=unisci a pulls.compare_compare=esegui un pull da pulls.switch_comparison_type=Cambia tipo di confronto pulls.switch_head_and_base=Testa e base di commutazione -pulls.filter_branch=Filtra branch +pulls.filter_branch=Filtra ramo pulls.no_results=Nessun risultato trovato. -pulls.nothing_to_compare=Questi rami sono uguali. Non c'è alcuna necessità di creare una pull request. -pulls.nothing_to_compare_and_allow_empty_pr=Questi rami sono uguali. Questa PR sarà vuota. -pulls.has_pull_request=`Una pull request tra questi rami esiste già: %[2]s#%[3]d` +pulls.nothing_to_compare=Questi rami sono uguali. Non c'è bisogno di creare una richiesta di modifica. +pulls.nothing_to_compare_and_allow_empty_pr=Questi rami sono uguali. Questa richiesta sarà vuota. +pulls.has_pull_request=`Una richiesta di modifica fra questi rami esiste già: %[2]s#%[3]d` pulls.create=Crea richiesta di modifica -pulls.title_desc_few=vorrebbe unire %[1]d commit da %[2]s a %[3]s +pulls.title_desc_few=vuole unire %[1]d commit da %[2]s a %[3]s pulls.merged_title_desc_few=ha unito %[1]d commit da %[2]s a %[3]s %[4]s -pulls.change_target_branch_at=`cambiato il branch di destinazione da %s a %s %s` +pulls.change_target_branch_at=`cambiato il ramo di destinazione da %s a %s %s` pulls.tab_conversation=Conversazione pulls.tab_commits=Commit pulls.tab_files=File modificati pulls.reopen_to_merge=Riapri questa pull request per effettuare l'unione. -pulls.cant_reopen_deleted_branch=Questa pull request non può essere riaperta perché il branch è stato eliminato. +pulls.cant_reopen_deleted_branch=Questa richiesta di modifia non può essere riaperta perché il ramo è stato eliminato. pulls.merged=Unito pulls.manually_merged=Unito manualmente pulls.is_closed=La pull request è stata chiusa. @@ -1645,7 +1665,7 @@ pulls.still_in_progress=Ancora in corso? pulls.add_prefix=Aggiungi prefisso %s pulls.remove_prefix=Rimuovi il prefisso %s pulls.data_broken=Questa pull request è rovinata a causa di informazioni mancanti del fork. -pulls.files_conflicted=Questa pull request ha modifiche in conflitto con il branch di destinazione. +pulls.files_conflicted=Questa richiesta di modifica va in conflitto con il ramo di destinazione. pulls.is_checking=Verifica dei conflitti di fusione in corso. Riprova tra qualche istante. pulls.is_ancestor=Questo ramo è già incluso nel ramo di destinazione. Non c'è nulla da fondere. pulls.is_empty=Le modifiche di questo ramo sono già nel ramo di destinazione. Questo sarà un commit vuoto. @@ -1663,7 +1683,7 @@ pulls.reject_count_1=%d richiesta di cambiamento pulls.reject_count_n=%d richieste di cambiamento pulls.waiting_count_1=%d in attesa di revisione pulls.waiting_count_n=%d in attesa di revisione -pulls.wrong_commit_id=l'id del commit deve essere un id del commit nel ramo di destinazione +pulls.wrong_commit_id=l'ID del commit deve essere un ID del commit nel ramo di destinazione pulls.no_merge_desc=Questa pull request non può essere unita perché tutte le opzioni di merge del repository sono disattivate. pulls.no_merge_helper=Attiva le opzioni di merge nelle impostazioni del repository o unisci la pull request manualmente. @@ -1676,7 +1696,7 @@ pulls.rebase_merge_commit_pull_request=Ricostruisci quindi crea commit unito pulls.squash_merge_pull_request=Crea commit mescolato pulls.merge_manually=Unito manualmente pulls.merge_commit_id=L'ID del commit di merge -pulls.require_signed_wont_sign=Il branch richiede commit firmati ma questo merge non verrà firmato +pulls.require_signed_wont_sign=Il ramo richiede commit firmati ma questa fusione non verrà firmata pulls.invalid_merge_option=Non puoi utilizzare questa opzione di merge per questa pull request. pulls.merge_conflict=Unione non riuscita: C'è stato un conflitto durante l'operazione. Suggerimento: Prova una strategia diversa @@ -1697,11 +1717,11 @@ pulls.status_checks_failure=Alcuni controlli sono falliti pulls.status_checks_error=Alcuni controlli hanno segnalato errori pulls.status_checks_requested=Richiesto pulls.status_checks_details=Dettagli -pulls.update_branch=Aggiorna il ramo tramite merge +pulls.update_branch=Aggiorna il ramo tramite fusione pulls.update_branch_rebase=Aggiorna il ramo per cambio base -pulls.update_branch_success=Brench aggiornato con successo -pulls.update_not_allowed=Non sei abilitato ad aggiornare il branch -pulls.outdated_with_base_branch=Questo brench non è aggiornato con il branch di base +pulls.update_branch_success=Ramo aggiornato con successo +pulls.update_not_allowed=Non ti è permesso aggiornare il ramo +pulls.outdated_with_base_branch=Questo ramo non è aggiornato con il ramo di base pulls.closed_at=`chiusa questa pull request %[2]s` pulls.reopened_at=`riaperta questa pull request %[2]s` @@ -1743,8 +1763,8 @@ milestones.deletion_desc=Eliminare una pietra miliare la rimuove da tutte le rel milestones.deletion_success=La pietra miliare è stata eliminata. milestones.filter_sort.least_complete=Meno completato milestones.filter_sort.most_complete=Più completato -milestones.filter_sort.most_issues=Più problemi -milestones.filter_sort.least_issues=Meno problemi +milestones.filter_sort.most_issues=Maggior parte delle segnalazioni +milestones.filter_sort.least_issues=Meno segnalazioni ext_wiki=Accesso al Wiki esterno @@ -1825,7 +1845,7 @@ activity.git_stats_pushed_n=hanno pushato activity.git_stats_commit_1=%d commit activity.git_stats_commit_n=%d commit activity.git_stats_push_to_branch=su %s e -activity.git_stats_push_to_all_branches=a tutti i brench. +activity.git_stats_push_to_all_branches=a tutti i rami. activity.git_stats_on_default_branch=Su %s, activity.git_stats_file_1=%d file activity.git_stats_file_n=%d file @@ -1872,7 +1892,7 @@ settings.mirror_settings.push_mirror.add=Aggiungi specchio di immissione settings.sync_mirror=Sincronizza ora settings.site=Sito web -settings.update_settings=Aggiorna impostazioni +settings.update_settings=Salva impostazioni settings.branches.update_default_branch=Aggiorna ramo predefinito settings.advanced_settings=Opzioni avanzate settings.wiki_desc=Abilita wiki del progetto @@ -1888,7 +1908,7 @@ settings.external_tracker_url=URL del tracciatore di segnalazioni esterno settings.external_tracker_url_error=L'URL del tracciatore di issue esterno non è un URL valido. settings.external_tracker_url_desc=I visitatori verranno reindirizzati all'URL del tracciatore di issue esterno cliccando sulla scheda delle issue. settings.tracker_url_format=Formato URL del gestore segnalazioni esterno -settings.tracker_url_format_error=L'URL del tracker di problemi esterno non è un URL valido. +settings.tracker_url_format_error=L'URL del tracciatore esterno delle segnalazioni non è un URL valido. settings.tracker_issue_style=Formato numerico del tracciatore di segnalazioni esterno settings.tracker_issue_style.numeric=Numerico settings.tracker_issue_style.alphanumeric=Alfanumerico @@ -1901,8 +1921,8 @@ settings.allow_only_contributors_to_track_time=Consenti soltanto ai contributori settings.pulls_desc=Abilita le richieste di modifica del progetto settings.pulls.ignore_whitespace=Ignora gli spazi bianchi per evitare conflitti settings.pulls.enable_autodetect_manual_merge=Abilita il rilevamento automatico della fusione manuale (Nota: in alcuni casi speciali possono verificarsi errori) -settings.pulls.allow_rebase_update=Abilita l'aggiornamento del ramo pull request per rebase -settings.pulls.default_delete_branch_after_merge=Elimina il ramo pull request dopo la fusione per impostazione predefinita +settings.pulls.allow_rebase_update=Abilita l'aggiornamento del ramo della richiesta per rebase +settings.pulls.default_delete_branch_after_merge=Elimina il ramo della richiesta dopo la fusione per impostazione predefinita settings.packages_desc=Abilita registro dei pacchetti del progetto settings.projects_desc=Abilita progetti del progetto settings.admin_settings=Impostazioni amministratore @@ -1913,7 +1933,7 @@ settings.admin_indexer_commit_sha=Ultimo SHA indicizzato settings.admin_indexer_unindexed=Non indicizzato settings.reindex_button=Aggiungi alla coda di re-indicizzazione settings.reindex_requested=Re-indicizzazione richiesta -settings.admin_enable_close_issues_via_commit_in_any_branch=Chiudi un issue tramite un commit eseguito in un branch non predefinito +settings.admin_enable_close_issues_via_commit_in_any_branch=Chiudi una segnalazione tramite un commit eseguito su un ramo non predefinito settings.danger_zone=Zona pericolosa settings.new_owner_has_same_repo=Il nuovo proprietario ha già un repository con lo stesso nome. Per favore scegli un altro nome. settings.convert=Converti in un progetto regolare @@ -2022,9 +2042,9 @@ settings.event_send_everything=Tutti gli eventi settings.event_choose=Eventi personalizzati… settings.event_header_repository=Eventi del progetto settings.event_create=Crea -settings.event_create_desc=Branch o tag creato. +settings.event_create_desc=Ramo o etichetta creati. settings.event_delete=Elimina -settings.event_delete_desc=Branch o tag eliminati. +settings.event_delete_desc=Ramo o etichetta eliminati. settings.event_fork=Fork settings.event_fork_desc=Repository forkato. settings.event_wiki=Wiki @@ -2040,7 +2060,7 @@ settings.event_issues_desc=Issue aperto, chiuso, riaperto o modificato. settings.event_issue_assign=Segnalazione assegnata settings.event_issue_assign_desc=Issue assegnata o non assegnata. settings.event_issue_label=Segnalazione etichettata -settings.event_issue_label_desc=Etichette dei Problemi aggiornate o cancellate. +settings.event_issue_label_desc=Etichette delle segnalazioni aggiornate o cancellate. settings.event_issue_milestone=Segnalazione risolta settings.event_issue_milestone_desc=Obiettivo raggiunto o abbandonato. settings.event_issue_comment=Commento segnalazione @@ -2062,7 +2082,7 @@ settings.event_pull_request_sync=Richiesta di modifica sincronizzata settings.event_pull_request_sync_desc=Pull request sincronizzata. settings.event_package=Pacchetto settings.event_package_desc=Pacchetto creato o eliminato in un repository. -settings.branch_filter=Filtro branch +settings.branch_filter=Filtro rami settings.branch_filter_desc=Whitelist dei rami per gli eventi di spinta, creazione dei rami e cancellazione dei rami, specificati come modello globo. Se vuoto o *, gli eventi per tutti i rami sono segnalati. Vedi la documentazione github.com/gobwas/glob per la sintassi. Esempi: master, {master,release*}. settings.active=Attivo settings.active_helper=Le informazioni sugli eventi innescati saranno inviate a questo URL del webhook. @@ -2336,8 +2356,8 @@ error.csv.unexpected=Impossibile visualizzare questo file perché contiene un ca error.csv.invalid_field_count=Impossibile visualizzare questo file perché ha un numero errato di campi alla riga %d. pulls.cmd_instruction_merge_desc = Unisci le modifiche e aggiornale su Forgejo. pulls.cmd_instruction_merge_title = Merge -pulls.cmd_instruction_checkout_desc = Dalla tua repository del progetto, accedi ad un nuovo ramo e prova le modifiche. -milestones.new_subheader = I traguardi possono aiutarti ad organizzare i problemi e a tracciare i loro progressi. +pulls.cmd_instruction_checkout_desc = Dalla tua repository del progetto, accedi a un nuovo ramo e prova le modifiche. +milestones.new_subheader = I traguardi possono aiutarti ad organizzare le segnalazioni e a tracciarne i progressi. activity.navbar.contributors = Contributori migrate.cancel_migrating_title = Annulla migrazione more_operations = Ulteriori operazioni @@ -2361,14 +2381,14 @@ from_comment = (commento) executable_file = File eseguibile commits.browse_further = Esplora di più commitstatus.success = Successo -projects.column.edit = Modifica Colonna -projects.column.new_submit = Crea Colonna -projects.column.new = Nuova Colonna -projects.column.set_default = Imposta Default +projects.column.edit = Modifica colonna +projects.column.new_submit = Crea colonna +projects.column.new = Nuova colonna +projects.column.set_default = Imposta default projects.column.unset_default = Annulla Default -projects.column.delete = Elimina Colonna -projects.card_type.desc = Anteprima Carte -projects.card_type.text_only = Solo Testo +projects.column.delete = Elimina colonna +projects.card_type.desc = Anteprima carte +projects.card_type.text_only = Solo testo issues.filter_milestone_open = Traguardi aperti issues.filter_milestone_closed = Traguardi chiusi issues.filter_milestone_all = Tutti i traguardi @@ -2378,7 +2398,7 @@ issues.label_exclusive = Esclusivo pulls.made_using_agit = AGit milestones.create_success = Il traguardo "%s" è stato creato. search.fuzzy.tooltip = Includi risultati di ricerca che combaciano anche approssimativamente al termine di ricerca -default_branch_label = default +default_branch_label = predefinito pulls.cmd_instruction_checkout_title = Checkout pull.deleted_branch = (eliminato):%s issues.filter_label_select_no_label = Senza etichette @@ -2398,7 +2418,7 @@ wiki.reserved_page = La nome della pagina della wiki "%s" è riservato. wiki.delete_page_notice_1 = La rimozione della pagina della wiki "%s" non può essere annullata. Continuare? settings.webhook.test_delivery_desc_disabled = Per testare questo richiamo HTTP con un evento finto, attivalo. settings.protected_branch_duplicate_rule_name = Esiste già una regola per questo insieme di rami -rss.must_be_on_branch = Devi essere su ramo per avere un feed RSS. +rss.must_be_on_branch = Devi essere su un ramo per avere un feed RSS. admin.manage_flags = Gestisci flag admin.enabled_flags = Flag abilitate per il progetto: admin.update_flags = Aggiorna flag @@ -2417,18 +2437,18 @@ migrate.invalid_local_path = Il percorso locale è invalido. Non esiste o non è migrate.migrating_failed.error = Impossibile migrare: %s migrate.forgejo.description = Migra dati da codeberg.org o da altre istanze Forgejo. cite_this_repo = Cita questo progetto -file_follow = Segui Symlink +file_follow = Segui symlink invisible_runes_header = `Questo file contiene caratteri Unicode invisibili` ambiguous_runes_header = `Questo file contiene caratteri Unicode ambigui` ambiguous_runes_description = `Questo file contiene caratteri Unicode che potrebbero essere confusi con altri caratteri. Se pensi che questo sia intenzionale puoi tranquillamente ignorare questo avviso. Usa il tasto Escape per rivelarli.` vendored = Vendored generated = Generato commit.contained_in = Questo commit è contenuto in: -commit.contained_in_default_branch = Questo commit è parte del ramo predefinito +commit.contained_in_default_branch = Questo commit fa parte del ramo predefinito commit.load_referencing_branches_and_tags = Carica rami ed etichette che fanno riferimento a questo commit editor.fail_to_apply_patch = Impossibile applicare toppa "%s" -editor.new_branch_name = Dai un nome al nuovo ramo per questo commit -editor.branch_already_exists = Il ramo "%s" esiste già nel progetto. +editor.new_branch_name = Dài un nome al nuovo ramo per questo commit +editor.branch_already_exists = Il ramo "%s" esiste già nella repo. editor.directory_is_a_file = Il nome cartella "%s" è già usato come nome file in questo progetto. editor.file_is_a_symlink = `"%s" è un collegamento simbolico. I collegamenti simbolici non possono essere modificati nell'editor web` editor.filename_is_a_directory = Il nome file "%s" è già usato come nome cartella in questo progetto. @@ -2437,7 +2457,7 @@ editor.file_deleting_no_longer_exists = Il file in eliminazione, "%s", non esist editor.file_already_exists = Un file chiamato "%s" esiste già in questo progetto. editor.fail_to_update_file = Impossibile aggiornare/creare il file "%s". editor.upload_file_is_locked = Il file "%s" è bloccato da %s. -editor.cannot_commit_to_protected_branch = Non si può fare commit sul ramo protetto "%s". +editor.cannot_commit_to_protected_branch = Non si possono fare commit sul ramo protetto "%s". commits.search.tooltip = Puoi prefissare parole chiave con "autore:", "committer:", "dopo:", o "prima:", esempio "ripristino autore:Alice prima:2019-01-13". issues.filter_project_all = Tutti i progetti issues.label_exclusive_desc = Dai all'etichetta il nome ambito/oggetto per renderla mutualmente esclusiva con altre etichette in ambito/. @@ -2461,7 +2481,7 @@ pulls.filter_changes_by_commit = Filtra per commit pulls.nothing_to_compare_have_tag = I rami/etichette selezionati sono uguali. pulls.merged_success = Richiesta di modifica fusa correttamente e chiusa pulls.closed = Richiesta di modifica chiusa -pulls.merged_info_text = Il ramo %s può essere eliminato ora. +pulls.merged_info_text = Il ramo %s può ora essere eliminato. pulls.blocked_by_user = Non puoi creare una richiesta di modifica in questo progetto perché sei bloccato dal proprietario. pulls.status_checks_hide_all = Nascondi tutti i controlli pulls.status_checks_show_all = Mostra tutti i controlli @@ -2470,7 +2490,7 @@ pulls.close = Chiudi la richiesta di modifica pulls.reopen_failed.head_branch = La richiesta di modifica non può essere riaperta perché il ramo genitore non esiste più. pulls.clear_merge_message = Cancella messaggio di fusione pulls.reopen_failed.base_branch = La richiesta di modifica non può essere riaperta perché il ramo di base non esiste più. -pulls.agit_explanation = Creata usando flusso di lavoro AGit. AGit permette ai contributori di proporre modifiche usando "git push" senza creare una derivazione o un nuovo ramo. +pulls.agit_explanation = Creata usando il flusso di lavoro AGit. AGit permette a chi contribuisce di proporre modifiche usando "git push" senza creare una derivazione o un nuovo ramo. pulls.recently_pushed_new_branches = Hai immesso sul ramo %[1]s %[2]s milestones.filter_sort.earliest_due_data = Scadenza più vicina signing.wont_sign.twofa = Devi avere la verifica a due fattori abilitata per firmare i commit. @@ -2485,7 +2505,7 @@ activity.navbar.recent_commits = Commit recenti contributors.contribution_type.filter_label = Tipi di contributo: search.type.tooltip = Cerca tipo search.match.tooltip = Includi solo risultati che corrispondono esattamente ai termini di ricerca -settings.mirror_settings.docs.disabled_push_mirror.instructions = Imposta il tuo progetto in modo che prelevi commit, etichette e rami da un altro progetto. +settings.mirror_settings.docs.disabled_push_mirror.instructions = Imposta il tuo progetto in modo che prelevi commit, etichette e rami da un'altra repo. settings.mirror_settings.docs.no_new_mirrors = Il tuo progetto sta specchiando i cambiamenti a/da un altro progetto. Tieni a mente che non puoi creare nuovi specchi al momento. settings.mirror_settings.docs.can_still_use = Nonostante tu non possa modificare specchi esistenti o crearne di nuovi puoi comunque il tuo specchio esistente. settings.mirror_settings.docs.pull_mirror_instructions = Per impostare uno specchio di prelievo consulta: @@ -2495,13 +2515,13 @@ settings.mirror_settings.docs.pulling_remote_title = Prelievo da un progetto rem settings.transfer_abort_success = Il trasferimento del progetto a %s è stato correttamente cancellato. settings.enter_repo_name = Inserisci il proprietario e il nome del progetto esattamente come mostrato: settings.confirmation_string = Stringa di conferma -settings.wiki_rename_branch_main = Normalizza il nome del ramo della wiki -settings.wiki_rename_branch_main_desc = Rinomina il ramo usato internamente dalla wiki come "%s". Questo è permanente è non può essere annullato. +settings.wiki_rename_branch_main = Normalizza il nome del ramo della Wiki +settings.wiki_rename_branch_main_desc = Rinomina il ramo usato internamente dalla Wiki in "%s". L'operazione è permanente è non può essere annullata. settings.wiki_rename_branch_main_notices_1 = Questa operazione NON PUÒ essere annullata. -settings.wiki_branch_rename_success = Il nome del ramo della wiki del progetto è stato normalizzato correttamente. -settings.wiki_branch_rename_failure = Impossibile normalizzare il nome del ramo della wiki del progetto. -settings.confirm_wiki_branch_rename = Rinomina ramo della wiki -settings.wiki_rename_branch_main_notices_2 = Questo rinominerà permanentemente il ramo interno della wiki del progetto di %s. Passaggi esistenti dovranno essere aggiornati. +settings.wiki_branch_rename_success = Il nome del ramo della wiki della repo è stato normalizzato correttamente. +settings.wiki_branch_rename_failure = Impossibile normalizzare il nome del ramo della wiki della repo. +settings.confirm_wiki_branch_rename = Rinomina il ramo della wiki +settings.wiki_rename_branch_main_notices_2 = Ciò rinominerà permanentemente il ramo interno della wiki della repo di %s. Passaggi esistenti dovranno essere aggiornati. settings.add_collaborator_blocked_our = Non si può aggiungere il collaboratore perché il proprietario del progetto lo ha bloccato. settings.webhook.replay.description_disabled = Per riprodurre questo richiamo HTTP, attivalo. settings.event_wiki_desc = Pagina wiki creata, rinominata, modificata o rimossa. @@ -2523,9 +2543,9 @@ issues.role.contributor_helper = Questo utente ha precedentemente fatto commit a pulls.blocked_by_official_review_requests = Questa richiesta di modifica è bloccata perché manca l'approvazione di uno o più revisori ufficiali. pulls.blocked_by_changed_protected_files_1 = Questa richiesta di modifica è bloccata perché modifica un file protetto: pulls.blocked_by_changed_protected_files_n = Questa richiesta di modifica è bloccata perché modifica file protetti: -pulls.has_merged = Respinto: la richiesta di modifica è stata fusa, non puoi fonderla di nuovo o cambiare il ramo di destinazione. +pulls.has_merged = Errore: la richiesta di modifica è stata fusa, non puoi fonderla di nuovo o cambiare il ramo di destinazione. milestones.filter_sort.latest_due_date = Scadenza più lontana -settings.mirror_settings.docs = Imposta il tuo progetto in modo che sincronizzi automaticamente commit, etichette e rami con un altro progetto. +settings.mirror_settings.docs = Imposta la tua repo in modo che sincronizzi automaticamente commit, etichette e rami con un'altra repo. settings.mirror_settings.docs.disabled_pull_mirror.instructions = Imposta il tuo progetto in modo che immetta commit, etichette e rami in un altro progetto automaticamente. Gli specchi di prelievo sono stati disabilitati dall'amministratore del sito. settings.mirror_settings.docs.disabled_push_mirror.info = Gli specchi di immissione sono stati disabilitati dall'amministratore del sito. settings.mirror_settings.docs.more_information_if_disabled = Puoi scoprire di più riguardo specchi di prelievo e di immissione qui: @@ -2565,7 +2585,7 @@ editor.unable_to_upload_files = Impossibile caricare i file su "%s" con errore: projects.create_success = Il progetto "%s" è stato creato. projects.column.set_default_desc = Imposta questa colonna come predefinita per segnalazioni e richieste di modifica non categorizzate projects.column.unset_default_desc = Disattiva questa colonna come predefinita -projects.column.deletion_desc = Eliminare la colonna di un progetto sposta tutte le relative segnalazioni in "Non categorizzate". Continuare? +projects.column.deletion_desc = L'eliminazione della colonna di un progetto sposta tutte le relative segnalazioni nella colonna di default. Continuare? issues.choose.ignore_invalid_templates = I modelli non validi sono stati ignorati issues.choose.invalid_templates = trovati %v modello/i non valido/i issues.choose.invalid_config = La configurazione della segnalazione contiene errori: @@ -2574,11 +2594,11 @@ issues.role.member_helper = questo utente è un membro dell'organizzazione che p issues.review.pending.tooltip = Questo commento non è attualmente visibile ad altri utenti. Per inviare il tuo commento in attesa selezione "%s" -> "%s/%s/%s" in cima alla pagina. pulls.blocked_by_approvals = Questa richiesta di modifica non ha ancora sufficienti approvazioni. %d di %d approvazioni concesse. pulls.blocked_by_rejection = Questa richiesta di modifica ha modifiche richieste da un revisore ufficiale. -pulls.blocked_by_outdated_branch = Questa richiesta di modifica è bloccata perché è obsoleta. +pulls.blocked_by_outdated_branch = Questa richiesta di modifica è bloccata poiché obsoleta. pulls.fast_forward_only_merge_pull_request = Solo fast-forward signing.will_sign = Questo commit verrà firmato con la chiave "%s". signing.wont_sign.error = C'è stato un errore durante il controllo per la firma del commit. -signing.wont_sign.nokey = Non c'è una chiave disponibile per firmare questo commit. +signing.wont_sign.nokey = Questa istanza non ha una chiave con cui firmare questo commit. signing.wont_sign.never = I commit non sono mai firmati. signing.wont_sign.always = I commit sono sempre firmati. signing.wont_sign.approved = La fusione non sarà firmata dato che la RM non è approvata. @@ -2593,14 +2613,14 @@ settings.pull_mirror_sync_in_progress = Prelevando cambiamenti dal progetto remo settings.push_mirror_sync_in_progress = Immettendo cambiamenti al progetto remoto %s. settings.update_mirror_settings = Aggiorna impostazioni dello specchio settings.branches.switch_default_branch = Passa al ramo predefinito -settings.branches.add_new_rule = Aggiungi nuova regola +settings.branches.add_new_rule = Aggiungi una nuova regola settings.actions_desc = Abilita azioni del progetto settings.new_owner_blocked_doer = Il nuovo proprietario ti ha bloccato. settings.update_settings_no_unit = Ili progetto dovrebbe consentire almeno qualche tipo di interazione. settings.add_collaborator_owner = Non si può aggiungere un proprietario come collaboratore. -branch.delete_desc = Eliminare un ramo è permanente. Nonostante il ramo eliminato potrebbe continuare ad esistere per un breve periodo di tempo prima di essere realmente eliminato, l'eliminazione NON PUÒ essere annullata in molti casi. Continuare? +branch.delete_desc = L'eliminazione di un ramo è definitiva. Nonostante il ramo eliminato potrebbe continuare ad esistere per un breve periodo di tempo prima di essere realmente eliminato, l'eliminazione NON PUÒ essere annullata in molti casi. Continuare? editor.invalid_commit_mail = Email invalida per creare un commit. -editor.branch_does_not_exist = Il ramo "%s" non esiste nel progetto. +editor.branch_does_not_exist = Il ramo "%s" non esiste nella repo. issues.label_archive = Archivia etichetta issues.label_archived_filter = Mostra etichette archiviate issues.dependency.no_permission_n = Non hai il permesso di lettura per leggere %s dipendenze @@ -2635,7 +2655,7 @@ signing.wont_sign.commitssigned = La fusione non verrà firmata dato che tutti i settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning = Al momento questo può essere fatto solo nel menù "Nuova migrazione". Per ulteriori informazioni consulta: settings.pulls.default_allow_edits_from_maintainers = Consenti modifica dai manutentori in modo predefinito settings.trust_model.committer.desc = Firme valide saranno etichettate "fidata" se corrispondo all'autore del commit, altrimenti saranno etichettate "non corrisponde". Questo costringe Forgejo ad esse l'autore dei commit firmati, con il vero autore etichettato con le sequenze Co-authored-by: e Co-commited-by: nel commit. La chiave predefinita di Forgejo deve corrispondere ad un utente nella base di dati. -signing.wont_sign.pubkey = Il commit non verrà firmato perché non hai una chiave pubblica associata al tuo account. +signing.wont_sign.pubkey = Il commit non verrà firmato perché non hai una chiave pubblica associata al tuo profilo. settings.releases_desc = Abilita rilasci del progetto settings.unarchive.text = Disarchiviare il progetto ripristinerà la sua abilità di ricevere commit e immissioni, oltre che nuove segnalazioni e richieste di modifica. settings.unarchive.success = Il progetto è stato disarchiviato correttamente. @@ -2649,7 +2669,7 @@ tag.ahead.target = a %s da questa etichetta release.tag_helper_new = Nuova etichetta. Questa etichetta sarà creata dalla destinazione. release.tag_helper_existing = Etichetta esistente. release.deletion_desc = La rimozione di un rilascio lo rimuove solo da Forgejo. Non influenza l'etichetta Git, i contenuti del tuo progetto o la sua cronologia. Continuare? -branch.already_exists = Un ramo chiamato "%s" esiste già. +branch.already_exists = Esiste già un ramo chiamato "%s". settings.merge_style_desc = Modalità di fusione settings.remove_protected_branch_failed = Impossibile rimuovere la regola di protezione del ramo "%s". settings.remove_protected_branch_success = La regola di protezione del ramo "%s" è stata rimossa. @@ -2680,6 +2700,39 @@ settings.protect_unprotected_file_patterns_desc = File non protetti dei quali è settings.protect_protected_file_patterns_desc = I file non protetti non possono essere modificati direttamente neanche se l'utente ha il permesso di aggiungere, modificare o eliminare file in questo ramo. Più sequenze possono essere separate usando il punto e virgola (";"). Vedi la documentazione su github.com/gobwas/glob per la sintassi della sequenze. Esempi: .drone.yml, /docs/**/*.txt. settings.protect_no_valid_status_check_patterns = Nessuna sequenza valida per il controllo dello stato. settings.event_pull_request_review_request_desc = Richiesta la revisione della richiesta di modifica o richiesta di revisione rimossa. +stars = Stelle +issues.num_participants_one = %d partecipante +open_with_editor = Apri con %s +n_commit_few = %s commit +n_branch_one = %s ramo +n_tag_few = %s tag +settings.web_hook_name_sourcehut_builds = Build SourceHut +settings.sourcehut_builds.manifest_path = Percorso manifest della build +settings.sourcehut_builds.visibility = Visibilità attività +settings.sourcehut_builds.secrets = Segreti +settings.enforce_on_admins_desc = Gli amministratori del repository non possono bypassare questa regola. +size_format = %[1]s: %[2]s; %[3]s: %[4]s +editor.push_out_of_date = Il push sembra essere obsoleto. +issues.archived_label_description = (Archiviato) %s +settings.sourcehut_builds.secrets_helper = Fornisci l'accesso ai segreti della build all'incarico (richiede il permesso SECRETS:RO) +settings.add_webhook.invalid_path = Il percorso non deve contenere dei componenti quali ".", "..", o una stringa vuota. Non può iniziare o finire con uno slash. +n_commit_one = %s commit +settings.sourcehut_builds.graphql_url = URL GraphQL (e.g. https://builds.sr.ht/query) +settings.enforce_on_admins = Imponi questa regola agli amministratori del repository +release.download_count_one = %s download +release.download_count_few = %s downloads +release.system_generated = Questo allegato è stato generato automaticamente. +pulls.ready_for_review = Pronto alla revisione? +editor.commit_id_not_matching = L'ID del commit non combacia con quello del commit che stavi modificando. Conferma le tue modifiche su un nuovo ramo, poi fondilo col ramo desiderato. +n_branch_few = %s rami +n_tag_one = %s tag +commits.search_branch = Questo Ramo +settings.rename_branch_failed_protected = Non è possibile rinominare il ramo %s perché è un ramo protetto. +settings.event_pull_request_enforcement = Imposizione +settings.matrix.room_id_helper = L'ID della Stanza può essere ricavato dal web client Element > Stanza > Impostazioni > Avanzato > ID interno stanza. Esempio: %s. +settings.graphql_url = URL GraphQL +settings.sourcehut_builds.access_token_helper = Token di accesso con grant JOBS:RW. Genera un token builds.sr.ht o un token builds.sr.ht con accesso ai segreti su meta.sr.ht. +settings.matrix.access_token_helper = È consigliata l'impostazione di un account Matrix dedicato per questa funzione. Il token di accesso può essere prelevato dal web client Element (in una pagina privata/incognito) > Menu utente (in alto a sinistra) > Tutte le impostazioni > Aiuto e informazioni > Avanzato > Token di accesso (sotto all'URL del Homeserver). Chiudi la pagina privata/incognito (La disconnessione invaliderebbe il token). [graphs] contributors.what = contribuzioni @@ -2902,7 +2955,7 @@ dashboard.delete_old_actions.started=Elimina tutte le vecchie azioni dal databas dashboard.update_checker=Controllore dell'aggiornamento dashboard.delete_old_system_notices=Elimina tutte le vecchie notifiche di sistema dal database -users.user_manage_panel=Gestici account utente +users.user_manage_panel=Gestici profili utente users.new_account=Crea account utente users.name=Nome utente users.full_name=Nome completo @@ -2937,7 +2990,7 @@ users.delete_account=Elimina profilo utente users.cannot_delete_self=Non puoi eliminare te stesso users.still_own_repo=Questo utente possiede ancora una o più repository. Eliminare o trasferire questi repository prima di continuare. users.still_has_org=Questo utente è membro di un'organizzazione. Rimuovi l'utente da tutte le organizzazioni prima di proseguire. -users.purge=Elimina Utente +users.purge=Elimina utente users.purge_help=Eliminare forzatamente l'utente e tutti i progetti, le organizzazioni e i pacchetti di proprietà dell'utente. Anche tutti i commenti e le segnalazioni verranno eliminati. users.deletion_success=L'account utente è stato eliminato. users.reset_2fa=Resetta 2FA @@ -2946,13 +2999,13 @@ users.list_status_filter.reset=Ripristina users.list_status_filter.is_active=Attivo users.list_status_filter.not_active=Inattivo users.list_status_filter.is_admin=Amministratore -users.list_status_filter.not_admin=Non Amministratore +users.list_status_filter.not_admin=Non amministratore users.list_status_filter.is_restricted=Limitato -users.list_status_filter.not_restricted=Non Limitato -users.list_status_filter.is_prohibit_login=Divieto Di Login -users.list_status_filter.not_prohibit_login=Consenti Login -users.list_status_filter.is_2fa_enabled=2FA Abilitato -users.list_status_filter.not_2fa_enabled=2FA Disabilitato +users.list_status_filter.not_restricted=Non limitato +users.list_status_filter.is_prohibit_login=Accesso vietato +users.list_status_filter.not_prohibit_login=Accesso consentito +users.list_status_filter.is_2fa_enabled=2FA abilitato +users.list_status_filter.not_2fa_enabled=2FA disabilitato emails.email_manage_panel=Gestisci email dell'utente emails.primary=Primario @@ -2981,7 +3034,7 @@ repos.private=Privati repos.watches=Segue repos.stars=Voti repos.forks=Fork -repos.issues=Problemi +repos.issues=Segnalazioni repos.size=Dimensione packages.package_manage_panel=Gestisci pacchetti @@ -3027,7 +3080,7 @@ auths.attribute_surname=Attributo cognome auths.attribute_mail=Attributo email auths.attribute_ssh_public_key=Attributo chiave SSH pubblica auths.attribute_avatar=Attributo avatar -auths.attributes_in_bind=Estrai attributi dal contesto nind DN +auths.attributes_in_bind=Estrai attributi dal contesto bind DN auths.allow_deactivate_all=Consenti un risultato di ricerca vuoto per disattivare tutti gli utenti auths.use_paged_search=Utilizza ricerca per pagina auths.search_page_size=Dimensioni pagina @@ -3130,7 +3183,7 @@ config.repo_root_path=Percorso radice del progetto config.lfs_root_path=Percorso radice LFS config.log_file_root_path=Percorso dei log config.script_type=Tipo di script -config.reverse_auth_user=Autenticazione utente inversa +config.reverse_auth_user=Utente autenticazione reverse proxy config.ssh_config=Configurazione SSH config.ssh_enabled=Attivo @@ -3175,7 +3228,7 @@ config.default_keep_email_private=Nascondi indirizzo email in modo predefinito config.default_allow_create_organization=Consenti la creazione di organizzazioni in modo predefinito config.enable_timetracking=Abilita il cronografo config.default_enable_timetracking=Attiva il cronografo di default -config.allow_dots_in_usernames = Consenti l'uso del punto nel nome utente. Non impatta gli account già esistenti. +config.allow_dots_in_usernames = Consenti l'uso del punto nel nome utente. Non impatta i profili già esistenti. config.default_allow_only_contributors_to_track_time=Consenti soltanto ai contributori di utilizzare il cronografo config.no_reply_address=Dominio email nascosto config.default_visibility_organization=Visibilità predefinita per le nuove organizzazioni @@ -3310,7 +3363,7 @@ dashboard.sync_repo_tags = Sincronizza etichette dai dati Git alla base di dati users.new_success = Il profilo utente "%s" è stato creato. users.still_own_packages = Questo utente possiede ancora uno o più pacchetti, elimina questi pacchetti prima. auths.oauth2_map_group_to_team = Associa gruppi reclamati a squadre di organizzazioni. (opzionale - richiede il nome reclamo sopra) -auths.tip.gitea = Registra una nuova applicazione OAuth2. La guida può essere trovata a https://docs.gitea.com/development/oauth2-provider +auths.tip.gitea = Registra una nuova applicazione OAuth2. La guida può essere trovata a https://forgejo.org/docs/latest/user/oauth2-provider config.test_mail_sent = Una email di prova è stata inviata a "%s". monitor.processes_count = %d processi monitor.download_diagnosis_report = Scarica relazione diagnostica @@ -3320,7 +3373,7 @@ packages.cleanup = Pulisci dati scaduti dashboard.cleanup_actions = Pulisci log scaduti e artefatti dalle azioni dashboard.stop_endless_tasks = Termina attività senza fine dashboard.start_schedule_tasks = Inizia pianificazione attività -dashboard.cancel_abandoned_jobs = Cancella lavori abbandonati +dashboard.cancel_abandoned_jobs = Cancella incarichi abbandonati auths.login_source_exist = La fonte di autenticazione "%s" esiste già. auths.invalid_openIdConnectAutoDiscoveryURL = URL di auto scoperta invalido (questo deve essere un URL valido che inizia con http:// o con https://) config.access_log_template = Modello log di accesso @@ -3351,6 +3404,10 @@ monitor.queue.settings.remove_all_items_done = Tutti gli elementi in coda sono s self_check.database_collation_mismatch = Pretendi che la base di dati usi la collazione: %s self_check.database_fix_mysql = Per utenti MySQL/MariaDB, potresti usare il comando "gitea doctor convert" per risolvere problemi di collazione, o potresti risolvere il problema manualmente tramite SQL con "ALTER ... COLLATE ...". self_check.database_collation_case_insensitive = La base di dati sta usando la collazione %s, che è una collazione insensibile. Nonostante Forgejo potrebbe lavorarci, ci potrebbero essere rari casi che non vanno come previsto. +auths.tip.gitlab_new = Registra una nuova applicazione su https://gitlab.com/-/profile/applications +config_summary = Riepilogo +config.open_with_editor_app_help = L'editor delle opzioni "Apri con" per il menu di clone. Se lasciato vuoto, verranno usati i default. Espandi per vedere i default. +config_settings = Impostazioni [action] @@ -3676,7 +3733,7 @@ runners.new_notice = Come avviare un esecutore runners.new = Crea un nuovo esecutore variables.update.success = La variabile è stata modificata. variables.update.failed = Impossibile modificare la variabile. -variables.creation.failed = Impossibile aggiungere variabile. +variables.creation.failed = Errore nell'aggiunta della variabile. variables.deletion.success = La variabile è stata rimossa. variables.deletion.failed = Impossibile rimuovere la variabile. variables.edit = Modifica variabile @@ -3699,6 +3756,7 @@ runs.no_workflows.documentation = Per ulteriori informazioni sulle Forgejo Actio runs.no_workflows.quick_start = Non sai come iniziare con le Forgejo Actions? Vedi la guida rapida. runners.delete_runner_notice = Se un'attività è in esecuzione su questo esecutore sarà terminata ed etichettata fallito. Potrebbe rompere flussi di lavoro di costruzione. runners.task_list = Attività recenti su questo esecutore +runs.no_job_without_needs = Il flusso di lavoro deve contenere almeno un incarico senza dipendenze. @@ -3717,3 +3775,40 @@ normal_file = File normale executable_file = File eseguibile changed_filemode = %[1]s → %[2]s + + +[search] +type_tooltip = Tipo ricerca +search = Cerca... +fuzzy = Approssimativa +match = Precisa +org_kind = Cerca organizzazioni... +package_kind = Ricerca pacchetti... +code_search_unavailable = La ricerca del codice non è attualmente disponibile. Contatta l'amministratore del sito. +code_kind = Cerca codice... +team_kind = Cerca team... +code_search_by_git_grep = I risultati della ricerca del codice sono forniti da "git grep". Potrebbero esserci risultati migliori se l'amministratore del sito avesse abilitato l'indicizzatore del codice. +project_kind = Ricerca progetti... +commit_kind = Ricerca commit... +branch_kind = Ricerca rami... +no_results = Non è stato trovato alcun risultato. +keyword_search_unavailable = La ricerca per parole chiave non è attualmente disponibile. Contatta l'amministratore del sito. +runner_kind = Ricerca esecutori... +match_tooltip = Includi solo risultati che corrispondono precisamente al termine di ricerca +fuzzy_tooltip = Includi anche risultati che corrispondono approssimativamente al termine di ricerca +user_kind = Cerca utenti... +repo_kind = Cerca repository... + +[munits.data] +gib = GiB +tib = TiB +pib = PiB +kib = KiB +mib = MiB +eib = EiB +b = B + +[markup] +filepreview.lines = Linee da %[1]d a %[2]d in %[3]s +filepreview.truncated = L'anteprima è stata troncata +filepreview.line = Linea %[1]d in %[2]s \ No newline at end of file diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 3a4f414a9b..e2ac989ae0 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -318,7 +318,6 @@ password_algorithm=パスワードハッシュアルゴリズム invalid_password_algorithm=無効なパスワードハッシュアルゴリズム password_algorithm_helper=パスワードハッシュアルゴリズムを設定します。 アルゴリズムにより動作要件と強度が異なります。 argon2アルゴリズムはかなり安全ですが、多くのメモリを使用するため小さなシステムには適さない場合があります。 enable_update_checker=アップデートチェッカーを有効にする -enable_update_checker_helper=gitea.ioに接続して定期的に新しいバージョンのリリースを確認します。 env_config_keys=環境設定 env_config_keys_prompt=以下の環境変数も設定ファイルに適用されます: allow_dots_in_usernames = ユーザー名にドットを使用できるようにします。既存のアカウントには影響しません。 @@ -3106,7 +3105,7 @@ auths.tip.google_plus=OAuth2クライアント資格情報を、Google APIコン auths.tip.openid_connect=OpenID Connect DiscoveryのURL (/.well-known/openid-configuration) をエンドポイントとして指定してください auths.tip.twitter=https://dev.twitter.com/apps へアクセスしてアプリケーションを作成し、“Allow this application to be used to Sign in with Twitter”オプションを有効にしてください。 auths.tip.discord=新しいアプリケーションを https://discordapp.com/developers/applications/me から登録してください。 -auths.tip.gitea=新しいOAuthアプリケーションを登録してください。 利用ガイドは https://docs.gitea.com/development/oauth2-provider にあります +auths.tip.gitea=新しいOAuthアプリケーションを登録してください。 利用ガイドは https://forgejo.org/docs/latest/user/oauth2-provider にあります auths.tip.yandex=`https://oauth.yandex.com/client/new で新しいアプリケーションを作成してください。 "Yandex.Passport API" セクションで次の項目を許可します: "Access to email address"、"Access to user avatar"、"Access to username, first name and surname, gender"` auths.tip.mastodon=認証したいMastodonインスタンスのカスタムURLを入力してください (入力しない場合はデフォルトのURLを使用します) auths.edit=認証ソースの編集 diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 3ccc1054c8..e2ab79e3f8 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -185,7 +185,7 @@ network_error=Tīkla kļūda [startpage] app_desc=Viegli uzstādāms Git serviss install=Vienkārši instalējams -install_desc=Vienkārši jāpalaiž izpildāmais fails vajadzīgajai platformai, jāizmanto Docker, vai jāiegūst pakotne. +install_desc=Vienkārši jāpalaiž izpildāmais fails vajadzīgajai platformai, jāizmanto Docker, vai jāiegūst pakotne. platform=Pieejama dažādām platformām platform_desc=Forgejo iespējams uzstādīt jebkur, kam Go var nokompilēt: Windows, macOS, Linux, ARM utt. Izvēlies to, kas tev patīk! lightweight=Viegla @@ -301,7 +301,6 @@ password_algorithm=Paroles jaucējsummas algoritms invalid_password_algorithm=Kļūdaina paroles jaucējfunkcija password_algorithm_helper=Norādiet paroles jaucējalgoritmu. Algoritmi atšķirās pēc prasībām pret resursiem un stipruma. Argon2 algoritms ir drošs, bet tam nepieciešams daudz operatīvās atmiņas, līdz ar ko tas var nebūt piemērots sistēmām ar maz pieejamajiem resursiem. enable_update_checker=Iespējot jaunu versiju paziņojumus -enable_update_checker_helper=Periodiski pārbaudīt jaunu version pieejamību, izgūstot datus no gitea.io. env_config_keys=Vides konfigurācija env_config_keys_prompt=Šie vides mainīgie tiks pielietoti arī konfigurācijas failā: @@ -2358,7 +2357,7 @@ settings.tags.protection.allowed.teams=Atļauts komandām settings.tags.protection.allowed.noone=Nevienam settings.tags.protection.create=Aizsargāt tagus settings.tags.protection.none=Nav uzstādīta tagu aizsargāšana. -settings.tags.protection.pattern.description=Var izmantot vienkāršu nosaukumu vai glob šablonu, vai regulāro izteiksmi, lai atbilstu vairākiem tagiem. Vairāk ir lasāms aizsargāto tagu šablonu dokumentācijā. +settings.tags.protection.pattern.description=Var izmantot vienkāršu nosaukumu vai glob šablonu, vai regulāro izteiksmi, lai atbilstu vairākiem tagiem. Vairāk ir lasāms aizsargāto tagu šablonu dokumentācijā. settings.bot_token=Bota pilnvara settings.chat_id=Tērzēšanas ID settings.thread_id=Pavediena ID @@ -2910,12 +2909,12 @@ packages.size=Izmērs packages.published=Publicēts defaulthooks=Noklusētie tīmekļa āķi -defaulthooks.desc=Tīmekļa āķi automātiski nosūta HTTP POST pieprasījumus serverim, kad iestājas noteikti Gitea notikumi. Šeit pievienotie tīmekļa āķi ir noklusējuma, un tie tiks pievienoti visiem jaunajiem repozitorijiem. Vairāk ir lasāms tīmekļa āķu dokumentācijā. +defaulthooks.desc=Tīmekļa āķi automātiski nosūta HTTP POST pieprasījumus serverim, kad iestājas noteikti Gitea notikumi. Šeit pievienotie tīmekļa āķi ir noklusējuma, un tie tiks pievienoti visiem jaunajiem repozitorijiem. Vairāk ir lasāms tīmekļa āķu dokumentācijā. defaulthooks.add_webhook=Pievienot noklusēto tīmekļa āķi defaulthooks.update_webhook=Mainīt noklusēto tīmekļa āķi systemhooks=Sistēmas tīmekļa āķi -systemhooks.desc=Tīmekļa āķi automātiski nosūta HTTP POST pieprasījumus serverim, kad iestājas noteikti Gitea notikumi. Šeit pievienotie tīmekļa āķi tiks izsaukti visiem sistēmas repozitorijiem, tādēļ lūgums apsvērt to iespējamo ietekmi uz veiktspēju. Vairāk ir lasāms tīmekļa āķu dokumentācijā. +systemhooks.desc=Tīmekļa āķi automātiski nosūta HTTP POST pieprasījumus serverim, kad iestājas noteikti Gitea notikumi. Šeit pievienotie tīmekļa āķi tiks izsaukti visiem sistēmas repozitorijiem, tādēļ lūgums apsvērt to iespējamo ietekmi uz veiktspēju. Vairāk ir lasāms tīmekļa āķu dokumentācijā. systemhooks.add_webhook=Pievienot sistēmas tīmekļa āķi systemhooks.update_webhook=Mainīt sistēmas tīmekļa āķi @@ -3020,7 +3019,7 @@ auths.tip.google_plus=Iegūstiet OAuth2 klienta pilnvaru no Google API konsoles auths.tip.openid_connect=Izmantojiet OpenID pieslēgšanās atklāšanas URL (/.well-known/openid-configuration), lai norādītu galapunktus auths.tip.twitter=Dodieties uz adresi https://dev.twitter.com/apps, izveidojiet lietotni un pārliecinieties, ka ir atzīmēts “Allow this application to be used to Sign in with Twitter” auths.tip.discord=Reģistrējiet jaunu aplikāciju adresē https://discordapp.com/developers/applications/me -auths.tip.gitea=Pievienot jaunu OAuth2 lietojumprogrammu. Dokumentācija ir pieejama https://docs.gitea.com/development/oauth2-provider +auths.tip.gitea=Pievienot jaunu OAuth2 lietojumprogrammu. Dokumentācija ir pieejama https://forgejo.org/docs/latest/user/oauth2-provider auths.tip.yandex=`Izveidojiet jaunu lietotni adresē https://oauth.yandex.com/client/new. Izvēlieties sekojošas tiesības "Yandex.Passport API" sadaļā: "Access to email address", "Access to user avatar" un "Access to username, first name and surname, gender"` auths.tip.mastodon=Norādiet pielāgotu mastodon instances URL, ar kuru vēlaties autorizēties (vai izmantojiet noklusēto) auths.edit=Labot autentifikācijas avotu diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 52ff71c90d..bdb6cd50d6 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -325,7 +325,6 @@ password_algorithm_helper = Stel het hashing-algoritme voor wachtwoorden in. De run_user_helper = De gebruikersnaam van het besturingssysteem waaronder Forgejo draait. Merk op dat deze gebruiker toegang moet hebben tot de hoofdmap van de repository. require_sign_in_view_popup = Beperk de toegang tot de pagina's tot ingelogde gebruikers. Bezoekers zien alleen de aanmeldings- en registratiepagina's. enable_update_checker_helper_forgejo = Het zal periodiek controleren op nieuwe Forgejo-versies door een TXT DNS-record op release.forgejo.org te controleren. -enable_update_checker_helper = Controleert periodiek op nieuwe versies door verbinding te maken met gitea.io. smtp_from_invalid = Het adres "E-mails versturen als" is ongeldig config_location_hint = Deze configuratieopties worden opgeslagen in: @@ -629,6 +628,7 @@ org_still_own_repo = Deze organisatie is eigenaar van één of meer repositories org_still_own_packages = Deze organisatie is eigenaar van één of meer pakketten, verwijder deze eerst. unset_password = De inloggebruiker heeft het wachtwoord niet ingesteld. unsupported_login_type = Het aanmeldtype wordt niet ondersteund om accounts te verwijderen. +required_prefix = De tekst moet beginnen met "%s" [user] @@ -662,6 +662,8 @@ settings = Gebruikersinstellingen form.name_reserved = De gebruikersnaam "%s" is gereserveerd. form.name_pattern_not_allowed = Het patroon "%s" is niet toegestaan in een gebruikersnaam. form.name_chars_not_allowed = Gebruikernaam "%s" bevat ongeldige karakters. +following_one = %d volgers +followers_one = %d volger [settings] @@ -986,6 +988,9 @@ update_hints = Tips bijwerken update_hints_success = Tips zijn bijgewerkt. hints = Tips additional_repo_units_hint_description = Toon een "Voeg meer eenheden toe..." knop voor repositories die niet alle beschikbare eenheden hebben ingeschakeld. +pronouns = Persoonlijke voornaamwoord +pronouns_custom = Aangepast +pronouns_unspecified = Ongedefinieerd [repo] owner=Eigenaar @@ -2704,6 +2709,11 @@ settings.enforce_on_admins = Handhaaf deze regel voor repository admins settings.event_pull_request_enforcement = Handhaving settings.enforce_on_admins_desc = Admins van deze repository kunnen deze regel niet omzeilen. issues.archived_label_description = (Gearchiveerd) %s +release.download_count_one = %s download +release.download_count_few = %s downloads +release.system_generated = Deze bijlage wordt automatisch gegenereerd. +settings.sourcehut_builds.secrets = Geheimen +settings.web_hook_name_sourcehut_builds = SourceHut Builds diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index e70fa9df57..5e8af1d750 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -19,8 +19,8 @@ language=Idioma notifications=Notificações active_stopwatch=Cronômetro ativo create_new=Criar… -user_profile_and_more=Perfil e Configurações... -signed_in_as=Acessado como +user_profile_and_more=Perfil e configurações… +signed_in_as=Sessão iniciada como enable_javascript=Este site requer JavaScript. toc=Índice licenses=Licenças @@ -37,18 +37,18 @@ twofa_scratch=Código de backup da autenticação de dois fatores passcode=Senha webauthn_insert_key=Insira sua chave de segurança -webauthn_sign_in=Pressione o botão na sua chave de segurança. Se a sua chave de segurança não tiver um botão, insira-a novamente. -webauthn_press_button=Por favor, pressione o botão na sua chave de segurança… +webauthn_sign_in=Pressione o botão na sua chave de segurança. Caso a sua chave de segurança não possuir um botão, insira-a novamente. +webauthn_press_button=Pressione o botão na sua chave de segurança… webauthn_use_twofa=Use um código de duas etapas do seu telefone webauthn_error=Não foi possível ler sua chave de segurança. webauthn_unsupported_browser=Seu navegador não oferece suporte ao WebAuthn. -webauthn_error_unknown=Ocorreu um erro desconhecido. Por favor, tente novamente. -webauthn_error_insecure=`WebAuthn suporta apenas conexões seguras. Para testar via HTTP, você pode usar a origem "localhost" ou "127.0.0.1"` +webauthn_error_unknown=Ocorreu um erro desconhecido. Tente novamente. +webauthn_error_insecure=WebAuthn suporta apenas conexões seguras. Para testar via HTTP, você pode usar a origem "localhost" ou "127.0.0.1" webauthn_error_unable_to_process=O servidor não pôde processar sua solicitação. webauthn_error_duplicated=A chave de segurança não é permitida para esta solicitação. Por favor, certifique-se que a chave já não está registrada. webauthn_error_empty=Você deve definir um nome para esta chave. -webauthn_error_timeout=Tempo limite atingido antes de sua chave poder ser lida. Por favor, recarregue esta página e tente novamente. -webauthn_reload=Recarregar +webauthn_error_timeout=Não foi possível ler a sua chave de segurança antes do tempo limite. Atualize a página e tente novamente. +webauthn_reload=Atualizar repository=Repositório organization=Organização @@ -79,7 +79,7 @@ pull_requests=Pull requests issues=Issues milestones=Marcos -ok=Ok +ok=OK cancel=Cancelar retry=Tentar novamente rerun=Reexecutar @@ -148,6 +148,8 @@ filter.is_archived = Arquivado filter.public = Público filter.is_template = Modelo filter.private = Privado +invalid_data = Dados inválidos: %v +more_items = Mais itens [aria] navbar=Barra de navegação @@ -160,6 +162,9 @@ number_of_contributions_in_the_last_12_months=%s contribuições nos últimos 12 contributions_zero=Sem contribuições less=Menos more=Mais +contributions_format = {contributions} em {day} de {month} de {year} +contributions_one = contribuição +contributions_few = contribuições [editor] buttons.heading.tooltip=Adicionar título @@ -309,7 +314,6 @@ password_algorithm=Algoritmo Hash de Senha invalid_password_algorithm=Algoritmo de hash de senha inválido password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O algoritmo argon2 é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas com menos recursos. enable_update_checker=Habilitar Verificador de Atualizações -enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io. env_config_keys=Configuração do ambiente env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu arquivo de configuração: allow_dots_in_usernames = Permitir pontos em nomes de usuário. Esta opção não afeta contas já existentes. @@ -363,6 +367,8 @@ code_search_results=`Resultados da pesquisa por: "%s"` code_last_indexed_at=Última indexação %s relevant_repositories_tooltip=Repositórios que são forks ou que não possuem tópico, nem ícone e nem descrição estão ocultos. relevant_repositories=Apenas repositórios relevantes estão sendo mostrados, mostrar resultados não filtrados. +stars_one = %d estrela +stars_few = %d estrelas [auth] create_new_account=Cadastrar conta @@ -438,6 +444,7 @@ change_unconfirmed_email_summary = Alterar o endereço de e-mail que o e-mail de last_admin = Não é possível remover o último administrador. Deve haver ao menos um usuário administrador. change_unconfirmed_email = Se você colocou o endereço de e-mail errado durante o cadastro, você pode alterá-lo abaixo, e uma confirmação será enviada para o novo endereço. remember_me.compromised = O token de login foi invalidado, o que pode indicar que a sua conta foi comprometida. Verifique se não há atividades suspeitas em sua conta. +tab_signin = Iniciar sessão [mail] view_it_on=Veja em %s @@ -607,6 +614,7 @@ org_still_own_packages=Esta organização ainda possui pacotes, exclua-os primei target_branch_not_exist=O branch de destino não existe. username_error_no_dots = ` pode conter apenas caracteres alfanuméricos ("0-9, "a-z", "A-Z"), hífens ("-") e traços inferiores ("_"). Não é permitido conter caracteres não alfanuméricos no início ou fim. Caracteres não alfanuméricos consecutivos também não são permitidos.` admin_cannot_delete_self = Você não pode excluir a si mesmo quando você é um administrador. Por favor, remova suas permissões de administrador primeiro. +AccessToken = Token de acesso [user] @@ -640,6 +648,7 @@ block_user.detail_2 = Este usuário não poderá interagir com seus repositório follow_blocked_user = Você não pode seguir este usuário, pois você o bloqueou ou foi bloqueado por ele. block_user.detail_3 = Este usuário não poderá adicionar-lhe como colaborador e você também não poderá adicioná-lo como colaborador. block_user.detail = Por favor, entenda que se você bloquear este usuário, outras ações serão tomadas. Tais como: +followers_one = %d seguidor [settings] profile=Perfil @@ -1049,9 +1058,9 @@ author_search_tooltip=Mostra um máximo de 30 usuários transfer.accept=Aceitar transferência -transfer.accept_desc=`Transferir para "%s"` +transfer.accept_desc=Transferir para "%s" transfer.reject=Rejeitar transferência -transfer.reject_desc=`Cancelar a transferência para "%s"` +transfer.reject_desc=Cancelar a transferência para "%s" transfer.no_permission_to_accept=Você não tem permissão para aceitar essa transferência. transfer.no_permission_to_reject=Você não tem permissão para rejeitar essa transferência. @@ -2862,31 +2871,31 @@ users.list_status_filter.not_restricted=Não restrito users.list_status_filter.is_prohibit_login=Proibir login users.list_status_filter.not_prohibit_login=Permitir login users.list_status_filter.is_2fa_enabled=2FA Ativado -users.list_status_filter.not_2fa_enabled=2FA Desativado +users.list_status_filter.not_2fa_enabled=Autenticação em duas etapas desativada users.details=Detalhes do usuário -emails.email_manage_panel=Gerenciamento de E-mail de Usuário +emails.email_manage_panel=Gerenciar e-mails de usuários emails.primary=Principal -emails.activated=Ativado +emails.activated=Em uso emails.filter_sort.email=E-mail -emails.filter_sort.email_reverse=E-mail (reverso) -emails.filter_sort.name=Nome de Usuário -emails.filter_sort.name_reverse=Nome de Usuário (reverso) -emails.updated=E-mail atualizado +emails.filter_sort.email_reverse=E-mail (decrescente) +emails.filter_sort.name=Usuário +emails.filter_sort.name_reverse=Usuário (decrescente) +emails.updated=Endereço de e-mail atualizado emails.not_updated=Falha ao atualizar o endereço de e-mail solicitado: %v -emails.duplicate_active=Este endereço de e-mail já está ativo para um usuário diferente. +emails.duplicate_active=Este endereço de e-mail já está em uso por outro usuário. emails.change_email_header=Atualizar Propriedades do E-mail -orgs.org_manage_panel=Gerenciamento da organização +orgs.org_manage_panel=Gerenciar organizações orgs.name=Nome orgs.teams=Equipes orgs.members=Membros orgs.new_orga=Nova organização -repos.repo_manage_panel=Gerenciamento do repositório +repos.repo_manage_panel=Gerenciar repositórios repos.unadopted=Repositórios Não Adotados repos.unadopted.no_more=Não foram encontrados mais repositórios não adotados -repos.owner=Proprietário +repos.owner=Proprietário(a) repos.name=Nome repos.private=Privado repos.watches=Observadores @@ -2896,9 +2905,9 @@ repos.issues=Issues repos.size=Tamanho repos.lfs_size=Tamanho do LFS -packages.package_manage_panel=Gerenciamento de Pacotes -packages.total_size=Tamanho Total: %s -packages.unreferenced_size=Tamanho Não Referenciado: %s +packages.package_manage_panel=Gerenciar pacotes +packages.total_size=Tamanho total: %s +packages.unreferenced_size=Tamanho não referenciado: %s packages.cleanup=Limpar dados expirados packages.owner=Proprietário packages.creator=Criador @@ -2917,13 +2926,13 @@ systemhooks=Webhooks do Sistema systemhooks.add_webhook=Adicionar Webhook do Sistema systemhooks.update_webhook=Atualizar Webhook do Sistema -auths.auth_manage_panel=Gerenciamento de fonte de autenticação +auths.auth_manage_panel=Gerenciar fontes de autenticação auths.new=Adicionar fonte de autenticação auths.name=Nome auths.type=Tipo -auths.enabled=Habilitado +auths.enabled=Habilitada auths.syncenabled=Habilitar sincronização de usuário -auths.updated=Atualizado +auths.updated=Atualizada auths.auth_type=Tipo de autenticação auths.auth_name=Nome da autenticação auths.security_protocol=Protocolo de segurança @@ -2933,7 +2942,7 @@ auths.port=Porta auths.bind_dn=Vincular DN auths.bind_password=Vincular senha auths.user_base=Base de pesquisa do usuário -auths.user_dn=Usuário do DN +auths.user_dn=DN do usuário auths.attribute_username=Atributo nome de usuário auths.attribute_username_placeholder=Deixe em branco para usar o nome de usuário inserido no Forgejo. auths.attribute_name=Atributo primeiro nome @@ -2943,11 +2952,11 @@ auths.attribute_ssh_public_key=Atributo de chave SSH pública auths.attribute_avatar=Atributo do avatar auths.attributes_in_bind=Buscar os atributos no contexto de Bind DN auths.allow_deactivate_all=Permitir que um resultado de pesquisa vazio para desativar todos os usuários -auths.use_paged_search=Use a pesquisa paginada +auths.use_paged_search=Usar pesquisa paginada auths.search_page_size=Tamanho da página auths.filter=Filtro de usuário auths.admin_filter=Filtro de administrador -auths.restricted_filter=Filtro de restrição +auths.restricted_filter=Filtro restrito auths.restricted_filter_helper=Deixe em branco para não definir nenhum usuário como restrito. Use um asterisco ('*') para definir todos os usuários que não correspondem ao Filtro de administrador como restritos. auths.verify_group_membership=Verificar associação ao grupo no LDAP (deixe o filtro vazio para ignorar) auths.group_search_base=Grupo de Pesquisa DN Base @@ -2955,34 +2964,34 @@ auths.group_attribute_list_users=Atributo do Grupo que Contém a Lista de Usuár auths.user_attribute_in_group=Atributo do Usuário Listado em Grupo auths.map_group_to_team=Mapear grupos LDAP para Organizações (deixe o campo vazio para pular) auths.map_group_to_team_removal=Remover usuários de equipes sincronizadas se o usuário não pertence ao grupo LDAP correspondente -auths.enable_ldap_groups=Habilitar grupos do LDAP +auths.enable_ldap_groups=Habilitar grupos LDAP auths.ms_ad_sa=Atributos de pesquisa do MS AD auths.smtp_auth=Tipo de autenticação SMTP -auths.smtphost=Host SMTP -auths.smtpport=Porta SMTP +auths.smtphost=Servidor +auths.smtpport=Porta auths.allowed_domains=Domínios permitidos -auths.allowed_domains_helper=Deixe em branco para permitir todos os domínios. Separe vários domínios com uma vírgula (','). -auths.skip_tls_verify=Pular verificação de TLS +auths.allowed_domains_helper=Deixe em branco para permitir todos os domínios. Separe múltiplos domínios com uma vírgula (","). +auths.skip_tls_verify=Ignorar validação TLS auths.force_smtps=Forçar SMTPS auths.force_smtps_helper=SMTPS é sempre usado no porto 465. Defina isso para forçar o SMTPS em outros portos. (Caso contrário STARTTLS será usado em outros portos se for suportado pelo host.) -auths.helo_hostname=HELO Hostname +auths.helo_hostname=Nome de servidor HELO auths.helo_hostname_helper=Hostname enviado com HELO. Deixe em branco para enviar o hostname atual. auths.disable_helo=Desativar HELO -auths.pam_service_name=Nome de Serviço PAM +auths.pam_service_name=Nome do serviço PAM auths.pam_email_domain=Domínio de e-mail do PAM (opcional) auths.oauth2_provider=Provedor OAuth2 -auths.oauth2_icon_url=URL do Ícone +auths.oauth2_icon_url=URL do ícone auths.oauth2_clientID=ID do cliente (chave) -auths.oauth2_clientSecret=Client Secret -auths.openIdConnectAutoDiscoveryURL=URL do OpenID Connect Auto Discovery -auths.oauth2_use_custom_url=Usar URLs personalizadas em vez de URLs padrão -auths.oauth2_tokenURL=URL do Token -auths.oauth2_authURL=URL de Authorização +auths.oauth2_clientSecret=Segredo do cliente +auths.openIdConnectAutoDiscoveryURL=URL de descoberta automática de conexão do OpenID +auths.oauth2_use_custom_url=Usar URLs personalizados em vez de URLs padrão +auths.oauth2_tokenURL=URL do código +auths.oauth2_authURL=URL da autorização auths.oauth2_profileURL=URL do perfil -auths.oauth2_emailURL=URL de e-mail -auths.skip_local_two_fa=Pular 2FA local +auths.oauth2_emailURL=URL do e-mail +auths.skip_local_two_fa=Ignorar autenticação em duas etapas local auths.skip_local_two_fa_helper=Deixar desligado significa que os usuários locais com 2FA ligada ainda terão que fazer login com 2FA -auths.oauth2_tenant=Tenant +auths.oauth2_tenant=Locatário auths.oauth2_scopes=Escopos Adicionais auths.oauth2_required_claim_name=Nome do Claim Obrigatorio auths.oauth2_required_claim_name_helper=Defina este nome para permitir o login desta fonte apenas para usuários que tenham um claim com este nome @@ -3476,7 +3485,7 @@ rpm.repository.multiple_groups = Este pacote está disponível em vários grupos secrets=Segredos description=Os segredos serão passados a certas ações e não poderão ser lidos de outra forma. none=Não há segredos ainda. -creation=Adicionar Segredo +creation=Adicionar segredo creation.name_placeholder=apenas caracteres alfanuméricos ou underline (_), não pode começar com GITEA_ ou GITHUB_ creation.value_placeholder=Insira qualquer conteúdo. Espaços em branco no início e no fim serão omitidos. creation.success=O segredo "%s" foi adicionado. @@ -3505,7 +3514,7 @@ runners=Runners runners.runner_manage_panel=Gerenciamento de Runners runners.new=Criar novo Runner runners.new_notice=Como iniciar um runner -runners.status=Status +runners.status=Estado runners.id=ID runners.name=Nome runners.owner_type=Tipo @@ -3516,10 +3525,10 @@ runners.runner_title=Runner runners.task_list=Tarefas recentes neste runner runners.task_list.no_tasks=Ainda não há nenhuma tarefa. runners.task_list.run=Executar -runners.task_list.status=Status +runners.task_list.status=Estado runners.task_list.repository=Repositório runners.task_list.commit=Commit -runners.task_list.done_at=Feito em +runners.task_list.done_at=Realizada em runners.edit_runner=Editar Runner runners.update_runner=Atualizar as Alterações runners.update_runner_success=Runner atualizado com sucesso @@ -3533,7 +3542,7 @@ runners.none=Nenhum runner disponível runners.status.unspecified=Desconhecido runners.status.idle=Inativo runners.status.active=Ativo -runners.status.offline=Offiline +runners.status.offline=Offline runners.version=Versão runners.reset_registration_token_success=Token de registro de runner redefinido com sucesso @@ -3573,7 +3582,7 @@ type-3.display_name=Projeto da organização [git.filemode] ; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", … -symbolic_link=Link simbólico +symbolic_link=Ligação simbólica changed_filemode = %[1]s → %[2]s directory = Diretório normal_file = Arquivo normal @@ -3587,3 +3596,21 @@ component_loading = Carregando %s... component_loading_failed = Não foi possível carregar o(a) %s component_loading_info = Pode demorar um pouco… contributors.what = contribuições + + +[search] +org_kind = Buscar organizações... +team_kind = Buscar equipes... +code_kind = Buscar código... +user_kind = Buscar usuários... +no_results = Nenhum resultado encontrado. +keyword_search_unavailable = A busca por palavras-chave não está disponível. Entre em contato com o administrador. +package_kind = Buscar pacotes... +project_kind = Buscar projetos... +search = Buscar... +fuzzy = Aproximada +fuzzy_tooltip = Inclui resultados que se aproximam dos termos de busca +match = Correspondente +match_tooltip = Inclui apenas os resultados que correspondem exatamente aos termos de busca +repo_kind = Buscar repositórios... +type_tooltip = Tipo de busca \ No newline at end of file diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 1f05e3e50a..d0c5320cdc 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -142,6 +142,19 @@ confirm_delete_selected=Confirma a exclusão de todos os itens marcados? name=Nome value=Valor +filter.is_fork = Derivado +filter.is_mirror = Replicado +filter.is_template = Modelo +filter.public = Público +filter.not_archived = Não arquivado +filter.private = Privado +filter.not_fork = Não derivado +filter.not_mirror = Não replicado +more_items = Mais itens +invalid_data = Dados inválidos: %v +filter.clear = Retirar filtros +filter.is_archived = Arquivado +filter.not_template = Não é modelo [aria] navbar=Barra de navegação @@ -154,6 +167,9 @@ number_of_contributions_in_the_last_12_months=%s contribuições nos últimos 12 contributions_zero=Nenhuma contribuição less=Menos more=Mais +contributions_format = {contributions} em {day} de {month} de {year} +contributions_one = contribuição +contributions_few = contribuições [editor] buttons.heading.tooltip=Adicionar cabeçalho @@ -182,6 +198,7 @@ missing_csrf=Pedido inválido: não há código CSRF invalid_csrf=Pedido inválido: código CSRF inválido not_found=Não foi possível encontrar o destino. network_error=Erro de rede +server_internal = Erro interno do servidor [startpage] app_desc=Um serviço Git auto-hospedado e fácil de usar @@ -302,9 +319,12 @@ password_algorithm=Algoritmo de Hash da Senha invalid_password_algorithm=Algoritmo de hash da senha inválido password_algorithm_helper=Definir o algoritmo de hash da senha. Os algoritmos têm requisitos e resistência distintos. `argon2` é bastante seguro, mas usa muita memória e pode ser inapropriado para sistemas pequenos. enable_update_checker=Habilitar verificador de novidades -enable_update_checker_helper=Verifica, periodicamente, se foi lançada alguma versão nova, fazendo uma ligação ao gitea.io. env_config_keys=Configuração do ambiente env_config_keys_prompt=As seguintes variáveis de ambiente também serão aplicadas ao seu ficheiro de configuração: +config_location_hint = Estas opções de configuração serão gravadas em: +enable_update_checker_helper_forgejo = Irá verificar periodicamente a existência de novas versões do Forgejo analisando um registo TXT DNS em release.forgejo.org. +smtp_from_invalid = O endereço para "Enviar email como" é inválido +allow_dots_in_usernames = Permitir que os utilizadores usem pontos no nome de utilizador. Não tem efeito sobre as contas existentes. [home] uname_holder=Nome de utilizador ou endereço de email @@ -354,6 +374,10 @@ code_search_results=`Resultados da pesquisa para "%s"` code_last_indexed_at=Última indexação %s relevant_repositories_tooltip=Repositórios que são derivações ou que não têm tópico, nem ícone, nem descrição, estão escondidos. relevant_repositories=Apenas estão a ser mostrados os repositórios relevantes. Mostrar resultados não filtrados. +stars_one = %d estrela +stars_few = %d estrelas +forks_one = %d derivação +forks_few = %d derivações [auth] create_new_account=Fazer inscrição @@ -426,6 +450,11 @@ sspi_auth_failed=Falhou a autenticação SSPI password_pwned=A senha utilizada está numa lista de senhas roubadas anteriormente expostas em fugas de dados públicas. Tente novamente com uma senha diferente e considere também mudar esta senha nos outros sítios. password_pwned_err=Não foi possível completar o pedido ao HaveIBeenPwned last_admin=Não pode remover o último administrador. Tem que existir pelo menos um administrador. +change_unconfirmed_email = Se forneceu um endereço de email errado durante o registo, pode mudá-lo abaixo e ser-lhe-á enviada uma confirmação para o novo endereço. +change_unconfirmed_email_summary = Mudar o endereço de email para onde a mensagem de habilitação é enviada. +tab_signin = Iniciar sessão +tab_signup = Criar conta +change_unconfirmed_email_error = Não foi possível mudar o endereço de email: %v [mail] view_it_on=Ver em %s @@ -492,6 +521,9 @@ team_invite.subject=%[1]s fez-lhe um convite para se juntar à organização %[2 team_invite.text_1=%[1]s fez-lhe um convite para se juntar à equipa %[2]s na organização %[3]s. team_invite.text_2=Clique na ligação seguinte para se juntar à equipa: team_invite.text_3=Nota: Este convite é dirigido a %[1]s. Se não estava à espera deste convite, pode ignorar este email. +admin.new_user.subject = O novo utilizador %s acabou de criar uma conta +admin.new_user.user_info = Informação do utilizador +admin.new_user.text = Clique aqui para gerir este utilizador a partir do painel de administração. [modal] yes=Sim @@ -592,6 +624,15 @@ org_still_own_packages=Esta organização ainda possui um ou mais pacotes, elimi target_branch_not_exist=O ramo de destino não existe. admin_cannot_delete_self=Não se pode auto-remover quando tem privilégios de administração. Remova esses privilégios primeiro. +username_error_no_dots = ` só pode conter caracteres alfanuméricos ("0-9","a-z","A-Z"), hífen ('-'), sublinhado ('_') e ponto ('.') Não pode começar nem terminar com caracteres não alfanuméricos, e caracteres não alfanuméricos consecutivos também são proibidos.` +unset_password = O utilizador não definiu a senha. +unsupported_login_type = O tipo de início de sessão não é suportado para eliminar a conta. +Biography = Biografia +Website = Sítio web +Location = Localização +To = Nome do ramo +required_prefix = A entrada tem de começar com "%s" +AccessToken = Código de acesso [user] change_avatar=Mude o seu avatar… @@ -617,6 +658,16 @@ settings=Configurações do utilizador form.name_reserved=O nome de utilizador "%s" está reservado. form.name_pattern_not_allowed=O padrão "%s" não é permitido no nome de utilizador. form.name_chars_not_allowed=O nome de utilizador "%s" contém caracteres inválidos. +block = Bloquear +unblock = Desbloquear +followers_one = %d seguidor +following_one = %d seguindo +block_user.detail = Note que se bloquear este utilizador, serão executadas outras operações, tais como: +block_user.detail_1 = Está a deixar de ser seguido por este utilizador. +block_user.detail_2 = Este utilizador não pode interagir com os seus repositórios, questões criadas e comentários. +block_user.detail_3 = Este/a utilizador/a não o/a pode adicionar como colaborador/a nem você pode o/a adicionar como colaborador/a. +follow_blocked_user = Não pode seguir este/a utilizador/a porque você o/a bloqueou ou este/a utilizador/a bloqueou-o/a a si. +block_user = Bloquear utilizador [settings] profile=Perfil @@ -929,6 +980,20 @@ visibility.limited=Limitada visibility.limited_tooltip=Visível apenas para utilizadores autenticados visibility.private=Privada visibility.private_tooltip=Visível apenas para membros das organizações a que se associou +additional_repo_units_hint = Encorajar a habilitação de unidades do repositório adicionais +update_hints = Modificar sugestões +change_password = Modificar a senha +pronouns = Pronomes +pronouns_custom = Personalizado +pronouns_unspecified = Não especificado +hints = Sugestões +blocked_users = Utilizadores bloqueados +blocked_since = Bloqueado desde %s +user_block_success = O utilizador foi bloqueado com sucesso. +additional_repo_units_hint_description = Mostrar um botão "Adicionar mais unidades..." para repositórios que não têm todas as unidades disponíveis habilitadas. +update_hints_success = As sugestões foram modificadas. +blocked_users_none = Não há utilizadores bloqueados. +user_unblock_success = O utilizador foi desbloqueado com sucesso. [repo] new_repo_helper=Um repositório contém todos os ficheiros do trabalho, incluindo o histórico das revisões. Já tem um hospedado noutro sítio? Migre o repositório. @@ -2589,6 +2654,32 @@ find_file.no_matching=Não foi encontrado qualquer ficheiro correspondente error.csv.too_large=Não é possível apresentar este ficheiro por ser demasiado grande. error.csv.unexpected=Não é possível apresentar este ficheiro porque contém um caractere inesperado na linha %d e coluna %d. error.csv.invalid_field_count=Não é possível apresentar este ficheiro porque tem um número errado de campos na linha %d. +issues.blocked_by_user = Não pode criar uma questão neste repositório porque foi bloqueado/a pelo/a proprietário/a do repositório. +issues.num_participants_one = %d participante +stars = Favoritos +editor.invalid_commit_mail = Email inválido para criar um cometimento. +editor.push_out_of_date = O envio parece estar fora de prazo. +admin.enabled_flags = Marcadores habilitados no repositório: +admin.update_flags = Modificar marcadores +admin.flags_replaced = Os marcadores do repositório foram substituídos +commits.browse_further = Explorar mais um pouco +commits.renamed_from = Renomeado de %s +size_format = %[1]s: %[2]s, %[3]s: %[4]s +issues.archived_label_description = (arquivado) %s +admin.failed_to_replace_flags = Falhou a reposição dos marcadores do repositório +open_with_editor = Abrir com %s +admin.manage_flags = Gerir marcadores +file_follow = Seguir ligação simbólica +rss.must_be_on_branch = Tem que estar num ramo que tenha uma fonte RSS. +n_commit_few = %s cometimentos +n_branch_one = %s ramo +n_branch_few = %s ramos +n_tag_one = %s etiqueta +n_tag_few = %s etiquetas +migrate.forgejo.description = Migrar dados de codeberg.org ou de outras instâncias Forgejo. +n_commit_one = %s cometimento +editor.commit_id_not_matching = O ID de cometimento não corresponde ao que estava a editar. Cometa para um ramo novo e depois integre. +commits.search_branch = Este ramo [graphs] component_loading=A carregar %s... @@ -3042,7 +3133,7 @@ auths.tip.google_plus=Obtenha credenciais de cliente OAuth2 a partir da consola auths.tip.openid_connect=Use o URL da descoberta de conexão OpenID (/.well-known/openid-configuration) para especificar os extremos auths.tip.twitter=`Vá a https://dev.twitter.com/apps, crie uma aplicação e certifique-se de que está habilitada a opção "Allow this application to be used to Sign in with Twitter"` auths.tip.discord=Registe uma nova aplicação em https://discordapp.com/developers/applications/me -auths.tip.gitea=Registe uma nova aplicação OAuth2. O guia pode ser encontrado em https://docs.gitea.com/development/oauth2-provider +auths.tip.gitea=Registe uma nova aplicação OAuth2. O guia pode ser encontrado em https://forgejo.org/docs/latest/user/oauth2-provider auths.tip.yandex=`Crie uma nova aplicação em https://oauth.yandex.com/client/new. Escolha as seguintes permissões da secção "Yandex.Passport API": "Acesso ao endereço de email", "Acesso ao avatar do utilizador" e "Acesso ao nome de utilizador, nome e sobrenome, género"` auths.tip.mastodon=Insira o URL de uma instância personalizada para a instância do mastodon com que se pretende autenticar (ou então use a predefinida) auths.edit=Editar fonte de autenticação @@ -3619,3 +3710,26 @@ executable_file=Ficheiro executável symbolic_link=Ligação simbólica submodule=Submódulo + + +[search] +org_kind = Pesquisar organizações... +keyword_search_unavailable = Pesquisar por palavra-chave não está disponível, neste momento. Entre em contacto com o administrador. +code_search_by_git_grep = Os resultados da pesquisa no código-fonte neste momento são fornecidos pelo "git grep". Esses resultados podem ser melhores se o administrador habilitar o indexador do repositório. +no_results = Não foram encontrados resultados correspondentes. +package_kind = Pesquisar pacotes... +runner_kind = Pesquisar executores... +project_kind = Pesquisar planeamentos... +branch_kind = Pesquisar ramos... +commit_kind = Pesquisar cometimentos... +search = Procurar... +type_tooltip = Tipo de pesquisa +fuzzy = Aproximada +fuzzy_tooltip = Incluir também os resultados que estejam próximos do termo de pesquisa +match = Fiel +match_tooltip = Incluir somente os resultados que correspondam rigorosamente ao termo de pesquisa +repo_kind = Pesquisar repositórios... +user_kind = Pesquisar utilizadores... +team_kind = Pesquisar equipas... +code_kind = Pesquisar código... +code_search_unavailable = A pesquisa de código não está disponível, neste momento. Entre em contacto com o administrador. \ No newline at end of file diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 102eea9d13..6017980183 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -33,7 +33,7 @@ access_token=Токен доступа re_type=Подтверждение пароля captcha=CAPTCHA twofa=Двухфакторная аутентификация -twofa_scratch=Двухфакторный scratch-код +twofa_scratch=Scratch-код 2ФА passcode=Код webauthn_insert_key=Вставьте ваш ключ безопасности @@ -217,7 +217,7 @@ license_desc=Всё это на документацией, прежде чем изменять любые настройки. -require_db_desc=Forgejo требует MySQL, PostgreSQL, SQLite3 или TiDB (по протоколу MySQL). +require_db_desc=Forgejo требуется MySQL, PostgreSQL, SQLite3 или TiDB (по протоколу MySQL). db_title=Настройки базы данных db_type=Тип базы данных host=Хост @@ -288,10 +288,10 @@ openid_signup=Включить саморегистрацию через OpenID openid_signup_popup=Включить регистрацию пользователей через OpenID. enable_captcha=Включить CAPTCHA при регистрации enable_captcha_popup=Запрашивать капчу при регистрации пользователя. -require_sign_in_view=Требовать авторизации для просмотра страниц +require_sign_in_view=Требовать авторизацию для просмотра содержимого require_sign_in_view_popup=Ограничить доступ к странице только вошедшими пользователями. Посетители увидят лишь страницы входа и регистрации. admin_setting_desc=Создание учётной записи администратора необязательно. Первый зарегистрированный пользователь автоматически становится администратором. -admin_title=Настройки учётной записи администратора +admin_title=Учётная запись администратора admin_name=Логин администратора admin_password=Пароль confirm_password=Подтверждение пароля @@ -321,7 +321,6 @@ password_algorithm=Алгоритм хеширования паролей invalid_password_algorithm=Некорректный алгоритм хеширования пароля password_algorithm_helper=Задайте алгоритм хеширования паролей. Алгоритмы имеют различные требования и стойкость. Алгоритм argon2 довольно безопасен, но он использует много памяти и может не подходить для слабых систем. enable_update_checker=Проверка обновлений -enable_update_checker_helper=Периодически проверяет наличие новых версий, подключаясь к gitea.io. env_config_keys=Настройка окружения env_config_keys_prompt=Следующие переменные окружения также будут применены к вашему конфигурационному файлу: enable_update_checker_helper_forgejo = Периодически проверять наличие новых версий Forgejo через DNS-запись TXT на release.forgejo.org. @@ -336,7 +335,7 @@ switch_dashboard_context=Переключить контекст панели у my_repos=Репозитории show_more_repos=Показать больше репозиториев… collaborative_repos=Совместные репозитории -my_orgs=Мои организации +my_orgs=Организации my_mirrors=Мои зеркала view_home=Показать %s search_repos=Поиск репозитория… @@ -559,8 +558,8 @@ SSPISeparatorReplacement=Разделитель SSPIDefaultLanguage=Язык по умолчанию require_error=` не может быть пустым.` -alpha_dash_error=` должен содержать только буквенно-цифровые символы, тире («-») и подчеркивания («_»).` -alpha_dash_dot_error=` должен содержать только буквенно-цифровые символы, тире ('-'), подчеркивания ('_') и точки ('.').` +alpha_dash_error=` может содержать только буквенно-цифровые символы, тире («-») и подчеркивания («_»).` +alpha_dash_dot_error=` может содержать только буквенно-цифровые символы, тире («-»), подчеркивания («_») и точки («.»).` git_ref_name_error=` должно быть правильным ссылочным именем Git.` size_error=` должен быть размер %s.` min_size_error=` должен содержать по крайней мере %s символов.` @@ -570,7 +569,7 @@ url_error=`«%s» не является валидным URL.` include_error=` должно содержать подстроку «%s».` glob_pattern_error=` неверный glob шаблон: %s.` regex_pattern_error=` Неверный шаблон регулярного выражения: %s.` -username_error=` может содержать только алфавитно-цифровые символы ('0-9','a-z','A-Z'), тире ('-'), подчеркивания ('_') и точку ('.'). Первый символ должен быть алфавитно-цифровым, последовательности из нескольких не алфавитно-цифровых символов запрещены.` +username_error=` может содержать только алфавитно-цифровые символы («0-9»,«a-z»,«A-Z»), тире («-»), подчеркивания («_») и точку («.»). Первый символ должен быть алфавитно-цифровым, последовательности из нескольких не алфавитно-цифровых символов запрещены.` invalid_group_team_map_error=` сопоставление недопустимо: %s` unknown_error=Неизвестная ошибка: captcha_incorrect=Капча не пройдена. @@ -615,7 +614,7 @@ invalid_ssh_key=Не удается проверить ключ SSH: %s invalid_gpg_key=Не удается проверить ключ GPG: %s invalid_ssh_principal=Неверный принципал: %s must_use_public_key=Ключ, который вы предоставили, является закрытым. Пожалуйста, не отправляйте свой закрытый ключ куда бы то ни было. Используйте для этих целей открытый ключ. -unable_verify_ssh_key=Не удаётся верифицировать ключ SSH, проверьте его на наличие ошибок. +unable_verify_ssh_key=Не удалось проверить ключ SSH. Проверьте его на наличие ошибок. auth_failed=Ошибка аутентификации: %v still_own_repo=Ваша учётная запись владеет одним или несколькими репозиториями, сначала удалите или передайте их. @@ -629,6 +628,15 @@ admin_cannot_delete_self = Вы не можете удалить свою учё username_error_no_dots = ` может состоять только из латинских букв («a-z», «A-Z»), цифр («0-9»), знаков минуса («-») и нижнего подчёркивания («_»). Знаки не могут стоять в начале или в конце, а также идти подряд.` unsupported_login_type = Удаление аккаунта невозможно с этим типом авторизации. unset_password = У пользователя не задан пароль. +required_prefix = Должно начинаться с «%s» +AccessToken = Токен доступа +FullName = Полное имя +Description = Описание +Pronouns = Местоимения +Biography = О себе +Website = Веб-сайт +Location = Местоположение +To = Название ветки [user] @@ -663,6 +671,8 @@ follow_blocked_user = Вы не можете подписаться на это block_user = Заблокировать пользователя block_user.detail_1 = Вы будете отписаны от этого пользователя. block_user.detail_3 = Вы не сможете добавлять друг друга в качестве соучастников репозиториев. +followers_one = %d подписчик +following_one = %d подписка [settings] profile=Профиль @@ -912,7 +922,7 @@ revoke_oauth2_grant=Отозвать доступ revoke_oauth2_grant_description=Отзыв доступа у этого стороннего приложения не позволит ему получать доступ к вашим данным. Вы уверены? revoke_oauth2_grant_success=Доступ был успешно отозван. -twofa_desc=Двухфакторная аутентификация повышает уровень безопасности вашей учётной записи. +twofa_desc=Для дополнительной защиты учётной записи вы можете настроить аутентификацию по одноразовым «TOTP» кодам, генерируемым на смартфоне или другом устройстве. twofa_recovery_tip=При утере устройства вы сможете восстановить доступ к учётной записи, использовав одноразовый ключ восстановления. twofa_is_enrolled=Ваша учётная запись в настоящее время использует двухфакторную аутентификацию. twofa_not_enrolled=Ваша учётная запись в настоящее время не использует двухфакторную аутентификацию. @@ -956,10 +966,10 @@ confirm_delete_account=Подтвердить удаление delete_account_title=Удалить эту учётную запись delete_account_desc=Вы уверены, что хотите навсегда удалить эту учётную запись? -email_notifications.enable=Разрешить уведомления по почте +email_notifications.enable=Включить уведомления по эл. почте email_notifications.onmention=Посылать письмо на эл. почту только при упоминании email_notifications.disable=Отключить уведомления по почте -email_notifications.submit=Установить настройки эл. почты +email_notifications.submit=Задать настройку уведомлений email_notifications.andyourown=И ваши собственные уведомления visibility=Видимость пользователя @@ -986,6 +996,9 @@ additional_repo_units_hint = Предлагать включить больше update_hints = Обновить подсказки update_hints_success = Подсказки обновлены. additional_repo_units_hint_description = Показывать кнопку "Добавить больше разделов" в репозиториях, в которых включены не все разделы. +pronouns_custom = Другие +pronouns = Местоимения +pronouns_unspecified = Не указаны [repo] owner=Владелец @@ -1211,11 +1224,11 @@ tag=тег released_this=выпустил(-а) это tagged_this=добавил(а) тег file.title=%s в %s -file_raw=Исходник +file_raw=Исходный file_history=История -file_view_source=Просмотреть исходный код -file_view_rendered=Просмотр отрендеренного -file_view_raw=Посмотреть исходник +file_view_source=Просмотр исходного текста +file_view_rendered=Просмотр рендера +file_view_raw=Открыть исходный file_permalink=Постоянная ссылка file_too_large=Этот файл слишком большой, поэтому он не может быть отображён. invisible_runes_header=`Этот файл содержит невидимые символы Юникода` @@ -1301,8 +1314,8 @@ editor.commit_empty_file_text=Файл, который вы собираетес editor.no_changes_to_show=Нет изменений. editor.fail_to_update_file=Не удалось обновить/создать файл «%s». editor.fail_to_update_file_summary=Ошибка: -editor.push_rejected_no_message=Изменение отклонено сервером без сообщения. Пожалуйста, проверьте хуки Git. -editor.push_rejected=Изменение отклонено сервером. Пожалуйста, проверьте хуки Git. +editor.push_rejected_no_message=Изменение отклонено сервером без сообщения. Пожалуйста, проверьте Git-хуки. +editor.push_rejected=Изменение отклонено сервером. Пожалуйста, проверьте Git-хуки. editor.push_rejected_summary=Полное сообщение об отклонении: editor.add_subdir=Добавить каталог… editor.unable_to_upload_files=Не удалось загрузить файлы в «%s» из-за ошибки: %v @@ -1397,7 +1410,7 @@ issues.filter_milestones=Фильтр этапов issues.filter_projects=Фильтровать проекты issues.filter_labels=Фильтр меток issues.filter_reviewers=Фильтр рецензентов -issues.new=Создать задачу +issues.new=Добавить задачу issues.new.title_empty=Заголовок не может быть пустым issues.new.labels=Метки issues.new.no_label=Нет меток @@ -1425,14 +1438,14 @@ issues.choose.ignore_invalid_templates=Некорректные шаблоны issues.choose.invalid_templates=Найден(ы) %v неверный(х) шаблон(ов) issues.choose.invalid_config=Конфигурация задачи содержит ошибки: issues.no_ref=Нет связанной ветки или тега -issues.create=Добавить задачу +issues.create=Создать задачу issues.new_label=Новая метка issues.new_label_placeholder=Имя метки issues.new_label_desc_placeholder=Описание issues.create_label=Добавить метку -issues.label_templates.title=Загрузить набор предопределённых меток +issues.label_templates.title=Загрузить набор меток issues.label_templates.info=Меток пока нет. Создайте новую метку или используйте этот набор меток: -issues.label_templates.helper=Выберите метку +issues.label_templates.helper=Выберите набор меток issues.label_templates.use=Использовать набор меток issues.label_templates.fail_to_load_file=Не удалось загрузить файл шаблона меток «%s»: %v issues.add_label=добавлена метка %s %s @@ -1521,7 +1534,7 @@ issues.commented_at=`оставлен комментарий в % issues.delete_comment_confirm=Вы уверены, что хотите удалить этот комментарий? issues.context.copy_link=Копировать ссылку issues.context.quote_reply=Цитировать ответ -issues.context.reference_issue=Ссылка в новой задаче +issues.context.reference_issue=Сослаться в новой задаче issues.context.edit=Редактировать issues.context.delete=Удалить issues.no_content=Описание отсутствует. @@ -1727,7 +1740,7 @@ compare.compare_base=Основа compare.compare_head=сравнить pulls.desc=Включить запросы на слияние и проверки кода. -pulls.new=Новый запрос на слияние +pulls.new=Новый запрос pulls.view=Просмотр запроса на слияние pulls.compare_changes=Новый запрос на слияние pulls.allow_edits_from_maintainers=Разрешить редактирование сопровождающими @@ -1840,9 +1853,9 @@ pulls.outdated_with_base_branch=Эта ветка отстает от базов pulls.close=Закрыть запрос на слияние pulls.closed_at=`закрыл этот запрос на слияние %[2]s` pulls.reopened_at=`переоткрыл этот запрос на слияние %[2]s` -pulls.cmd_instruction_hint=`Просмотреть инструкции для командной строки.` -pulls.cmd_instruction_merge_title=Слить -pulls.cmd_instruction_merge_desc=Слить изменения и обновить в Forgejo. +pulls.cmd_instruction_hint=`Показать инструкции для командной строки.` +pulls.cmd_instruction_merge_title=Слейте изменения +pulls.cmd_instruction_merge_desc=Слейте изменения и отправьте их обратно. pulls.clear_merge_message=Очистить сообщение о слиянии pulls.clear_merge_message_hint=Очистка сообщения о слиянии удалит только содержимое сообщения коммита, но сохранит сгенерированные git добавки, такие как "Co-Authored-By …". @@ -2059,18 +2072,18 @@ settings.advanced_settings=Расширенные настройки settings.wiki_desc=Включить вики репозитория settings.use_internal_wiki=Использовать встроенную вики settings.use_external_wiki=Использовать внешнюю вики -settings.external_wiki_url=URL внешней вики +settings.external_wiki_url=Ссылка на внешнюю вики settings.external_wiki_url_error=URL внешней вики не является корректным URL. settings.external_wiki_url_desc=Посетители будут перенаправлены на URL, когда они кликнут по вкладке. -settings.issues_desc=Включить систему учёта задач репозитория -settings.use_internal_issue_tracker=Использовать встроенную систему учета задач -settings.use_external_issue_tracker=Использовать внешнюю систему учета задач +settings.issues_desc=Включить систему задач +settings.use_internal_issue_tracker=Использовать встроенную систему задач +settings.use_external_issue_tracker=Использовать внешнюю систему задач settings.external_tracker_url=Ссылка на внешнюю систему отслеживания задач settings.external_tracker_url_error=URL внешнего баг-трекера не является корректным URL. settings.external_tracker_url_desc=Посетители будут перенаправлены на URL, когда они кликнут по вкладке. settings.tracker_url_format=Формат ссылки внешней системы отслеживания задач settings.tracker_url_format_error=Формат URL внешнего баг-трекера некорректен. -settings.tracker_issue_style=Формат нумерации для внешней системы учета задач +settings.tracker_issue_style=Формат нумерации во внешней системе задач settings.tracker_issue_style.numeric=Цифровой settings.tracker_issue_style.alphanumeric=Буквенноцифровой settings.tracker_issue_style.regexp=Регулярное выражение @@ -2079,8 +2092,8 @@ settings.tracker_issue_style.regexp_pattern_desc=Вместо {index}{user}, {repo} и {index} для имени пользователя, репозитория и номера задачи. settings.enable_timetracker=Включить отслеживание времени settings.allow_only_contributors_to_track_time=Подсчитывать время могут только соавторы -settings.pulls_desc=Включить запросы на слияние -settings.pulls.ignore_whitespace=Игнорировать незначащие изменения (пробелы, табуляция) при проверке на конфликты слияния +settings.pulls_desc=Включить запросы слияний +settings.pulls.ignore_whitespace=Игнорировать незначащие различия (пробелы, табуляцию) при проверке слияний на конфликты settings.pulls.enable_autodetect_manual_merge=Включить автоопределение ручного слияния (Примечание: в некоторых особых случаях могут возникнуть ошибки) settings.pulls.allow_rebase_update=Включить обновление ветки из запроса на слияние путём rebase settings.pulls.default_delete_branch_after_merge=Удалить ветку запроса после его слияния по умолчанию @@ -2088,12 +2101,12 @@ settings.pulls.default_allow_edits_from_maintainers=По умолчанию ра settings.releases_desc=Включить выпуски settings.packages_desc=Включить реестр пакетов settings.projects_desc=Включить проекты репозитория -settings.actions_desc=Включить действия репозитория +settings.actions_desc=Включить действия settings.admin_settings=Настройки администратора -settings.admin_enable_health_check=Выполнять проверки целостности этого репозитория (git fsck) +settings.admin_enable_health_check=Проверять целостность этого репозитория (git fsck) settings.admin_code_indexer=Индексатор кода settings.admin_stats_indexer=Индексатор статистики кода -settings.admin_indexer_commit_sha=Последний индексированный SHA +settings.admin_indexer_commit_sha=Последний индексированный коммит settings.admin_indexer_unindexed=Не индексировано settings.reindex_button=Добавить в очередь переиндексации settings.reindex_requested=Переиндексация запрошена @@ -2653,7 +2666,7 @@ editor.invalid_commit_mail = Неправильная почта для созд pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветки или повторное слияние невозможно. settings.enter_repo_name = Введите имя владельца и название репозитория как указано: signing.wont_sign.error = Не удалось проверить возможность подписать коммит. -signing.wont_sign.nokey = Нет ключей для подписи этого коммита. +signing.wont_sign.nokey = Сервер не предоставляет ключ для подписи коммита. settings.wiki_globally_editable = Разрешить редактирование вики всем пользователям settings.webhook.test_delivery_desc_disabled = Активируйте этот веб-хук для проверки тестовым событием. commits.browse_further = Смотреть далее @@ -2703,6 +2716,25 @@ settings.enforce_on_admins = Обязательно для администра settings.enforce_on_admins_desc = Администраторы репозитория не смогут обойти это ограничение. settings.rename_branch_failed_protected = Невозможно переименовать защищённую ветку «%s». issues.archived_label_description = (Архивная) %s +settings.sourcehut_builds.graphql_url = Ссылка на GraphQL (напр. https://builds.sr.ht/query) +settings.sourcehut_builds.secrets_helper = Дать задачам доступ к секретам сборки (требуется разрешение SECRETS:RO) +settings.add_webhook.invalid_path = Путь не может включать части «.», «..» или «//». Он не может начинаться или заканчиваться на слеш. +settings.web_hook_name_sourcehut_builds = SourceHut Builds +settings.sourcehut_builds.manifest_path = Путь манифеста сборки +settings.sourcehut_builds.visibility = Видимость задач +settings.sourcehut_builds.secrets = Секреты +release.download_count_one = %s скачивание +release.download_count_few = %s скачиваний +release.system_generated = Это вложение сгенерировано автоматически. +settings.event_pull_request_enforcement = Форсирование +pulls.cmd_instruction_checkout_desc = В репозитории вашего проекта перейдите на эта ветку и протестируйте изменения. +error.broken_git_hook = Гит-хуки этого репозитория сломаны. Ознакомьтесь с документацией и почините их, затем отправьте какие-нибудь коммиты для обновления статуса. +pulls.cmd_instruction_checkout_title = Перейдите на ветку +settings.graphql_url = Ссылка GraphQL +settings.sourcehut_builds.access_token_helper = Токен builds.sr.ht с разрешением JOBS:RW. Создайте обычный токен или токен с доступом к секретам на meta.sr.ht. +settings.matrix.room_id_helper = ID комнаты можно получить в веб-клиенте Element: Настройки комнаты > Подробности > Внутренний ID комнаты. Пример: %s. +settings.matrix.access_token_helper = Рекомендуется создать отдельный аккаунт. Токен доступа можно получить в веб-клиенте Element (в приватной вкладке или режиме инкогнито): Пользовательское меню (сверху слева) > Все настройки > Помощь и о программе > Токен доступа (под ссылкой Homeserver). Закройте вкладку/окно, не выходя из Element. Выход аннулирует токен. +settings.mirror_settings.pushed_repository = Удалённый репозиторий [graphs] @@ -2842,7 +2874,7 @@ integrations=Интеграции authentication=Аутентификация emails=Адреса эл. почты пользователей config=Конфигурация -notices=Системные уведомления +notices=Системные оповещения monitor=Мониторинг first_page=Первая last_page=Последняя @@ -3332,7 +3364,7 @@ monitor.queue.settings.changed=Настройки обновлены monitor.queue.settings.remove_all_items=Удалить все monitor.queue.settings.remove_all_items_done=Все элементы в очереди были удалены. -notices.system_notice_list=Уведомления системы +notices.system_notice_list=Системные оповещения notices.view_detail_header=Подробности уведомления notices.operations=Операции notices.select_all=Выбрать всё @@ -3370,6 +3402,7 @@ config.open_with_editor_app_help = Приложения для "Открыть config_settings = Настройки auths.tips.gmail_settings = Настройки Gmail: auths.tip.gitlab_new = Создайте новое приложение в https://gitlab.com/-/profile/applications +monitor.queue.review_add = Подробности / добавить рабочих [action] @@ -3730,6 +3763,9 @@ variables.id_not_exist = Переменная с идентификатором runs.no_workflows.quick_start = Не знаете, как начать использовать Действия Forgejo? Читайте руководство по быстрому старту. runs.no_workflows.documentation = Чтобы узнать больше о Действиях Forgejo, читайте документацию. runs.workflow = Рабочий поток +runs.status_no_select = Любой статус +runs.no_matching_online_runner_helper = Нет работающего раннера с меткой: %s +runs.no_job_without_needs = Рабочий процесс должен содержать хотя бы одну задачу без зависимостей. [projects] type-1.display_name=Индивидуальный проект @@ -3778,3 +3814,9 @@ match_tooltip = Включать только результаты, точно code_search_unavailable = Поиск по коду сейчас недоступен. Уточните подробности у администратора. runner_kind = Поиск раннеров... code_search_by_git_grep = Эти результаты получены через «git grep». Результатов может быть больше, если администратор сервера включит индексатор кода. + + +[markup] +filepreview.line = Строка %[1]d в %[2]s +filepreview.lines = Строки с %[1]d по %[2]d в %[3]s +filepreview.truncated = Предпросмотр был обрезан \ No newline at end of file diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 28c3c3cc21..2167d116d0 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -184,7 +184,7 @@ network_error=Chyba siete [startpage] app_desc=Jednoducho prístupný vlastný Git install=Jednoduchá inštalácia -install_desc=Jednoducho spustite binárku pre vašu platformu, pošlite ju ako Docker, alebo ju získajte ako balíček. +install_desc=Jednoducho spustite binárku pre vašu platformu, pošlite ju ako Docker, alebo ju získajte ako balíček. platform=Multiplatformový platform_desc=Forgejo beží všade kde je možné preložiť Go: Windows, macOS, Linux, ARM, a podobne. Vyberte si! lightweight=Ľahká @@ -298,7 +298,6 @@ password_algorithm=Hašovací algoritmus hesla invalid_password_algorithm=Neplatný hash algoritmus hesla password_algorithm_helper=Nastavte algoritmus hashovania hesla. Algoritmy majú rôzne požiadavky a silu. Algoritmus argon2 je pomerne bezpečný, ale využíva veľa pamäte a môže byť nevhodný pre malé systémy. enable_update_checker=Povoliť kontrolu aktualizácií -enable_update_checker_helper=Pravidelne kontroluje nové verzie pripojením k gitea.io. [home] uname_holder=Používateľské meno alebo emailová adresa diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index cda98d17a4..ed6153b70f 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -235,7 +235,6 @@ default_enable_timetracking = Privzeto omogočite sledenje času default_enable_timetracking_popup = Privzeto omogočite sledenje času za nove shrambe. invalid_password_algorithm = Nepravilen algoritem za stiskanje gesla enable_update_checker = Omogočite preverjanje posodobitev -enable_update_checker_helper = Redno preverja izdaje novih različic tako, da se poveže s spletnim mestom gitea.io. env_config_keys = Konfiguracija okolja env_config_keys_prompt = V konfiguracijski datoteki bodo uporabljene tudi naslednje okoljske spremenljivke: smtp_from_invalid = Naslov "Pošlji e-pošto kot" je neveljaven diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 0c9aa521c6..153e40b113 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -185,7 +185,7 @@ network_error=Ağ hatası [startpage] app_desc=Zahmetsiz, kendi sunucunuzda barındırabileceğiniz Git servisi install=Kurulumu kolay -install_desc=Platformunuz için ikili dosyayı çalıştırın, Docker ile yükleyin veya paket olarak edinin. +install_desc=Platformunuz için ikili dosyayı çalıştırın, Docker ile yükleyin veya paket olarak edinin. platform=Farklı platformlarda çalışablir platform_desc=Forgejo Go ile derleme yapılabilecek her yerde çalışmaktadır: Windows, macOS, Linux, ARM, vb. Hangisini seviyorsanız onu seçin! lightweight=Hafif @@ -301,7 +301,6 @@ password_algorithm=Parola Hash Algoritması invalid_password_algorithm=Hatalı parola hash algoritması password_algorithm_helper=Parola hash algoritmasını ayarlayın. Algoritmalar değişen gereksinimlere ve güce sahiptirler. argon2 algoritması iyi özelliklere sahip olmasına rağmen fazla miktarda bellek kullanır ve küçük sistemler için uygun olmayabilir. enable_update_checker=Güncelleme Denetleyicisini Etkinleştir -enable_update_checker_helper=Düzenli olarak gitea.io'ya bağlanarak yeni yayınlanan sürümleri denetler. env_config_keys=Ortam Yapılandırma env_config_keys_prompt=Aşağıdaki ortam değişkenleri de yapılandırma dosyanıza eklenecektir: @@ -2357,7 +2356,7 @@ settings.tags.protection.allowed.teams=İzin verilen takımlar settings.tags.protection.allowed.noone=Hiç kimse settings.tags.protection.create=Etiketi Koru settings.tags.protection.none=Korumalı etiket yok. -settings.tags.protection.pattern.description=Birden çok etiketi eşleştirmek için tek bir ad, glob deseni veya normal ifade kullanabilirsiniz. Daha fazlası için korumalı etiketler rehberini okuyun. +settings.tags.protection.pattern.description=Birden çok etiketi eşleştirmek için tek bir ad, glob deseni veya normal ifade kullanabilirsiniz. Daha fazlası için korumalı etiketler rehberini okuyun. settings.bot_token=Bot Jetonu settings.chat_id=Sohbet Kimliği settings.thread_id=İş Parçacığı ID @@ -2904,12 +2903,12 @@ packages.size=Boyut packages.published=Yayınlandı defaulthooks=Varsayılan Web İstemcileri -defaulthooks.desc=Web İstemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak HTTP POST isteklerini sunucuya yapar. Burada tanımlanan Web İstemcileri varsayılandır ve tüm yeni depolara kopyalanır. web istemcileri kılavuzunda daha fazla bilgi edinin. +defaulthooks.desc=Web İstemcileri, belirli Gitea olayları tetiklendiğinde otomatik olarak HTTP POST isteklerini sunucuya yapar. Burada tanımlanan Web İstemcileri varsayılandır ve tüm yeni depolara kopyalanır. web istemcileri kılavuzunda daha fazla bilgi edinin. defaulthooks.add_webhook=Varsayılan Web İstemcisi Ekle defaulthooks.update_webhook=Varsayılan Web İstemcisini Güncelle systemhooks=Sistem Web İstemcileri -systemhooks.desc=Belirli Gitea olayları tetiklendiğinde Web istemcileri otomatik olarak bir sunucuya HTTP POST istekleri yapar. Burada tanımlanan web istemcileri sistemdeki tüm depolar üzerinde çalışır, bu yüzden lütfen bunun olabilecek tüm performans sonuçlarını göz önünde bulundurun. web istemcileri kılavuzunda daha fazla bilgi edinin. +systemhooks.desc=Belirli Gitea olayları tetiklendiğinde Web istemcileri otomatik olarak bir sunucuya HTTP POST istekleri yapar. Burada tanımlanan web istemcileri sistemdeki tüm depolar üzerinde çalışır, bu yüzden lütfen bunun olabilecek tüm performans sonuçlarını göz önünde bulundurun. web istemcileri kılavuzunda daha fazla bilgi edinin. systemhooks.add_webhook=Sistem Web İstemcisi Ekle systemhooks.update_webhook=Sistem Web İstemcisi Güncelle @@ -3014,7 +3013,7 @@ auths.tip.google_plus=OAuth2 istemci kimlik bilgilerini https://console.develope auths.tip.openid_connect=Bitiş noktalarını belirlemek için OpenID Connect Discovery URL'sini kullanın (/.well-known/openid-configuration) auths.tip.twitter=https://dev.twitter.com/apps adresine gidin, bir uygulama oluşturun ve “Bu uygulamanın Twitter ile oturum açmak için kullanılmasına izin ver” seçeneğinin etkin olduğundan emin olun auths.tip.discord=https://discordapp.com/developers/applications/me adresinde yeni bir uygulama kaydedin -auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://docs.gitea.com/development/oauth2-provider adresinde bulunabilir +auths.tip.gitea=Yeni bir OAuth2 uygulaması kaydedin. Rehber https://forgejo.org/docs/latest/user/oauth2-provider adresinde bulunabilir auths.tip.yandex=`https://oauth.yandex.com/client/new adresinde yeni bir uygulama oluşturun. "Yandex.Passport API'sı" bölümünden aşağıdaki izinleri seçin: "E-posta adresine erişim", "Kullanıcı avatarına erişim" ve "Kullanıcı adına, ad ve soyadına, cinsiyete erişim"` auths.tip.mastodon=Kimlik doğrulaması yapmak istediğiniz mastodon örneği için özel bir örnek URL girin (veya varsayılan olanı kullanın) auths.edit=Kimlik Doğrulama Kaynağı Düzenle diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index f30759686e..f1a4d28491 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -57,7 +57,7 @@ mirror=镜像 new_repo=创建仓库 new_migrate=迁移外部仓库 new_mirror=创建新的镜像 -new_fork=新的仓库Fork +new_fork=新的派生仓库 new_org=创建组织 new_project=创建项目 new_project_column=创建列 @@ -169,7 +169,9 @@ number_of_contributions_in_the_last_12_months=一年内 %s 次贡献 contributions_zero=目前还没有贡献。 less=更少的 more=更多的 -contributions_format = {contributions} 于 {month} {day}, {year} +contributions_format = {year}{month}{day} 当日有 {contributions} +contributions_few = 项贡献 +contributions_one = 贡献 [editor] buttons.heading.tooltip=添加标题 @@ -247,7 +249,7 @@ repo_path=仓库根目录 repo_path_helper=所有远程 Git 仓库将保存到此目录。 lfs_path=LFS根目录 lfs_path_helper=存储为Git LFS的文件将被存储在此目录。留空禁用LFS -run_user=以用户名运行 +run_user=以用户运行 run_user_helper=输入 Forgejo 运行的操作系统用户名。请注意,此用户必须具有对仓库根路径的访问权限。 domain=服务器域名 domain_helper=服务器的域名或主机地址。 @@ -319,7 +321,6 @@ password_algorithm=密码哈希算法 invalid_password_algorithm=无效的密码哈希算法 password_algorithm_helper=设置密码散列算法。算法有不同的要求和强度。 argon2 算法相当安全,但使用大量内存,因此可能不适合小型系统。 enable_update_checker=启用更新检查 -enable_update_checker_helper=通过连接到 gitea.io 定期检查新版本发布。 env_config_keys=环境配置 env_config_keys_prompt=以下环境变量也将应用于您的配置文件: allow_dots_in_usernames = 允许用户在用户名中使用英文句号。不影响已有的帐户。 @@ -629,6 +630,7 @@ admin_cannot_delete_self = 您无法以管理员的身份删除自己。请先 admin_cannot_delete_self=当您是管理员时,您不能删除自己。请先移除您的管理员权限 unsupported_login_type = 该账号使用的登录方式不支持删除此账户。 unset_password = 当前登录用户尚未设置密码。 +required_prefix = 输入必须以“%s”开头 [user] change_avatar=修改头像 @@ -662,6 +664,8 @@ follow_blocked_user = 您不能关注该用户,因为您已屏蔽该用户或 block = 屏蔽 unblock = 解除屏蔽 block_user.detail_3 = 该用户无法将您添加为合作者,您也无法将其添加为合作者。 +followers_one = %d 人关注 +following_one = %d 人被该用户关注 [settings] profile=个人信息 @@ -724,10 +728,10 @@ comment_type_group_issue_ref=工单引用 saved_successfully=您的设置已成功保存。 privacy=隐私设置 keep_activity_private=隐藏个人资料页面中的活动 -keep_activity_private_popup=使活动仅对您和管理员可见 +keep_activity_private_popup=您的活动将只对您自己和本实例的管理员可见 lookup_avatar_by_mail=使用电子邮箱地址查找头像 -federated_avatar_lookup=Federated Avatar 查找 +federated_avatar_lookup=查找联合头像 enable_custom_avatar=启动自定义头像 choose_new_avatar=选择新的头像 update_avatar=更新头像 @@ -980,6 +984,14 @@ blocked_since = 自 %s 起被屏蔽 user_unblock_success = 已成功取消对该用户的屏蔽。 user_block_success = 已成功屏蔽该用户。 change_password = 更改密码 +additional_repo_units_hint = 鼓励仓库启用更多功能 +hints = 提示 +update_hints = 更新提示 +additional_repo_units_hint_description = 在所有存在未启用的功能的仓库内显示一个“选择更多功能…”按钮。 +update_hints_success = 提示更改成功。 +pronouns_custom = 自定义 +pronouns = 代词 +pronouns_unspecified = 不指定 [repo] new_repo_helper=代码仓库包含了所有的项目文件,包括版本历史记录。已经在其他地方托管了?迁移仓库。 @@ -1064,7 +1076,7 @@ reactions_more=再加载 %d unit_disabled=站点管理员已禁用此仓库单元。 language_other=其它 adopt_search=输入用户名以搜索未被收录的仓库... (留空以查找全部) -adopt_preexisting_label=收录仓库 +adopt_preexisting_label=收录文件 adopt_preexisting=收录已存在的仓库 adopt_preexisting_content=从 %s 创建仓库 adopt_preexisting_success=从 %s 收录仓库成功 @@ -1307,7 +1319,7 @@ editor.file_is_a_symlink=`"%s" 是一个符号链接,无法在 web 编辑器 editor.filename_is_a_directory=此仓库中已存在名为“%s” 的目录。 editor.file_editing_no_longer_exists=正在编辑的文件 %s 已不存在。 editor.file_deleting_no_longer_exists=正在删除的文件 %s 已不存在。 -editor.file_changed_while_editing=文件内容在您进行编辑时已经发生变动。单击此处 查看变动的具体内容,或者 再次提交 覆盖已发生的变动。 +editor.file_changed_while_editing=文件内容在您进行编辑时被更改。单击此处 查看具体被更改的内容,或者 再次提交 覆盖其它在编辑时产生的更改。 editor.file_already_exists=此仓库已经存在名为 %s 的文件。 editor.commit_empty_file_header=提交一个空文件 editor.commit_empty_file_text=您要提交的文件是空的,继续吗? @@ -1396,7 +1408,7 @@ projects.column.set_default_desc=设置此列为未分类工单和合并请求 projects.column.unset_default=取消设为默认 projects.column.unset_default_desc=取消此列为默认值 projects.column.delete=删除列 -projects.column.deletion_desc=删除项目列会将所有相关工单移到“未分类”。是否继续? +projects.column.deletion_desc=删除项目列会将所有相关工单移到默认的列。是否继续? projects.column.color=颜色 projects.open=开启 projects.close=关闭 @@ -1921,7 +1933,7 @@ milestones.filter_sort.least_issues=工单从少到多 signing.will_sign=这个提交将用密钥 "%s" 签名。 signing.wont_sign.error=在检查提交是否可以被签名时出错。 -signing.wont_sign.nokey=没有可用的密钥来签署这个提交。 +signing.wont_sign.nokey=本实例没有密钥用于签署该提交。 signing.wont_sign.never=提交从未签名。 signing.wont_sign.always=提交总是签名。 signing.wont_sign.pubkey=由于您没有公钥关联到您的账户,提交将不会被签名。 @@ -1975,7 +1987,7 @@ activity.period.semiyearly=6 个月 activity.period.yearly=1年 activity.overview=概览 activity.active_prs_count_1=%d 合并请求 -activity.active_prs_count_n=%d 合并请求 +activity.active_prs_count_n=%d 项活动的合并请求 activity.merged_prs_count_1=已合并的合并请求 activity.merged_prs_count_n=已合并的合并请求 activity.opened_prs_count_1=新合并请求 @@ -1988,24 +2000,24 @@ activity.title.prs_merged_by=%[2]s 由 %[1]s 合并 activity.title.prs_opened_by=%[2]s 创建了 %[1]s activity.merged_prs_label=已合并 activity.opened_prs_label=已创建 -activity.active_issues_count_1=%d 工单 -activity.active_issues_count_n=%d 工单 +activity.active_issues_count_1=%d 项活动的工单 +activity.active_issues_count_n=%d 项活动的工单 activity.closed_issues_count_1=已关闭的工单 activity.closed_issues_count_n=已关闭的工单 -activity.title.issues_1=%d 工单 -activity.title.issues_n=%d 工单 +activity.title.issues_1=%d 项工单 +activity.title.issues_n=%d 项工单 activity.title.issues_closed_from=%s 从 %s 被关闭 activity.title.issues_created_by=%[2]s 创建了 %[1]s activity.closed_issue_label=已关闭 -activity.new_issues_count_1=创建工单 -activity.new_issues_count_n=创建工单 +activity.new_issues_count_1=新工单 +activity.new_issues_count_n=新工单 activity.new_issue_label=打开的 -activity.title.unresolved_conv_1=%d 未解决的会话 -activity.title.unresolved_conv_n=%d 未解决的会话 +activity.title.unresolved_conv_1=%d 项未解决的会话 +activity.title.unresolved_conv_n=%d 项未解决的会话 activity.unresolved_conv_desc=这些最近更新的工单和合并请求还没有解决。 activity.unresolved_conv_label=打开 -activity.title.releases_1=%d 版本发布 -activity.title.releases_n=%d 版本发布 +activity.title.releases_1=%d 个版本发布 +activity.title.releases_n=%d 个版本发布 activity.title.releases_published_by=%[2]s 发布了 %[1]s activity.published_release_label=已发布 activity.no_git_activity=在此期间没有任何提交活动。 @@ -2095,11 +2107,11 @@ settings.advanced_settings=高级设置 settings.wiki_desc=启用仓库百科 settings.use_internal_wiki=使用内置百科 settings.use_external_wiki=使用外部百科 -settings.external_wiki_url=外部 Wiki 链接 +settings.external_wiki_url=外部百科链接 settings.external_wiki_url_error=外部百科链接无效 settings.external_wiki_url_desc=当点击百科标签时,访问者将被重定向到外部百科系统的URL。 -settings.issues_desc=启用工单系统 -settings.use_internal_issue_tracker=使用内置的轻量级工单管理系统 +settings.issues_desc=启用仓库工单系统 +settings.use_internal_issue_tracker=使用内置的工单管理系统 settings.use_external_issue_tracker=使用外部的工单管理系统 settings.external_tracker_url=外部工单系统 URL settings.external_tracker_url_error=外部百科链接无效 @@ -2116,12 +2128,12 @@ settings.tracker_url_format_desc=使用占位符 {user}, {rep settings.enable_timetracker=启用时间跟踪 settings.allow_only_contributors_to_track_time=仅允许成员跟踪时间 settings.pulls_desc=启用合并请求 -settings.pulls.ignore_whitespace=忽略空白冲突 +settings.pulls.ignore_whitespace=忽略空格冲突 settings.pulls.enable_autodetect_manual_merge=启用自动检测手动合并 (注意:在某些特殊情况下可能发生错误判断) settings.pulls.allow_rebase_update=允许通过变基更新拉取请求分支 settings.pulls.default_delete_branch_after_merge=默认合并后删除合并请求分支 settings.pulls.default_allow_edits_from_maintainers=默认开启允许维护者编辑 -settings.releases_desc=启用发布 +settings.releases_desc=启用版本发布 settings.packages_desc=启用仓库软件包注册中心 settings.projects_desc=启用仓库项目 settings.actions_desc=启用 Actions @@ -2146,7 +2158,7 @@ settings.convert_fork_desc=您可以将该镜像仓库转换为普通仓库, settings.convert_fork_notices_1=该操作会将派生仓库转换为普通仓库,但该操作不可逆。 settings.convert_fork_confirm=转换仓库 settings.convert_fork_succeed=此派生仓库已经转换为普通仓库。 -settings.transfer=转移仓库所有权 +settings.transfer=转让仓库所有权 settings.transfer.rejected=代码库转移被拒绝。 settings.transfer.success=代码库转移成功。 settings.transfer_abort=取消转移 @@ -2188,7 +2200,7 @@ settings.delete_notices_fork_1=- 在此仓库删除后,它的派生仓库将 settings.deletion_success=仓库已被删除。 settings.update_settings_success=仓库设置已更新。 settings.update_settings_no_unit=该代码库应该至少允许某种形式的交互。 -settings.confirm_delete=删除本仓库 +settings.confirm_delete=删除仓库 settings.add_collaborator=增加协作者 settings.add_collaborator_success=协作者添加成功! settings.add_collaborator_inactive_user=无法添加未激活的用户作为合作者。 @@ -2230,12 +2242,12 @@ settings.webhook.delivery.success=一个事件已被添加到推送队列。可 settings.githooks_desc=Git Hook 是 Git 本身提供的功能。您可以在下方编辑 hook 文件以设置自定义操作。 settings.githook_edit_desc=如果钩子未启动,则会显示样例文件中的内容。如果想要删除某个钩子,则提交空白文本即可。 settings.githook_name=钩子名称 -settings.githook_content=钩子文本 -settings.update_githook=更新钩子设置 +settings.githook_content=钩子内容 +settings.update_githook=更新钩子 settings.add_webhook_desc=Forgejo 将向目标 URL 发送具有指定内容类型的 POST 请求。在 webhooks 指南 中阅读更多内容。 settings.payload_url=目标 URL settings.http_method=HTTP 方法 -settings.content_type=POST Content Type +settings.content_type=POST 内容类型 settings.secret=密钥文本 settings.slack_username=服务名称 settings.slack_icon_url=图标 URL @@ -2245,7 +2257,7 @@ settings.discord_icon_url=图标 URL settings.event_desc=触发条件: settings.event_push_only=推送事件 settings.event_send_everything=所有事件 -settings.event_choose=自定义事件... +settings.event_choose=自定义事件… settings.event_header_repository=仓库事件 settings.event_create=创建 settings.event_create_desc=创建分支或标签 @@ -2264,9 +2276,9 @@ settings.event_repository_desc=创建或删除仓库 settings.event_header_issue=工单事件 settings.event_issues=工单 settings.event_issues_desc=工单已打开、已关闭、已重新打开或已编辑。 -settings.event_issue_assign=工单已指派 +settings.event_issue_assign=工单已分配 settings.event_issue_assign_desc=工单已被指派或取消指派。 -settings.event_issue_label=已标记工单 +settings.event_issue_label=工单已分类 settings.event_issue_label_desc=工单标签被更新或清除。 settings.event_issue_milestone=工单被收入里程碑中 settings.event_issue_milestone_desc=工单被收入或取消收入里程碑中。 @@ -2409,7 +2421,7 @@ settings.block_outdated_branch=如果拉取请求已经过时,阻止合并 settings.block_outdated_branch_desc=当头部分支落后基础分支时,不能合并。 settings.default_branch_desc=请选择一个默认的分支用于合并请求和提交: settings.merge_style_desc=合并方式 -settings.default_merge_style_desc=合并请求的默认合并样式: +settings.default_merge_style_desc=默认合并方式 settings.choose_branch=选择一个分支... settings.no_protected_branch=没有受保护的分支 settings.edit_protected_branch=编辑 @@ -2423,10 +2435,10 @@ settings.tags.protection.allowed=允许列表 settings.tags.protection.allowed.users=允许的账号 settings.tags.protection.allowed.teams=允许的团队 settings.tags.protection.allowed.noone=无 -settings.tags.protection.create=保护Git标签 +settings.tags.protection.create=新建规则 settings.tags.protection.none=没有受保护的Git标签 settings.tags.protection.pattern.description=你可以使用单个名称或 glob 模式匹配或正则表达式来匹配多个标签。了解详情请访问 受保护Git标签指南。 -settings.bot_token=Bot 令牌 +settings.bot_token=机器人令牌 settings.chat_id=聊天 ID settings.thread_id=线程 ID settings.matrix.homeserver_url=主服务器网址 @@ -2651,7 +2663,7 @@ mirror_sync = 已同步 vendored = Vendored issues.blocked_by_user = 你无法在此仓库创建工单,因为你已被仓库所有者屏蔽。 issues.comment.blocked_by_user = 你无法对此工单进行评论,因为你已被仓库所有者或此工单的发布者屏蔽。 -settings.wiki_rename_branch_main_desc = 将 Wiki 内部使用的分支重命名为“%s”。 此操作是永久性的且不可撤消。 +settings.wiki_rename_branch_main_desc = 将百科内部使用的分支重命名为“%s”。 此操作是永久性的且不可撤消。 generated = 已生成 editor.invalid_commit_mail = 用于创建提交的邮件地址无效。 pulls.blocked_by_user = 你无法在此存储库上创建合并请求,因为您已被仓库所有者屏蔽。 @@ -2664,23 +2676,23 @@ settings.wiki_globally_editable = 允许任何人编辑百科 settings.mirror_settings.pushed_repository = 已推送的仓库 settings.new_owner_blocked_doer = 新所有者已将你拉黑。 settings.enter_repo_name = 输入所有者和仓库的名称: -settings.wiki_rename_branch_main = 标准化 Wiki 分支名称 +settings.wiki_rename_branch_main = 标准化百科分支名称 settings.wiki_rename_branch_main_notices_1 = 此操作无法撤消。 -settings.wiki_branch_rename_success = wiki 仓库的分支名称已成功规范化。 -settings.confirm_wiki_branch_rename = 重命名 wiki 分支 +settings.wiki_branch_rename_success = 百科仓库的分支名称已成功规范化。 +settings.confirm_wiki_branch_rename = 重命名百科分支 pulls.commit_ref_at = `在提交 %[2]s 中引用了此合并请求` desc.sha256 = SHA256 settings.ignore_stale_approvals = 忽略过时的批准 settings.ignore_stale_approvals_desc = 不对旧的提交(过时的审查)计入已批准的合并请求数量。注:如过期的审核已被取消,则无需设置。 -settings.archive.mirrors_unavailable = 如果仓库已存档,则仓库镜像不再可用。 -settings.wiki_rename_branch_main_notices_2 = 这将预先重命名 %s 的存储库 wiki 的内部分支。 现存的检出方式需要更新。 -settings.wiki_branch_rename_failure = 无法标准化存储库 wiki 的分支名称。 +settings.archive.mirrors_unavailable = 不能镜像已归档的仓库。 +settings.wiki_rename_branch_main_notices_2 = 这将预先重命名 %s 的存储库百科的内部分支。 现存的检出方式需要更新。 +settings.wiki_branch_rename_failure = 无法标准化存储库百科的分支名称。 settings.add_collaborator_blocked_our = 因仓库所有者已将其拉黑,不能添加该用户为协作者。 settings.add_collaborator_blocked_them = 因该用户已将仓库所有者拉黑,不能添加该用户为协作者。 -settings.units.units = 仓库单元 +settings.units.units = 仓库功能 pulls.fast_forward_only_merge_pull_request = 仅快速向前 settings.units.overview = 概览 -settings.units.add_more = 添加更多 +settings.units.add_more = 添加更多… file_follow = 跟随符号链接 pulls.reopen_failed.head_branch = 因头部分支不再存在,该合并请求不能再被重新打开。 pulls.reopen_failed.base_branch = 因基础分支不再存在,该合并请求不能再被重新打开。 @@ -2703,7 +2715,21 @@ n_branch_one = %s 分支 n_branch_few = %s 分支 n_tag_one = %s 标签 n_tag_few = %s 标签 -editor.commit_id_not_matching = 此提交ID与您当前编辑的不匹配,将提交至新分支后合并。 +editor.commit_id_not_matching = 此提交ID与您当前编辑的不匹配。请提交到一个新的分支,然后再将这个新的分支合并回当前分支。 +issues.num_participants_one = %d 个参与者 +issues.archived_label_description = (已归档)%s +editor.push_out_of_date = 推送似乎已过期。 +settings.enforce_on_admins = 对仓库的管理员适用该规则 +settings.enforce_on_admins_desc = 使仓库管理员也须遵守此规则。 +settings.sourcehut_builds.secrets = 密钥 +size_format = %[1]s: %[2]s, %[3]s: %[4]s +settings.sourcehut_builds.graphql_url = GraphQL URL (例如: https://builds.sr.ht/query) +settings.add_webhook.invalid_path = 路径中不能包含“.”或“..”,也不能在开头或结尾中使用斜杠。 +settings.sourcehut_builds.secrets_helper = 给予任务访问构建密钥的权限(需要 SECRETS:RO 权限) +release.download_count_one = %s 下载 +release.download_count_few = %s 下载 +release.system_generated = 此附件是自动生成的。 +pulls.ready_for_review = 准备好接受评审了吗? [graphs] component_loading=正在加载 %s... @@ -3160,7 +3186,7 @@ auths.tip.google_plus=从谷歌 API 控制台 (https://console.developers.google auths.tip.openid_connect=使用 OpenID 连接发现 URL (/.well-known/openid-configuration) 来指定终点 auths.tip.twitter=访问 https://dev.twitter.com/apps,创建应用并确保启用了"允许此应用程序用于登录 Twitter"的选项。 auths.tip.discord=在 https://discordapp.com/developers/applications/me 上注册新应用程序 -auths.tip.gitea=注册一个新的 OAuth2 应用程序。可以访问 https://docs.gitea.com/development/oauth2-provider 查看帮助 +auths.tip.gitea=注册一个新的 OAuth2 应用程序。可以访问 https://forgejo.org/docs/latest/user/oauth2-provider 查看帮助 auths.tip.yandex=在 https://oauth.yandex.com/client/new 上创建一个新的应用程序。在“ Yandex.Passport API”这部分中选择以下权限:“访问电子邮件地址(Access to email address)”,“访问用户头像(Access to user avatar)”和“访问用户名,名字和姓氏,性别(Access to username, first name and surname, genderAccess to username, first name and surname, gender)” auths.tip.mastodon=输入您想要认证的 mastodon 实例的自定义 URL (或使用默认值) auths.edit=修改认证源 @@ -3386,6 +3412,7 @@ self_check.database_fix_mysql=对于MySQL/MariaDB用户,您可以使用“gite auths.tips.gmail_settings = Gmail 设置: auths.tip.gitlab_new = 在 https://gitlab.com/-/profile/applications 上注册新应用 config_settings = 设置 +config_summary = 概况 [action] create_repo=创建了仓库 %s @@ -3776,3 +3803,18 @@ fuzzy = 模糊 code_search_by_git_grep = 当前搜索结果由 git grep 提供,如果站点管理员启用了仓库索引可能会有更好的结果。 match = 匹配 match_tooltip = 仅包含与搜索词完全匹配的结果 +fuzzy_tooltip = 在搜索结果中包含与搜索词相近的项目 + + +[munits.data] +pib = PiB +gib = GiB +tib = TiB +eib = EiB +b = B +kib = KiB +mib = MiB + +[markup] +filepreview.line = %[2]s 中的第 %[1]d 行 +filepreview.lines = %[3]s 中的第 %[1]d 到 %[2]d 行 \ No newline at end of file diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 1faabf9b99..96d5d0b29c 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -314,7 +314,6 @@ password_algorithm=密碼雜湊演算法 invalid_password_algorithm=無效的密碼雜湊演算法 password_algorithm_helper=設定密碼雜湊演算法。演算法有不同的需求與強度。argon2 演算法雖然較安全但會使用大量記憶體,可能不適用於小型系統。 enable_update_checker=啟用更新檢查器 -enable_update_checker_helper=定期連線到 gitea.io 檢查更新。 run_user_helper = 輸入 Forgejo 執行的作業系統使用者名稱。請注意,此使用者必須具有對儲存庫根路徑的訪問許可權。 [home] diff --git a/package-lock.json b/package-lock.json index cba2278ead..be6eadaee6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "packages": { "": { "dependencies": { - "@citation-js/core": "0.7.9", - "@citation-js/plugin-bibtex": "0.7.9", - "@citation-js/plugin-csl": "0.7.9", + "@citation-js/core": "0.7.11", + "@citation-js/plugin-bibtex": "0.7.11", + "@citation-js/plugin-csl": "0.7.11", "@citation-js/plugin-software-formats": "0.6.1", "@github/markdown-toolbar-element": "2.2.3", "@github/relative-time-element": "4.4.0", @@ -34,17 +34,17 @@ "katex": "0.16.10", "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.9.0", - "mini-css-extract-plugin": "2.8.1", + "mini-css-extract-plugin": "2.9.0", "minimatch": "9.0.4", "monaco-editor": "0.47.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", "postcss": "8.4.38", "postcss-loader": "8.1.1", - "postcss-nesting": "12.1.1", + "postcss-nesting": "12.1.2", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.13.0", + "swagger-ui-dist": "5.17.2", "tailwindcss": "3.4.3", "temporal-polyfill": "0.2.4", "throttle-debounce": "5.0.0", @@ -54,9 +54,9 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.4.21", + "vue": "3.4.24", "vue-bar-graph": "2.0.0", - "vue-chartjs": "5.3.0", + "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", @@ -64,10 +64,10 @@ "wrap-ansi": "9.0.0" }, "devDependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.42.1", + "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", + "@playwright/test": "1.43.0", "@stoplight/spectral-cli": "6.11.1", - "@stylistic/eslint-plugin-js": "1.7.0", + "@stylistic/eslint-plugin-js": "1.7.2", "@stylistic/stylelint-plugin": "2.1.1", "@vitejs/plugin-vue": "5.0.4", "@vue/test-utils": "2.4.5", @@ -78,25 +78,25 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.4.0", + "eslint-plugin-regexp": "2.5.0", "eslint-plugin-sonarjs": "0.25.1", "eslint-plugin-unicorn": "52.0.0", - "eslint-plugin-vitest": "0.4.1", + "eslint-plugin-vitest": "0.5.4", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "9.24.0", + "eslint-plugin-vue": "9.25.0", "eslint-plugin-vue-scoped-css": "2.8.0", - "eslint-plugin-wc": "2.0.4", - "happy-dom": "14.5.0", + "eslint-plugin-wc": "2.1.0", + "happy-dom": "14.7.1", "markdownlint-cli": "0.39.0", "postcss-html": "1.6.0", - "stylelint": "16.3.1", + "stylelint": "16.4.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "updates": "16.0.1", - "vite-string-plugin": "1.1.5", - "vitest": "1.4.0" + "vite-string-plugin": "1.2.0", + "vitest": "1.5.2" }, "engines": { "node": ">= 18.0.0" @@ -262,9 +262,9 @@ "integrity": "sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==" }, "node_modules/@citation-js/core": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.9.tgz", - "integrity": "sha512-fSbkB32JayDChZnAYC/kB+sWHRvxxL7ibVetyBOyzOc+5aCnjb6UVsbcfhnkOIEyAMoRRvWDyFmakEoTtA5ttQ==", + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/@citation-js/core/-/core-0.7.11.tgz", + "integrity": "sha512-evQtyzeW+Gbmq+xWciIq9sbcvXXDbm8q32orD/HDd5ay6RQFKoW/BKxBLp+Nmpxgspb9sxTJn3iFK7+jxOTNTw==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -292,9 +292,9 @@ } }, "node_modules/@citation-js/plugin-bibtex": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.9.tgz", - "integrity": "sha512-gIJpCd6vmmTOcRfDrSOjtoNhw2Mi94UwFxmgJ7GwkXyTYcNheW5VlMMo1tlqjakJGARQ0eOsKcI57gSPqJSS2g==", + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-bibtex/-/plugin-bibtex-0.7.11.tgz", + "integrity": "sha512-G4vEmLjrQUxgBIp3ffWN5dDOlwjPsrRSi/uTyxDJuFgKBD8GR1eO7Y/ZcePNAOHMqUxG7lxhhBbZJwcJZNVHYw==", "dependencies": { "@citation-js/date": "^0.5.0", "@citation-js/name": "^0.4.2", @@ -320,9 +320,9 @@ } }, "node_modules/@citation-js/plugin-csl": { - "version": "0.7.9", - "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.9.tgz", - "integrity": "sha512-mbD7CnUiPOuVnjeJwo+d0RGUcY0PE8n01gHyjq0qpTeS42EGmQ9+LzqfsTUVWWBndTwc6zLRuIF1qFAUHKE4oA==", + "version": "0.7.11", + "resolved": "https://registry.npmjs.org/@citation-js/plugin-csl/-/plugin-csl-0.7.11.tgz", + "integrity": "sha512-4OGZ9wHZDfpgiPU2cOXWGuKt7P+ndGWAeLG95nOG+DXe5U+f9EEZTXfaM4C99x8Ri+g6JklR96A3kuYZxYLllg==", "dependencies": { "@citation-js/date": "^0.5.0", "citeproc": "^2.4.6" @@ -866,9 +866,9 @@ } }, "node_modules/@eslint-community/eslint-plugin-eslint-comments": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.1.0.tgz", - "integrity": "sha512-B2mwipifrBS5E00vN8vME68laPMZ0h3sNGOEDj5g9iUN9k5EU99Omq0Nc325eKNoFFDnDtiHp3DqIjO+1bstag==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-plugin-eslint-comments/-/eslint-plugin-eslint-comments-4.3.0.tgz", + "integrity": "sha512-6e93KtgsndNkvwCCa07LOQJSwzzLLxwrFll3+huyFoiiQXWG0KBcmo0Q1bVgYQQDLfWOOZl2VPBsXqZL6vHIBQ==", "dev": true, "dependencies": { "escape-string-regexp": "^4.0.0", @@ -878,7 +878,7 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/@eslint-community/eslint-utils": { @@ -1351,12 +1351,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.42.1.tgz", - "integrity": "sha512-Gq9rmS54mjBL/7/MvBaNOBwbfnh7beHvS6oS4srqXFcQHpQCV1+c8JXWE8VLPyRDhgS3H8x8A7hztqI9VnwrAQ==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.43.0.tgz", + "integrity": "sha512-Ebw0+MCqoYflop7wVKj711ccbNlrwTBCtjY5rlbiY9kHL2bCYxq+qltK6uPsVBGGAOb033H2VO0YobcQVxoW7Q==", "dev": true, "dependencies": { - "playwright": "1.42.1" + "playwright": "1.43.0" }, "bin": { "playwright": "cli.js" @@ -2126,12 +2126,12 @@ } }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.0.tgz", - "integrity": "sha512-PN6On/+or63FGnhhMKSQfYcWutRlzOiYlVdLM6yN7lquoBTqUJHYnl4TA4MHwiAt46X5gRxDr1+xPZ1lOLcL+Q==", + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.7.2.tgz", + "integrity": "sha512-ZYX7C5p7zlHbACwFLU+lISVh6tdcRP/++PWegh2Sy0UgMT5kU0XkPa2tKWEtJYzZmPhJxu9LxbnWcnE/tTwSDQ==", "dev": true, "dependencies": { - "@types/eslint": "^8.56.2", + "@types/eslint": "^8.56.8", "acorn": "^8.11.3", "escape-string-regexp": "^4.0.0", "eslint-visitor-keys": "^3.4.3", @@ -2224,9 +2224,9 @@ } }, "node_modules/@types/eslint": { - "version": "8.56.7", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.7.tgz", - "integrity": "sha512-SjDvI/x3zsZnOkYZ3lCt9lOZWZLB2jIlNKz+LBgCtDurK0JZcwucxYHn1w2BJkD34dgX9Tjnak0txtq4WTggEA==", + "version": "8.56.9", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.56.9.tgz", + "integrity": "sha512-W4W3KcqzjJ0sHg2vAq9vfml6OhsJ53TcUjUqfzzZf/EChUtwspszj/S0pzMxnfRcO55/iGq47dscXw71Fxc4Zg==", "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -2545,13 +2545,13 @@ } }, "node_modules/@vitest/expect": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.4.0.tgz", - "integrity": "sha512-Jths0sWCJZ8BxjKe+p+eKsoqev1/T8lYcrjavEaz8auEJ4jAVY0GwW3JKmdVU4mmNPLPHixh4GNXP7GFtAiDHA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-1.5.2.tgz", + "integrity": "sha512-rf7MTD1WCoDlN3FfYJ9Llfp0PbdtOMZ3FIF0AVkDnKbp3oiMW1c8AmvRZBcqbAhDUAvF52e9zx4WQM1r3oraVA==", "dev": true, "dependencies": { - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/spy": "1.5.2", + "@vitest/utils": "1.5.2", "chai": "^4.3.10" }, "funding": { @@ -2559,12 +2559,12 @@ } }, "node_modules/@vitest/runner": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.4.0.tgz", - "integrity": "sha512-EDYVSmesqlQ4RD2VvWo3hQgTJ7ZrFQ2VSJdfiJiArkCerDAGeyF1i6dHkmySqk573jLp6d/cfqCN+7wUB5tLgg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-1.5.2.tgz", + "integrity": "sha512-7IJ7sJhMZrqx7HIEpv3WrMYcq8ZNz9L6alo81Y6f8hV5mIE6yVZsFoivLZmr0D777klm1ReqonE9LyChdcmw6g==", "dev": true, "dependencies": { - "@vitest/utils": "1.4.0", + "@vitest/utils": "1.5.2", "p-limit": "^5.0.0", "pathe": "^1.1.1" }, @@ -2600,9 +2600,9 @@ } }, "node_modules/@vitest/snapshot": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.4.0.tgz", - "integrity": "sha512-saAFnt5pPIA5qDGxOHxJ/XxhMFKkUSBJmVt5VgDsAqPTX6JP326r5C/c9UuCMPoXNzuudTPsYDZCoJ5ilpqG2A==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-1.5.2.tgz", + "integrity": "sha512-CTEp/lTYos8fuCc9+Z55Ga5NVPKUgExritjF5VY7heRFUfheoAqBneUlvXSUJHUZPjnPmyZA96yLRJDP1QATFQ==", "dev": true, "dependencies": { "magic-string": "^0.30.5", @@ -2626,9 +2626,9 @@ } }, "node_modules/@vitest/spy": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.4.0.tgz", - "integrity": "sha512-Ywau/Qs1DzM/8Uc+yA77CwSegizMlcgTJuYGAi0jujOteJOUf1ujunHThYo243KG9nAyWT3L9ifPYZ5+As/+6Q==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-1.5.2.tgz", + "integrity": "sha512-xCcPvI8JpCtgikT9nLpHPL1/81AYqZy1GCy4+MCHBE7xi8jgsYkULpW5hrx5PGLgOQjUpb6fd15lqcriJ40tfQ==", "dev": true, "dependencies": { "tinyspy": "^2.2.0" @@ -2638,9 +2638,9 @@ } }, "node_modules/@vitest/utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.4.0.tgz", - "integrity": "sha512-mx3Yd1/6e2Vt/PUC98DcqTirtfxUyAZ32uK82r8rZzbtBeBo+nqgnjx/LvqQdWsrvNtm14VmurNgcf4nqY5gJg==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-1.5.2.tgz", + "integrity": "sha512-sWOmyofuXLJ85VvXNsroZur7mOJGiQeM0JN3/0D1uU8U9bGFM69X1iqHaRXl6R8BwaLY6yPCogP257zxTzkUdA==", "dev": true, "dependencies": { "diff-sequences": "^29.6.3", @@ -2668,105 +2668,102 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.21.tgz", - "integrity": "sha512-MjXawxZf2SbZszLPYxaFCjxfibYrzr3eYbKxwpLR9EQN+oaziSu3qKVbwBERj1IFIB8OLUewxB5m/BFzi613og==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.24.tgz", + "integrity": "sha512-vbW/tgbwJYj62N/Ww99x0zhFTkZDTcGh3uwJEuadZ/nF9/xuFMC4693P9r+3sxGXISABpDKvffY5ApH9pmdd1A==", "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/shared": "3.4.21", + "@babel/parser": "^7.24.4", + "@vue/shared": "3.4.24", "entities": "^4.5.0", "estree-walker": "^2.0.2", - "source-map-js": "^1.0.2" + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.21.tgz", - "integrity": "sha512-IZC6FKowtT1sl0CR5DpXSiEB5ayw75oT2bma1BEhV7RRR1+cfwLrxc2Z8Zq/RGFzJ8w5r9QtCOvTjQgdn0IKmA==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.24.tgz", + "integrity": "sha512-4XgABML/4cNndVsQndG6BbGN7+EoisDwi3oXNovqL/4jdNhwvP8/rfRMTb6FxkxIxUUtg6AI1/qZvwfSjxJiWA==", "dependencies": { - "@vue/compiler-core": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-core": "3.4.24", + "@vue/shared": "3.4.24" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.21.tgz", - "integrity": "sha512-me7epoTxYlY+2CUM7hy9PCDdpMPfIwrOvAXud2Upk10g4YLv9UBW7kL798TvMeDhPthkZ0CONNrK2GoeI1ODiQ==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.24.tgz", + "integrity": "sha512-nRAlJUK02FTWfA2nuvNBAqsDZuERGFgxZ8sGH62XgFSvMxO2URblzulExsmj4gFZ8e+VAyDooU9oAoXfEDNxTA==", "dependencies": { - "@babel/parser": "^7.23.9", - "@vue/compiler-core": "3.4.21", - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21", + "@babel/parser": "^7.24.4", + "@vue/compiler-core": "3.4.24", + "@vue/compiler-dom": "3.4.24", + "@vue/compiler-ssr": "3.4.24", + "@vue/shared": "3.4.24", "estree-walker": "^2.0.2", - "magic-string": "^0.30.7", - "postcss": "^8.4.35", - "source-map-js": "^1.0.2" + "magic-string": "^0.30.10", + "postcss": "^8.4.38", + "source-map-js": "^1.2.0" } }, "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", - "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.21.tgz", - "integrity": "sha512-M5+9nI2lPpAsgXOGQobnIueVqc9sisBFexh5yMIMRAPYLa7+5wEJs8iqOZc1WAa9WQbx9GR2twgznU8LTIiZ4Q==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.24.tgz", + "integrity": "sha512-ZsAtr4fhaUFnVcDqwW3bYCSDwq+9Gk69q2r/7dAHDrOMw41kylaMgOP4zRnn6GIEJkQznKgrMOGPMFnLB52RbQ==", "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-dom": "3.4.24", + "@vue/shared": "3.4.24" } }, "node_modules/@vue/reactivity": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.21.tgz", - "integrity": "sha512-UhenImdc0L0/4ahGCyEzc/pZNwVgcglGy9HVzJ1Bq2Mm9qXOpP8RyNTjookw/gOCUlXSEtuZ2fUg5nrHcoqJcw==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.24.tgz", + "integrity": "sha512-nup3fSYg4i4LtNvu9slF/HF/0dkMQYfepUdORBcMSsankzRPzE7ypAFurpwyRBfU1i7Dn1kcwpYsE1wETSh91g==", "dependencies": { - "@vue/shared": "3.4.21" + "@vue/shared": "3.4.24" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.21.tgz", - "integrity": "sha512-pQthsuYzE1XcGZznTKn73G0s14eCJcjaLvp3/DKeYWoFacD9glJoqlNBxt3W2c5S40t6CCcpPf+jG01N3ULyrA==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.24.tgz", + "integrity": "sha512-c7iMfj6cJMeAG3s5yOn9Rc5D9e2/wIuaozmGf/ICGCY3KV5H7mbTVdvEkd4ZshTq7RUZqj2k7LMJWVx+EBiY1g==", "dependencies": { - "@vue/reactivity": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/reactivity": "3.4.24", + "@vue/shared": "3.4.24" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.21.tgz", - "integrity": "sha512-gvf+C9cFpevsQxbkRBS1NpU8CqxKw0ebqMvLwcGQrNpx6gqRDodqKqA+A2VZZpQ9RpK2f9yfg8VbW/EpdFUOJw==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.24.tgz", + "integrity": "sha512-uXKzuh/Emfad2Y7Qm0ABsLZZV6H3mAJ5ZVqmAOlrNQRf+T5mxpPGZBfec1hkP41t6h6FwF6RSGCs/gd8WbuySQ==", "dependencies": { - "@vue/runtime-core": "3.4.21", - "@vue/shared": "3.4.21", + "@vue/runtime-core": "3.4.24", + "@vue/shared": "3.4.24", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.21.tgz", - "integrity": "sha512-aV1gXyKSN6Rz+6kZ6kr5+Ll14YzmIbeuWe7ryJl5muJ4uwSwY/aStXTixx76TwkZFJLm1aAlA/HSWEJ4EyiMkg==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.24.tgz", + "integrity": "sha512-H+DLK4sQF6sRgzKyofmlEVBIV/9KrQU6HIV7nt6yIwSGGKvSwlV8pqJlebUKLpbXaNHugdSfAbP6YmXF69lxow==", "dependencies": { - "@vue/compiler-ssr": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-ssr": "3.4.24", + "@vue/shared": "3.4.24" }, "peerDependencies": { - "vue": "3.4.21" + "vue": "3.4.24" } }, "node_modules/@vue/shared": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.21.tgz", - "integrity": "sha512-PuJe7vDIi6VYSinuEbUIQgMIRZGgM8e4R+G+/dQTk0X1NEdvgvvgv7m+rfmDH1gZzyA1OjjoWskvHlfRNfQf3g==" + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.24.tgz", + "integrity": "sha512-BW4tajrJBM9AGAknnyEw5tO2xTmnqgup0VTnDAMcxYmqOX0RG0b9aSUGAbEKolD91tdwpA6oCwbltoJoNzpItw==" }, "node_modules/@vue/test-utils": { "version": "2.4.5", @@ -3993,9 +3990,9 @@ } }, "node_modules/css-functions-list": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.1.tgz", - "integrity": "sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/css-functions-list/-/css-functions-list-3.2.2.tgz", + "integrity": "sha512-c+N0v6wbKVxTu5gOBBFkr9BEdBWaqqjQeiJ8QvSRIJOf+UxlJh930m8e6/WNeODIK0mYLFkoONrnj16i2EcvfQ==", "dev": true, "engines": { "node": ">=12 || >=16" @@ -5773,9 +5770,9 @@ } }, "node_modules/eslint-plugin-regexp": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.4.0.tgz", - "integrity": "sha512-OL2S6VPjQhs9s/NclQ0qattVq1J0GU8ox70/HIVy5Dxw+qbbdd7KQkyucsez2clEQjvdtDe12DTnPphFFUyXFg==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-regexp/-/eslint-plugin-regexp-2.5.0.tgz", + "integrity": "sha512-I7vKcP0o75WS5SHiVNXN+Eshq49sbrweMQIuqSL3AId9AwDe9Dhbfug65vw64LxmOd4v+yf5l5Xt41y9puiq0g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", @@ -5839,18 +5836,18 @@ } }, "node_modules/eslint-plugin-vitest": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.4.1.tgz", - "integrity": "sha512-+PnZ2u/BS+f5FiuHXz4zKsHPcMKHie+K+1Uvu/x91ovkCMEOJqEI8E9Tw1Wzx2QRz4MHOBHYf1ypO8N1K0aNAA==", + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.5.4.tgz", + "integrity": "sha512-um+odCkccAHU53WdKAw39MY61+1x990uXjSPguUCq3VcEHdqJrOb8OTMrbYlY6f9jAKx7x98kLVlIe3RJeJqoQ==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^7.4.0" + "@typescript-eslint/utils": "^7.7.1" }, "engines": { "node": "^18.0.0 || >= 20.0.0" }, "peerDependencies": { - "eslint": ">=8.0.0", + "eslint": "^8.57.0 || ^9.0.0", "vitest": "*" }, "peerDependenciesMeta": { @@ -5868,10 +5865,110 @@ "integrity": "sha512-ZSsVOaOIig0oVLzRTyk8lUfBfqzWxr/J3/NFMfGGRIkGQPejJYmDH3gXmSJxAojts77uzAGB/UmVrwi2DC4LYA==", "dev": true }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/scope-manager": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.7.1.tgz", + "integrity": "sha512-PytBif2SF+9SpEUKynYn5g1RHFddJUcyynGpztX3l/ik7KmZEv19WCMhUBkHXPU9es/VWGD3/zg3wg90+Dh2rA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/types": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.7.1.tgz", + "integrity": "sha512-AmPmnGW1ZLTpWa+/2omPrPfR7BcbUU4oha5VIbSbS1a1Tv966bklvLNXxp3mrbc+P2j4MNOTfDffNsk4o0c6/w==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.7.1.tgz", + "integrity": "sha512-CXe0JHCXru8Fa36dteXqmH2YxngKJjkQLjxzoj6LYwzZ7qZvgsLSc+eqItCrqIop8Vl2UKoAi0StVWu97FQZIQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/visitor-keys": "7.7.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/utils": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.7.1.tgz", + "integrity": "sha512-QUvBxPEaBXf41ZBbaidKICgVL8Hin0p6prQDu6bbetWo39BKbWJxRsErOzMNT1rXvTll+J7ChrbmMCXM9rsvOQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.15", + "@types/semver": "^7.5.8", + "@typescript-eslint/scope-manager": "7.7.1", + "@typescript-eslint/types": "7.7.1", + "@typescript-eslint/typescript-estree": "7.7.1", + "semver": "^7.6.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-plugin-vitest/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.7.1.tgz", + "integrity": "sha512-gBL3Eq25uADw1LQ9kVpf3hRM+DWzs0uZknHYK3hq4jcTPqVCClHGDnB6UUUV2SFeBeA4KWHWbbLqmbGcZ4FYbw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.7.1", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/eslint-plugin-vue": { - "version": "9.24.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.24.0.tgz", - "integrity": "sha512-9SkJMvF8NGMT9aQCwFc5rj8Wo1XWSMSHk36i7ZwdI614BU7sIOR28ZjuFPKp8YGymZN12BSEbiSwa7qikp+PBw==", + "version": "9.25.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.25.0.tgz", + "integrity": "sha512-tDWlx14bVe6Bs+Nnh3IGrD+hb11kf2nukfm6jLsmJIhmiRQ1SUaksvwY9U5MvPB0pcrg0QK0xapQkfITs3RKOA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", @@ -5887,7 +5984,7 @@ "node": "^14.17.0 || >=16.0.0" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0" + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" } }, "node_modules/eslint-plugin-vue-scoped-css": { @@ -5917,9 +6014,9 @@ } }, "node_modules/eslint-plugin-wc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.0.4.tgz", - "integrity": "sha512-ORu7MBv0hXIvq894EJad70m+AvHGbmrDdKT6lcgtCVVhEbuIAyxg0ilfqqqHOmsh8PfcUBeEae3y7CElKvm1KQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-wc/-/eslint-plugin-wc-2.1.0.tgz", + "integrity": "sha512-s/BGOtmpgQ2yifR6EC1OM9t0DwYLgg4ZAL07Kw4eXvBb5TYaPafI+65tswvnZvhH8FqcjERLbBZPPvYsvinkfg==", "dev": true, "dependencies": { "is-valid-element-name": "^1.0.0", @@ -6678,9 +6775,9 @@ } }, "node_modules/happy-dom": { - "version": "14.5.0", - "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.5.0.tgz", - "integrity": "sha512-KvOtCq7eamc7cjihM0F1wj6FptuXzooc3Typa7Vgu6ns2uKGXC4BIFlK80SdH2w8zcW0gtxpBVI/sUqbYtljDA==", + "version": "14.7.1", + "resolved": "https://registry.npmjs.org/happy-dom/-/happy-dom-14.7.1.tgz", + "integrity": "sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==", "dev": true, "dependencies": { "entities": "^4.5.0", @@ -8813,9 +8910,9 @@ } }, "node_modules/mini-css-extract-plugin": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.8.1.tgz", - "integrity": "sha512-/1HDlyFRxWIZPI1ZpgqlZ8jMw/1Dp/dl3P0L1jtZ+zVcHqwPhGwaJwKL00WVgfnBy6PWCde9W65or7IIETImuA==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.0.tgz", + "integrity": "sha512-Zs1YsZVfemekSZG+44vBsYTLQORkPMwnlv+aehcxK/NLKC+EGhDB39/YePYYqx/sTk6NnYpuqikhSn7+JIevTA==", "dependencies": { "schema-utils": "^4.0.0", "tapable": "^2.2.1" @@ -9532,12 +9629,12 @@ "dev": true }, "node_modules/playwright": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.42.1.tgz", - "integrity": "sha512-PgwB03s2DZBcNRoW+1w9E+VkLBxweib6KTXM0M3tkiT4jVxKSi6PmVJ591J+0u10LUrgxB7dLRbiJqO5s2QPMg==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.43.0.tgz", + "integrity": "sha512-SiOKHbVjTSf6wHuGCbqrEyzlm6qvXcv7mENP+OZon1I07brfZLGdfWV0l/efAzVx7TF3Z45ov1gPEkku9q25YQ==", "dev": true, "dependencies": { - "playwright-core": "1.42.1" + "playwright-core": "1.43.0" }, "bin": { "playwright": "cli.js" @@ -9550,9 +9647,9 @@ } }, "node_modules/playwright-core": { - "version": "1.42.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.42.1.tgz", - "integrity": "sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==", + "version": "1.43.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.43.0.tgz", + "integrity": "sha512-iWFjyBUH97+pUFiyTqSLd8cDMMOS0r2ZYz2qEsPjH8/bX++sbIJT35MSwKnp1r/OQBAqC5XO99xFbJ9XClhf4w==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -9768,9 +9865,9 @@ } }, "node_modules/postcss-nesting": { - "version": "12.1.1", - "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.1.tgz", - "integrity": "sha512-qc74KvIAQNa5ujZKG1UV286dhaDW6basbUy2i9AzNU/T8C9hpvGu9NZzm1SfePe2yP7sPYgpA8d4sPVopn2Hhw==", + "version": "12.1.2", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-12.1.2.tgz", + "integrity": "sha512-FUmTHGDNundodutB4PUBxt/EPuhgtpk8FJGRsBhOuy+6FnkR2A8RZWIsyyy6XmhvX2DZQQWIkvu+HB4IbJm+Ew==", "funding": [ { "type": "github", @@ -11034,20 +11131,20 @@ "dev": true }, "node_modules/stylelint": { - "version": "16.3.1", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.3.1.tgz", - "integrity": "sha512-/JOwQnBvxEKOT2RtNgGpBVXnCSMBgKOL2k7w0K52htwCyJls4+cHvc4YZgXlVoAZS9QJd2DgYAiRnja96pTgxw==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-16.4.0.tgz", + "integrity": "sha512-uSx7VMuXwLuYcNSIg+0/fFNv0WinsfLAqsVVy7h7p80clKOHiGE8pfY6UjqwylTHiJrRIahTl6a8FPxGezhWoA==", "dev": true, "dependencies": { "@csstools/css-parser-algorithms": "^2.6.1", "@csstools/css-tokenizer": "^2.2.4", "@csstools/media-query-list-parser": "^2.1.9", - "@csstools/selector-specificity": "^3.0.2", + "@csstools/selector-specificity": "^3.0.3", "@dual-bundle/import-meta-resolve": "^4.0.0", "balanced-match": "^2.0.0", "colord": "^2.9.3", "cosmiconfig": "^9.0.0", - "css-functions-list": "^3.2.1", + "css-functions-list": "^3.2.2", "css-tree": "^2.3.1", "debug": "^4.3.4", "fast-glob": "^3.3.2", @@ -11076,7 +11173,7 @@ "strip-ansi": "^7.1.0", "supports-hyperlinks": "^3.0.0", "svg-tags": "^1.0.0", - "table": "^6.8.1", + "table": "^6.8.2", "write-file-atomic": "^5.0.1" }, "bin": { @@ -11398,9 +11495,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.13.0.tgz", - "integrity": "sha512-uaWhh6j18IIs5tOX0arvIBnVINAzpTXaQXkr7qAk8zoupegJVg0UU/5+S/FgsgVCnzVsJ9d7QLjIxkswEeTg0Q==" + "version": "5.17.2", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.17.2.tgz", + "integrity": "sha512-V/NqUw6QoTrjSpctp2oLQvxrl3vW29UsUtZyq7B1CF0v870KOFbYGDQw8rpKaKm0JxTwHpWnW1SN9YuKZdiCyw==" }, "node_modules/sync-fetch": { "version": "0.4.5", @@ -12160,9 +12257,9 @@ } }, "node_modules/vite-node": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.4.0.tgz", - "integrity": "sha512-VZDAseqjrHgNd4Kh8icYHWzTKSCZMhia7GyHfhtzLW33fZlG9SwsB6CEhgyVOWkJfJ2pFLrp/Gj1FSfAiqH9Lw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-1.5.2.tgz", + "integrity": "sha512-Y8p91kz9zU+bWtF7HGt6DVw2JbhyuB2RlZix3FPYAYmUyZ3n7iTp8eSyLyY6sxtPegvxQtmlTMhfPhUfCUF93A==", "dev": true, "dependencies": { "cac": "^6.7.14", @@ -12182,9 +12279,9 @@ } }, "node_modules/vite-string-plugin": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.5.tgz", - "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.2.0.tgz", + "integrity": "sha512-IijlLgTxUDUwOpLoBLZCZO2us4fZWPRpj8XWoD9OAYjjUEge8enV4gaDTOs7uEsC8EJ9+NmusdLwmgWajFO45Q==", "dev": true }, "node_modules/vite/node_modules/@types/estree": { @@ -12242,16 +12339,16 @@ } }, "node_modules/vitest": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.4.0.tgz", - "integrity": "sha512-gujzn0g7fmwf83/WzrDTnncZt2UiXP41mHuFYFrdwaLRVQ6JYQEiME2IfEjU3vcFL3VKa75XhI3lFgn+hfVsQw==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-1.5.2.tgz", + "integrity": "sha512-l9gwIkq16ug3xY7BxHwcBQovLZG75zZL0PlsiYQbf76Rz6QGs54416UWMtC0jXeihvHvcHrf2ROEjkQRVpoZYw==", "dev": true, "dependencies": { - "@vitest/expect": "1.4.0", - "@vitest/runner": "1.4.0", - "@vitest/snapshot": "1.4.0", - "@vitest/spy": "1.4.0", - "@vitest/utils": "1.4.0", + "@vitest/expect": "1.5.2", + "@vitest/runner": "1.5.2", + "@vitest/snapshot": "1.5.2", + "@vitest/spy": "1.5.2", + "@vitest/utils": "1.5.2", "acorn-walk": "^8.3.2", "chai": "^4.3.10", "debug": "^4.3.4", @@ -12263,9 +12360,9 @@ "std-env": "^3.5.0", "strip-literal": "^2.0.0", "tinybench": "^2.5.1", - "tinypool": "^0.8.2", + "tinypool": "^0.8.3", "vite": "^5.0.0", - "vite-node": "1.4.0", + "vite-node": "1.5.2", "why-is-node-running": "^2.2.2" }, "bin": { @@ -12280,8 +12377,8 @@ "peerDependencies": { "@edge-runtime/vm": "*", "@types/node": "^18.0.0 || >=20.0.0", - "@vitest/browser": "1.4.0", - "@vitest/ui": "1.4.0", + "@vitest/browser": "1.5.2", + "@vitest/ui": "1.5.2", "happy-dom": "*", "jsdom": "*" }, @@ -12307,27 +12404,24 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.9", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.9.tgz", - "integrity": "sha512-S1+hd+dIrC8EZqKyT9DstTH/0Z+f76kmmvZnkfQVmOpDEF9iVgdYif3Q/pIWHmCoo59bQVGW0kVL3e2nl+9+Sw==", + "version": "0.30.10", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.10.tgz", + "integrity": "sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" - }, - "engines": { - "node": ">=12" } }, "node_modules/vue": { - "version": "3.4.21", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.21.tgz", - "integrity": "sha512-5hjyV/jLEIKD/jYl4cavMcnzKwjMKohureP8ejn3hhEjwhWIhWeuzL2kJAjzl/WyVsgPY56Sy4Z40C3lVshxXA==", + "version": "3.4.24", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.24.tgz", + "integrity": "sha512-NPdx7dLGyHmKHGRRU5bMRYVE+rechR+KDU5R2tSTNG36PuMwbfAJ+amEvOAw7BPfZp5sQulNELSLm5YUkau+Sg==", "dependencies": { - "@vue/compiler-dom": "3.4.21", - "@vue/compiler-sfc": "3.4.21", - "@vue/runtime-dom": "3.4.21", - "@vue/server-renderer": "3.4.21", - "@vue/shared": "3.4.21" + "@vue/compiler-dom": "3.4.24", + "@vue/compiler-sfc": "3.4.24", + "@vue/runtime-dom": "3.4.24", + "@vue/server-renderer": "3.4.24", + "@vue/shared": "3.4.24" }, "peerDependencies": { "typescript": "*" @@ -12348,9 +12442,9 @@ } }, "node_modules/vue-chartjs": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.0.tgz", - "integrity": "sha512-8XqX0JU8vFZ+WA2/knz4z3ThClduni2Nm0BMe2u0mXgTfd9pXrmJ07QBI+WAij5P/aPmPMX54HCE1seWL37ZdQ==", + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.1.tgz", + "integrity": "sha512-rZjqcHBxKiHrBl0CIvcOlVEBwRhpWAVf6rDU3vUfa7HuSRmGtCslc0Oc8m16oAVuk0erzc1FCtH1VCriHsrz+A==", "peerDependencies": { "chart.js": "^4.1.1", "vue": "^3.0.0-0 || ^2.7.0" diff --git a/package.json b/package.json index 1723216f51..5a95503a58 100644 --- a/package.json +++ b/package.json @@ -4,9 +4,9 @@ "node": ">= 18.0.0" }, "dependencies": { - "@citation-js/core": "0.7.9", - "@citation-js/plugin-bibtex": "0.7.9", - "@citation-js/plugin-csl": "0.7.9", + "@citation-js/core": "0.7.11", + "@citation-js/plugin-bibtex": "0.7.11", + "@citation-js/plugin-csl": "0.7.11", "@citation-js/plugin-software-formats": "0.6.1", "@github/markdown-toolbar-element": "2.2.3", "@github/relative-time-element": "4.4.0", @@ -33,17 +33,17 @@ "katex": "0.16.10", "license-checker-webpack-plugin": "0.2.1", "mermaid": "10.9.0", - "mini-css-extract-plugin": "2.8.1", + "mini-css-extract-plugin": "2.9.0", "minimatch": "9.0.4", "monaco-editor": "0.47.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.3.0", "postcss": "8.4.38", "postcss-loader": "8.1.1", - "postcss-nesting": "12.1.1", + "postcss-nesting": "12.1.2", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.13.0", + "swagger-ui-dist": "5.17.2", "tailwindcss": "3.4.3", "temporal-polyfill": "0.2.4", "throttle-debounce": "5.0.0", @@ -53,9 +53,9 @@ "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", "vanilla-colorful": "0.7.2", - "vue": "3.4.21", + "vue": "3.4.24", "vue-bar-graph": "2.0.0", - "vue-chartjs": "5.3.0", + "vue-chartjs": "5.3.1", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", "webpack": "5.91.0", @@ -63,10 +63,10 @@ "wrap-ansi": "9.0.0" }, "devDependencies": { - "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.42.1", + "@eslint-community/eslint-plugin-eslint-comments": "4.3.0", + "@playwright/test": "1.43.0", "@stoplight/spectral-cli": "6.11.1", - "@stylistic/eslint-plugin-js": "1.7.0", + "@stylistic/eslint-plugin-js": "1.7.2", "@stylistic/stylelint-plugin": "2.1.1", "@vitejs/plugin-vue": "5.0.4", "@vue/test-utils": "2.4.5", @@ -77,25 +77,25 @@ "eslint-plugin-jquery": "1.5.1", "eslint-plugin-no-jquery": "2.7.0", "eslint-plugin-no-use-extend-native": "0.5.0", - "eslint-plugin-regexp": "2.4.0", + "eslint-plugin-regexp": "2.5.0", "eslint-plugin-sonarjs": "0.25.1", "eslint-plugin-unicorn": "52.0.0", - "eslint-plugin-vitest": "0.4.1", + "eslint-plugin-vitest": "0.5.4", "eslint-plugin-vitest-globals": "1.5.0", - "eslint-plugin-vue": "9.24.0", + "eslint-plugin-vue": "9.25.0", "eslint-plugin-vue-scoped-css": "2.8.0", - "eslint-plugin-wc": "2.0.4", - "happy-dom": "14.5.0", + "eslint-plugin-wc": "2.1.0", + "happy-dom": "14.7.1", "markdownlint-cli": "0.39.0", "postcss-html": "1.6.0", - "stylelint": "16.3.1", + "stylelint": "16.4.0", "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", "stylelint-value-no-unknown-custom-properties": "6.0.1", "svgo": "3.2.0", "updates": "16.0.1", - "vite-string-plugin": "1.1.5", - "vitest": "1.4.0" + "vite-string-plugin": "1.2.0", + "vitest": "1.5.2" }, "browserslist": ["defaults"] } diff --git a/release-notes/8.0.0/fix/3399.md b/release-notes/8.0.0/fix/3399.md new file mode 100644 index 0000000000..d25d66d5f0 --- /dev/null +++ b/release-notes/8.0.0/fix/3399.md @@ -0,0 +1 @@ +The regression in the [`fogejo admin user create`](https://forgejo.org/docs/v7.0/admin/command-line/#admin-user-create) CLI command [is fixed](https://codeberg.org/forgejo/forgejo/issues/3399) and it is backward compatible. diff --git a/release-notes/8.0.0/fix/3430.md b/release-notes/8.0.0/fix/3430.md new file mode 100644 index 0000000000..17d91653e9 --- /dev/null +++ b/release-notes/8.0.0/fix/3430.md @@ -0,0 +1 @@ +Fixed a bug where the `/api/v1/repos/{owner}/{repo}/wiki` API endpoints were using a hardcoded "master" branch for the wiki, rather than the branch they really use. diff --git a/release-notes/8.0.0/fix/3442.md b/release-notes/8.0.0/fix/3442.md new file mode 100644 index 0000000000..7c4feafb68 --- /dev/null +++ b/release-notes/8.0.0/fix/3442.md @@ -0,0 +1 @@ +Save updated empty comments instead of skipping the update silently, [which prevented the removal of attachments of such comments](https://codeberg.org/forgejo/forgejo/issues/3424). diff --git a/release-notes/8.0.0/fix/3444.md b/release-notes/8.0.0/fix/3444.md new file mode 100644 index 0000000000..4988fdad15 --- /dev/null +++ b/release-notes/8.0.0/fix/3444.md @@ -0,0 +1 @@ +Fixed bleve indexer failing when [fuzziness exceeds the maximum 2](https://codeberg.org/forgejo/forgejo/pulls/3444) diff --git a/release-notes/8.0.0/fix/3451.md b/release-notes/8.0.0/fix/3451.md new file mode 100644 index 0000000000..e0c307e896 --- /dev/null +++ b/release-notes/8.0.0/fix/3451.md @@ -0,0 +1 @@ +Fixed an error 500 when visiting [the LFS settings](https://codeberg.org/forgejo/forgejo/pulls/3451) at `/{owner}/{repo}/settings/lfs/find?oid=...`. diff --git a/renovate.json b/renovate.json index 0be6ebaa37..74432c9dca 100644 --- a/renovate.json +++ b/renovate.json @@ -15,23 +15,45 @@ "helpers:pinGitHubActionDigests" ], "semanticCommits": "disabled", + "automergeStrategy": "merge-commit", "postUpdateOptions": ["gomodTidy", "gomodUpdateImportPaths", "npmDedupe"], "prConcurrentLimit": 5, + "internalChecksFilter": "strict", "packageRules": [ { "description": "Require approval for go and python minor version", - "matchDepNames": ["go", "python", "golang", "docker.io/golang", "docker.io/library/golang"], + "matchDepNames": [ + "go", + "python", + "golang", + "docker.io/golang", + "docker.io/library/golang" + ], "matchUpdateTypes": ["minor"], "dependencyDashboardApproval": true }, { "description": "Require dashboard approval for some deps", - "matchDepNames": ["github.com/go-ap/activitypub"], + "matchDepNames": [ + "bitnami/minio", + "github.com/go-ap/activitypub", + "github.com/nektos/act" + ], "dependencyDashboardApproval": true }, + { + "description": "Schedule some deps less frequently", + "matchDepNames": ["github.com/google/pprof"], + "extends": ["schedule:quarterly"] + }, { "description": "Group golang packages", - "matchDepNames": ["go", "golang", "docker.io/golang", "docker.io/library/golang"], + "matchDepNames": [ + "go", + "golang", + "docker.io/golang", + "docker.io/library/golang" + ], "groupName": "golang packages" }, { @@ -46,6 +68,21 @@ "matchUpdateTypes": ["minor", "patch", "digest"], "automerge": true }, + { + "description": "Split minor and patch updates", + "matchDepNames": [ + "swagger-ui-dist" + ], + "separateMinorPatch": true + }, + { + "description": "Automerge patch updates", + "matchDepNames": [ + "swagger-ui-dist" + ], + "matchUpdateTypes": ["patch"], + "automerge": true + }, { "description": "Update renovate with higher prio to come through rate limit", "matchDatasources": ["docker"], @@ -57,6 +94,17 @@ "matchDepNames": ["actions/cascading-pr"], "matchManagers": ["github-actions"], "enabled": false + }, + { + "description": "Automerge some packages when ci succeeds", + "extends": ["packages:linters"], + "matchDepNames": ["vitest"], + "automerge": true + }, + { + "description": "Hold back on some package updates for a few days", + "matchDepNames": ["monaco-editor"], + "minimumReleaseAge": "30 days" } ], "customManagers": [ @@ -76,6 +124,14 @@ "matchStrings": ["\\s+node-version: ['\"]?(?.+?)['\"]?\\s"], "depNameTemplate": "node", "datasourceTemplate": "node-version" + }, + { + "description": "Update deps inside Makefile", + "customType": "regex", + "fileMatch": ["^Makefile$"], + "matchStrings": [ + " \\?= (?.+?)@(?.+?) # renovate: datasource=(?.+?)\\s" + ] } ] } diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index caaad2b83b..1e1ed69011 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -9,6 +9,8 @@ import ( "net/http" actions_model "code.gitea.io/gitea/models/actions" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/util" @@ -54,6 +56,18 @@ func (s *Service) Register( return nil, errors.New("runner registration token has been invalidated, please use the latest one") } + if runnerToken.OwnerID > 0 { + if _, err := user_model.GetUserByID(ctx, runnerToken.OwnerID); err != nil { + return nil, errors.New("owner of the token not found") + } + } + + if runnerToken.RepoID > 0 { + if _, err := repo_model.GetRepositoryByID(ctx, runnerToken.RepoID); err != nil { + return nil, errors.New("repository of the token not found") + } + } + labels := req.Msg.Labels // TODO: agent_labels should be removed from pb after Gitea 1.20 released. // Old version runner's agent_labels slice is not empty and labels slice is empty. diff --git a/routers/api/v1/admin/hooks.go b/routers/api/v1/admin/hooks.go index 4c168b55bf..b246cb61b1 100644 --- a/routers/api/v1/admin/hooks.go +++ b/routers/api/v1/admin/hooks.go @@ -8,7 +8,6 @@ import ( "net/http" "code.gitea.io/gitea/models/webhook" - "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -38,7 +37,7 @@ func ListHooks(ctx *context.APIContext) { // "200": // "$ref": "#/responses/HookList" - sysHooks, err := webhook.GetSystemWebhooks(ctx, optional.None[bool]()) + sysHooks, err := webhook.GetSystemWebhooks(ctx, false) if err != nil { ctx.Error(http.StatusInternalServerError, "GetSystemWebhooks", err) return diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 12da8a9597..9ea210ee4e 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -30,7 +30,7 @@ import ( user_service "code.gitea.io/gitea/services/user" ) -func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64, loginName string) { +func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64) { if sourceID == 0 { return } @@ -47,7 +47,6 @@ func parseAuthSource(ctx *context.APIContext, u *user_model.User, sourceID int64 u.LoginType = source.Type u.LoginSource = source.ID - u.LoginName = loginName } // CreateUser create a user @@ -83,12 +82,13 @@ func CreateUser(ctx *context.APIContext) { Passwd: form.Password, MustChangePassword: true, LoginType: auth.Plain, + LoginName: form.LoginName, } if form.MustChangePassword != nil { u.MustChangePassword = *form.MustChangePassword } - parseAuthSource(ctx, u, form.SourceID, form.LoginName) + parseAuthSource(ctx, u, form.SourceID) if ctx.Written() { return } @@ -192,9 +192,17 @@ func EditUser(ctx *context.APIContext) { form := web.GetForm(ctx).(*api.EditUserOption) + // If either LoginSource or LoginName is given, the other must be present too. + if form.SourceID != nil || form.LoginName != nil { + if form.SourceID == nil || form.LoginName == nil { + ctx.Error(http.StatusUnprocessableEntity, "LoginSourceAndLoginName", fmt.Errorf("source_id and login_name must be specified together")) + return + } + } + authOpts := &user_service.UpdateAuthOptions{ - LoginSource: optional.FromNonDefault(form.SourceID), - LoginName: optional.Some(form.LoginName), + LoginSource: optional.FromPtr(form.SourceID), + LoginName: optional.FromPtr(form.LoginName), Password: optional.FromNonDefault(form.Password), MustChangePassword: optional.FromPtr(form.MustChangePassword), ProhibitLogin: optional.FromPtr(form.ProhibitLogin), diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 25311aa7a6..62d2d18621 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -996,6 +996,8 @@ func Routes() *web.Route { m.Post("/migrate", reqToken(), bind(api.MigrateRepoOptions{}), repo.Migrate) m.Group("/{username}/{reponame}", func() { + m.Get("/compare/*", reqRepoReader(unit.TypeCode), repo.CompareDiff) + m.Combo("").Get(reqAnyRepoReader(), repo.Get). Delete(reqToken(), reqOwner(), repo.Delete). Patch(reqToken(), reqAdmin(), bind(api.EditRepoOption{}), repo.Edit) diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index c33beee0ae..852b7a2ee0 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -437,7 +437,7 @@ func GetBranchProtection(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp)) + ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp, repo)) } // ListBranchProtections list branch protections for a repo @@ -470,7 +470,7 @@ func ListBranchProtections(ctx *context.APIContext) { } apiBps := make([]*api.BranchProtection, len(bps)) for i := range bps { - apiBps[i] = convert.ToBranchProtection(ctx, bps[i]) + apiBps[i] = convert.ToBranchProtection(ctx, bps[i], repo) } ctx.JSON(http.StatusOK, apiBps) @@ -682,7 +682,7 @@ func CreateBranchProtection(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp)) + ctx.JSON(http.StatusCreated, convert.ToBranchProtection(ctx, bp, repo)) } // EditBranchProtection edits a branch protection for a repo @@ -964,7 +964,7 @@ func EditBranchProtection(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp)) + ctx.JSON(http.StatusOK, convert.ToBranchProtection(ctx, bp, repo)) } // DeleteBranchProtection deletes a branch protection for a repo diff --git a/routers/api/v1/repo/compare.go b/routers/api/v1/repo/compare.go new file mode 100644 index 0000000000..549b9b7fa9 --- /dev/null +++ b/routers/api/v1/repo/compare.go @@ -0,0 +1,99 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "net/http" + "strings" + + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/gitrepo" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/services/context" + "code.gitea.io/gitea/services/convert" +) + +// CompareDiff compare two branches or commits +func CompareDiff(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/compare/{basehead} Get commit comparison information + // --- + // summary: Get commit comparison information + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: basehead + // in: path + // description: compare two branches or commits + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/Compare" + // "404": + // "$ref": "#/responses/notFound" + + if ctx.Repo.GitRepo == nil { + gitRepo, err := gitrepo.OpenRepository(ctx, ctx.Repo.Repository) + if err != nil { + ctx.Error(http.StatusInternalServerError, "OpenRepository", err) + return + } + ctx.Repo.GitRepo = gitRepo + defer gitRepo.Close() + } + + infoPath := ctx.Params("*") + infos := []string{ctx.Repo.Repository.DefaultBranch, ctx.Repo.Repository.DefaultBranch} + if infoPath != "" { + infos = strings.SplitN(infoPath, "...", 2) + if len(infos) != 2 { + if infos = strings.SplitN(infoPath, "..", 2); len(infos) != 2 { + infos = []string{ctx.Repo.Repository.DefaultBranch, infoPath} + } + } + } + + _, _, headGitRepo, ci, _, _ := parseCompareInfo(ctx, api.CreatePullRequestOption{ + Base: infos[0], + Head: infos[1], + }) + if ctx.Written() { + return + } + defer headGitRepo.Close() + + verification := ctx.FormString("verification") == "" || ctx.FormBool("verification") + files := ctx.FormString("files") == "" || ctx.FormBool("files") + + apiCommits := make([]*api.Commit, 0, len(ci.Commits)) + userCache := make(map[string]*user_model.User) + for i := 0; i < len(ci.Commits); i++ { + apiCommit, err := convert.ToCommit(ctx, ctx.Repo.Repository, ctx.Repo.GitRepo, ci.Commits[i], userCache, + convert.ToCommitOptions{ + Stat: true, + Verification: verification, + Files: files, + }) + if err != nil { + ctx.ServerError("toCommit", err) + return + } + apiCommits = append(apiCommits, apiCommit) + } + + ctx.JSON(http.StatusOK, &api.Compare{ + TotalCommits: len(ci.Commits), + Commits: apiCommits, + }) +} diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index 2640819a54..0d304dd66d 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -311,7 +311,7 @@ func SearchIssues(ctx *context.APIContext) { ctx.SetLinkHeader(int(total), limit) ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } // ListIssues list the issues of a repository @@ -548,7 +548,7 @@ func ListIssues(ctx *context.APIContext) { ctx.SetLinkHeader(int(total), listOptions.PageSize) ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } func getUserIDForFilter(ctx *context.APIContext, queryName string) int64 { @@ -614,7 +614,7 @@ func GetIssue(ctx *context.APIContext) { ctx.NotFound() return } - ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue)) } // CreateIssue create an issue of a repository @@ -737,7 +737,7 @@ func CreateIssue(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "GetIssueByID", err) return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue)) } // EditIssue modify an issue of a repository @@ -913,7 +913,7 @@ func EditIssue(ctx *context.APIContext) { ctx.InternalServerError(err) return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, issue)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, issue)) } func DeleteIssue(ctx *context.APIContext) { diff --git a/routers/api/v1/repo/issue_attachment.go b/routers/api/v1/repo/issue_attachment.go index fc9e88d63d..6f3e4b5e77 100644 --- a/routers/api/v1/repo/issue_attachment.go +++ b/routers/api/v1/repo/issue_attachment.go @@ -108,7 +108,7 @@ func ListIssueAttachments(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, issue).Attachments) + ctx.JSON(http.StatusOK, convert.ToAPIIssue(ctx, ctx.Doer, issue).Attachments) } // CreateIssueAttachment creates an attachment and saves the given file diff --git a/routers/api/v1/repo/issue_dependency.go b/routers/api/v1/repo/issue_dependency.go index a42920d4fd..c40e92c01b 100644 --- a/routers/api/v1/repo/issue_dependency.go +++ b/routers/api/v1/repo/issue_dependency.go @@ -153,7 +153,7 @@ func GetIssueDependencies(ctx *context.APIContext) { blockerIssues = append(blockerIssues, &blocker.Issue) } - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, blockerIssues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, blockerIssues)) } // CreateIssueDependency create a new issue dependencies @@ -214,7 +214,7 @@ func CreateIssueDependency(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target)) } // RemoveIssueDependency remove an issue dependency @@ -275,7 +275,7 @@ func RemoveIssueDependency(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, target)) } // GetIssueBlocks list issues that are blocked by this issue @@ -381,7 +381,7 @@ func GetIssueBlocks(ctx *context.APIContext) { issues = append(issues, &depMeta.Issue) } - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } // CreateIssueBlocking block the issue given in the body by the issue in path @@ -438,7 +438,7 @@ func CreateIssueBlocking(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency)) } // RemoveIssueBlocking unblock the issue given in the body by the issue in path @@ -495,7 +495,7 @@ func RemoveIssueBlocking(ctx *context.APIContext) { return } - ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency)) + ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, ctx.Doer, dependency)) } func getParamsIssue(ctx *context.APIContext) *issues_model.Issue { diff --git a/routers/api/v1/repo/issue_pin.go b/routers/api/v1/repo/issue_pin.go index 8fcf670fd0..af3e06332a 100644 --- a/routers/api/v1/repo/issue_pin.go +++ b/routers/api/v1/repo/issue_pin.go @@ -207,7 +207,7 @@ func ListPinnedIssues(ctx *context.APIContext) { return } - ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, ctx.Doer, issues)) } // ListPinnedPullRequests returns a list of all pinned PRs diff --git a/routers/api/v1/repo/issue_tracked_time.go b/routers/api/v1/repo/issue_tracked_time.go index c640515881..f83855efac 100644 --- a/routers/api/v1/repo/issue_tracked_time.go +++ b/routers/api/v1/repo/issue_tracked_time.go @@ -138,7 +138,7 @@ func ListTrackedTimes(ctx *context.APIContext) { } ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } // AddTime add time manual to the given issue @@ -225,7 +225,7 @@ func AddTime(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, trackedTime)) + ctx.JSON(http.StatusOK, convert.ToTrackedTime(ctx, user, trackedTime)) } // ResetIssueTime reset time manual to the given issue @@ -455,7 +455,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) { ctx.Error(http.StatusInternalServerError, "LoadAttributes", err) return } - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } // ListTrackedTimesByRepository lists all tracked times of the repository @@ -567,7 +567,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) { } ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } // ListMyTrackedTimes lists all tracked times of the current user @@ -629,5 +629,5 @@ func ListMyTrackedTimes(ctx *context.APIContext) { } ctx.SetTotalCountHeader(count) - ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, trackedTimes)) + ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(ctx, ctx.Doer, trackedTimes)) } diff --git a/routers/api/v1/repo/release.go b/routers/api/v1/repo/release.go index f0f3c0bbc7..057282b210 100644 --- a/routers/api/v1/repo/release.go +++ b/routers/api/v1/repo/release.go @@ -231,17 +231,18 @@ func CreateRelease(ctx *context.APIContext) { form.Target = ctx.Repo.Repository.DefaultBranch } rel = &repo_model.Release{ - RepoID: ctx.Repo.Repository.ID, - PublisherID: ctx.Doer.ID, - Publisher: ctx.Doer, - TagName: form.TagName, - Target: form.Target, - Title: form.Title, - Note: form.Note, - IsDraft: form.IsDraft, - IsPrerelease: form.IsPrerelease, - IsTag: false, - Repo: ctx.Repo.Repository, + RepoID: ctx.Repo.Repository.ID, + PublisherID: ctx.Doer.ID, + Publisher: ctx.Doer, + TagName: form.TagName, + Target: form.Target, + Title: form.Title, + Note: form.Note, + IsDraft: form.IsDraft, + IsPrerelease: form.IsPrerelease, + HideArchiveLinks: form.HideArchiveLinks, + IsTag: false, + Repo: ctx.Repo.Repository, } if err := release_service.CreateRelease(ctx.Repo.GitRepo, rel, nil, ""); err != nil { if repo_model.IsErrReleaseAlreadyExist(err) { @@ -261,13 +262,14 @@ func CreateRelease(ctx *context.APIContext) { rel.Note = form.Note rel.IsDraft = form.IsDraft rel.IsPrerelease = form.IsPrerelease + rel.HideArchiveLinks = form.HideArchiveLinks rel.PublisherID = ctx.Doer.ID rel.IsTag = false rel.Repo = ctx.Repo.Repository rel.Publisher = ctx.Doer rel.Target = form.Target - if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil { + if err = release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil, true); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) return } @@ -341,7 +343,10 @@ func EditRelease(ctx *context.APIContext) { if form.IsPrerelease != nil { rel.IsPrerelease = *form.IsPrerelease } - if err := release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil); err != nil { + if form.HideArchiveLinks != nil { + rel.HideArchiveLinks = *form.HideArchiveLinks + } + if err := release_service.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, nil, nil, nil, false); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRelease", err) return } diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 562c3eb64c..6692633fab 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -845,6 +845,15 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { newHasWiki = *opts.HasWiki } if currHasWiki || newHasWiki { + wikiPermissions := repo.MustGetUnit(ctx, unit_model.TypeWiki).DefaultPermissions + if opts.GloballyEditableWiki != nil { + if *opts.GloballyEditableWiki { + wikiPermissions = repo_model.UnitAccessModeWrite + } else { + wikiPermissions = repo_model.UnitAccessModeRead + } + } + if newHasWiki && opts.ExternalWiki != nil && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { // Check that values are valid if !validation.IsValidExternalURL(opts.ExternalWiki.ExternalWikiURL) { @@ -864,9 +873,10 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { } else if newHasWiki && opts.ExternalWiki == nil && !unit_model.TypeWiki.UnitGlobalDisabled() { config := &repo_model.UnitConfig{} units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeWiki, - Config: config, + RepoID: repo.ID, + Type: unit_model.TypeWiki, + Config: config, + DefaultPermissions: wikiPermissions, }) deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) } else if !newHasWiki { @@ -876,6 +886,14 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if !unit_model.TypeWiki.UnitGlobalDisabled() { deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) } + } else if *opts.GloballyEditableWiki { + config := &repo_model.UnitConfig{} + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeWiki, + Config: config, + DefaultPermissions: wikiPermissions, + }) } } diff --git a/routers/api/v1/repo/wiki.go b/routers/api/v1/repo/wiki.go index f18ea087c4..b6f51cdadc 100644 --- a/routers/api/v1/repo/wiki.go +++ b/routers/api/v1/repo/wiki.go @@ -193,7 +193,7 @@ func getWikiPage(ctx *context.APIContext, wikiName wiki_service.WebPath) *api.Wi } // get commit count - wiki revisions - commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) + commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.GetWikiBranchName(), pageFilename) // Get last change information. lastCommit, err := wikiRepo.GetCommitByPath(pageFilename) @@ -432,7 +432,7 @@ func ListPageRevisions(ctx *context.APIContext) { } // get commit count - wiki revisions - commitsCount, _ := wikiRepo.FileCommitsCount("master", pageFilename) + commitsCount, _ := wikiRepo.FileCommitsCount(ctx.Repo.Repository.GetWikiBranchName(), pageFilename) page := ctx.FormInt("page") if page <= 1 { @@ -442,7 +442,7 @@ func ListPageRevisions(ctx *context.APIContext) { // get Commit Count commitsHistory, err := wikiRepo.CommitsByFileAndRange( git.CommitsByFileAndRangeOptions{ - Revision: "master", + Revision: ctx.Repo.Repository.GetWikiBranchName(), File: pageFilename, Page: page, }) @@ -487,7 +487,7 @@ func findWikiRepoCommit(ctx *context.APIContext) (*git.Repository, *git.Commit) return nil, nil } - commit, err := wikiRepo.GetBranchCommit("master") + commit, err := wikiRepo.GetBranchCommit(ctx.Repo.Repository.GetWikiBranchName()) if err != nil { if git.IsErrNotExist(err) { ctx.NotFound(err) diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index c7fa98a697..b55ea1d0a9 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -421,3 +421,9 @@ type swaggerBlockedUserList struct { // in:body Body []api.BlockedUser `json:"body"` } + +// swagger:response Compare +type swaggerCompare struct { + // in:body + Body api.Compare `json:"body"` +} diff --git a/routers/common/compare.go b/routers/common/compare.go new file mode 100644 index 0000000000..4d1cc2f0d8 --- /dev/null +++ b/routers/common/compare.go @@ -0,0 +1,21 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package common + +import ( + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" +) + +// CompareInfo represents the collected results from ParseCompareInfo +type CompareInfo struct { + HeadUser *user_model.User + HeadRepo *repo_model.Repository + HeadGitRepo *git.Repository + CompareInfo *git.CompareInfo + BaseBranch string + HeadBranch string + DirectComparison bool +} diff --git a/routers/init.go b/routers/init.go index 28e1c49801..821a0ef38c 100644 --- a/routers/init.go +++ b/routers/init.go @@ -132,7 +132,7 @@ func InitWebInstalled(ctx context.Context) { if setting.EnableSQLite3 { log.Info("SQLite3 support is enabled") } else if setting.Database.Type.IsSQLite3() { - log.Fatal("SQLite3 support is disabled, but it is used for database setting. Please get or build a Gitea release with SQLite3 support.") + log.Fatal("SQLite3 support is disabled, but it is used for database setting. Please get or build a Forgejo release with SQLite3 support.") } mustInitCtx(ctx, common.InitDBEngine) diff --git a/routers/install/install.go b/routers/install/install.go index 5030306d89..282ebe9ead 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -159,6 +159,7 @@ func Install(ctx *context.Context) { form.DefaultAllowCreateOrganization = setting.Service.DefaultAllowCreateOrganization form.DefaultEnableTimetracking = setting.Service.DefaultEnableTimetracking form.NoReplyAddress = setting.Service.NoReplyAddress + form.EnableUpdateChecker = true form.PasswordAlgorithm = hash.ConfigHashAlgorithm(setting.PasswordHashAlgo) middleware.AssignForm(form, ctx.Data) @@ -211,7 +212,7 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool { } if hasPostInstallationUser && dbMigrationVersion > 0 { - log.Error("The database is likely to have been used by Gitea before, database migration version=%d", dbMigrationVersion) + log.Error("The database is likely to have been used by Forgejo before, database migration version=%d", dbMigrationVersion) confirmed := form.ReinstallConfirmFirst && form.ReinstallConfirmSecond && form.ReinstallConfirmThird if !confirmed { ctx.Data["Err_DbInstalledBefore"] = true @@ -219,11 +220,11 @@ func checkDatabase(ctx *context.Context, form *forms.InstallForm) bool { return false } - log.Info("User confirmed re-installation of Gitea into a pre-existing database") + log.Info("User confirmed re-installation of Forgejo into a pre-existing database") } if hasPostInstallationUser || dbMigrationVersion > 0 { - log.Info("Gitea will be installed in a database with: hasPostInstallationUser=%v, dbMigrationVersion=%v", hasPostInstallationUser, dbMigrationVersion) + log.Info("Forgejo will be installed in a database with: hasPostInstallationUser=%v, dbMigrationVersion=%v", hasPostInstallationUser, dbMigrationVersion) } return true diff --git a/routers/private/actions.go b/routers/private/actions.go index 397f20a091..425c480b3e 100644 --- a/routers/private/actions.go +++ b/routers/private/actions.go @@ -27,7 +27,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) { defer rd.Close() if err := json.NewDecoder(rd).Decode(&genRequest); err != nil { - log.Error("%v", err) + log.Error("JSON Decode failed: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) @@ -36,7 +36,7 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) { owner, repo, err := parseScope(ctx, genRequest.Scope) if err != nil { - log.Error("%v", err) + log.Error("parseScope failed: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) @@ -46,18 +46,18 @@ func GenerateActionsRunnerToken(ctx *context.PrivateContext) { if errors.Is(err, util.ErrNotExist) || (token != nil && !token.IsActive) { token, err = actions_model.NewRunnerToken(ctx, owner, repo) if err != nil { - err := fmt.Sprintf("error while creating runner token: %v", err) - log.Error("%v", err) + errMsg := fmt.Sprintf("error while creating runner token: %v", err) + log.Error("NewRunnerToken failed: %v", errMsg) ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: err, + Err: errMsg, }) return } } else if err != nil { - err := fmt.Sprintf("could not get unactivated runner token: %v", err) - log.Error("%v", err) + errMsg := fmt.Sprintf("could not get unactivated runner token: %v", err) + log.Error("GetLatestRunnerToken failed: %v", errMsg) ctx.JSON(http.StatusInternalServerError, private.Response{ - Err: err, + Err: errMsg, }) return } diff --git a/routers/private/hook_post_receive.go b/routers/private/hook_post_receive.go index fff47caa88..2558ffe1ab 100644 --- a/routers/private/hook_post_receive.go +++ b/routers/private/hook_post_receive.go @@ -7,6 +7,7 @@ import ( "fmt" "net/http" "strconv" + "time" git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" @@ -71,6 +72,7 @@ func HookPostReceive(ctx *gitea_context.PrivateContext) { PusherName: opts.UserName, RepoUserName: ownerName, RepoName: repoName, + TimeNano: time.Now().UnixNano(), } updates = append(updates, option) if repo.IsEmpty && (refFullName.BranchName() == "master" || refFullName.BranchName() == "main") { diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 0613492845..cb356a184a 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -4,6 +4,7 @@ package private import ( + "errors" "fmt" "net/http" "os" @@ -101,6 +102,60 @@ func (ctx *preReceiveContext) AssertCreatePullRequest() bool { return true } +var errPermissionDenied = errors.New("permission denied for changing repo settings") + +func (ctx *preReceiveContext) canChangeSettings() error { + if !ctx.loadPusherAndPermission() { + return errPermissionDenied + } + + if !ctx.userPerm.IsOwner() && !ctx.userPerm.IsAdmin() { + return errPermissionDenied + } + + if ctx.Repo.Repository.IsFork { + return errPermissionDenied + } + + return nil +} + +func (ctx *preReceiveContext) validatePushOptions() error { + opts := web.GetForm(ctx).(*private.HookOptions) + + if len(opts.GitPushOptions) == 0 { + return nil + } + + changesRepoSettings := false + for key := range opts.GitPushOptions { + switch key { + case private.GitPushOptionRepoPrivate, private.GitPushOptionRepoTemplate: + changesRepoSettings = true + case "topic", "force-push", "title", "description": + // Agit options + default: + return fmt.Errorf("unknown option %s", key) + } + } + + if changesRepoSettings { + return ctx.canChangeSettings() + } + + return nil +} + +func (ctx *preReceiveContext) assertPushOptions() bool { + if err := ctx.validatePushOptions(); err != nil { + ctx.JSON(http.StatusForbidden, private.Response{ + UserMsg: fmt.Sprintf("options validation failed: %v", err), + }) + return false + } + return true +} + // HookPreReceive checks whether a individual commit is acceptable func HookPreReceive(ctx *gitea_context.PrivateContext) { opts := web.GetForm(ctx).(*private.HookOptions) @@ -111,6 +166,12 @@ func HookPreReceive(ctx *gitea_context.PrivateContext) { opts: opts, } + if !ourCtx.assertPushOptions() { + log.Trace("Git push options validation failed") + return + } + log.Trace("Git push options validation succeeded") + // Iterate across the provided old commit IDs for i := range opts.OldCommitIDs { oldCommitID := opts.OldCommitIDs[i] diff --git a/routers/private/hook_verification.go b/routers/private/hook_verification.go index 42b8e5abed..764c976fa9 100644 --- a/routers/private/hook_verification.go +++ b/routers/private/hook_verification.go @@ -47,7 +47,7 @@ func verifyCommits(oldCommitID, newCommitID string, repo *git.Repository, env [] _ = stdoutWriter.Close() err := readAndVerifyCommitsFromShaReader(stdoutReader, repo, env) if err != nil { - log.Error("%v", err) + log.Error("readAndVerifyCommitsFromShaReader failed: %v", err) cancel() } _ = stdoutReader.Close() @@ -66,7 +66,6 @@ func readAndVerifyCommitsFromShaReader(input io.ReadCloser, repo *git.Repository line := scanner.Text() err := readAndVerifyCommit(line, repo, env) if err != nil { - log.Error("%v", err) return err } } diff --git a/routers/private/mail.go b/routers/private/mail.go index c19ee67896..cf3abb31c6 100644 --- a/routers/private/mail.go +++ b/routers/private/mail.go @@ -35,7 +35,7 @@ func SendEmail(ctx *context.PrivateContext) { defer rd.Close() if err := json.NewDecoder(rd).Decode(&mail); err != nil { - log.Error("%v", err) + log.Error("JSON Decode failed: %v", err) ctx.JSON(http.StatusInternalServerError, private.Response{ Err: err.Error(), }) diff --git a/routers/utils/utils.go b/routers/utils/utils.go index 93359a842d..7fddb5a498 100644 --- a/routers/utils/utils.go +++ b/routers/utils/utils.go @@ -6,26 +6,10 @@ package utils import ( "html" - "net/url" "strings" - - "code.gitea.io/gitea/modules/setting" ) // SanitizeFlashErrorString will sanitize a flash error string func SanitizeFlashErrorString(x string) string { return strings.ReplaceAll(html.EscapeString(x), "\n", "
    ") } - -// IsExternalURL checks if rawURL points to an external URL like http://example.com -func IsExternalURL(rawURL string) bool { - parsed, err := url.Parse(rawURL) - if err != nil { - return true - } - appURL, _ := url.Parse(setting.AppURL) - if len(parsed.Host) != 0 && strings.Replace(parsed.Host, "www.", "", 1) != strings.Replace(appURL.Host, "www.", "", 1) { - return true - } - return false -} diff --git a/routers/utils/utils_test.go b/routers/utils/utils_test.go index 440aad87c6..6e7f3c33cd 100644 --- a/routers/utils/utils_test.go +++ b/routers/utils/utils_test.go @@ -5,47 +5,8 @@ package utils import ( "testing" - - "code.gitea.io/gitea/modules/setting" - - "github.com/stretchr/testify/assert" ) -func TestIsExternalURL(t *testing.T) { - setting.AppURL = "https://try.gitea.io/" - type test struct { - Expected bool - RawURL string - } - newTest := func(expected bool, rawURL string) test { - return test{Expected: expected, RawURL: rawURL} - } - for _, test := range []test{ - newTest(false, - "https://try.gitea.io"), - newTest(true, - "https://example.com/"), - newTest(true, - "//example.com"), - newTest(true, - "http://example.com"), - newTest(false, - "a/"), - newTest(false, - "https://try.gitea.io/test?param=false"), - newTest(false, - "test?param=false"), - newTest(false, - "//try.gitea.io/test?param=false"), - newTest(false, - "/hey/hey/hey#3244"), - newTest(true, - "://missing protocol scheme"), - } { - assert.Equal(t, test.Expected, IsExternalURL(test.RawURL)) - } -} - func TestSanitizeFlashErrorString(t *testing.T) { tests := []struct { name string diff --git a/routers/web/admin/hooks.go b/routers/web/admin/hooks.go index 8d59fbb858..c1f42c0061 100644 --- a/routers/web/admin/hooks.go +++ b/routers/web/admin/hooks.go @@ -8,9 +8,9 @@ import ( "code.gitea.io/gitea/models/webhook" "code.gitea.io/gitea/modules/base" - "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/services/context" + webhook_service "code.gitea.io/gitea/services/webhook" ) const ( @@ -35,9 +35,10 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { sys["Title"] = ctx.Tr("admin.systemhooks") sys["Description"] = ctx.Tr("admin.systemhooks.desc") - sys["Webhooks"], err = webhook.GetSystemWebhooks(ctx, optional.None[bool]()) + sys["Webhooks"], err = webhook.GetSystemWebhooks(ctx, false) sys["BaseLink"] = setting.AppSubURL + "/admin/hooks" sys["BaseLinkNew"] = setting.AppSubURL + "/admin/system-hooks" + sys["WebhookList"] = webhook_service.List() if err != nil { ctx.ServerError("GetWebhooksAdmin", err) return @@ -48,6 +49,7 @@ func DefaultOrSystemWebhooks(ctx *context.Context) { def["Webhooks"], err = webhook.GetDefaultWebhooks(ctx) def["BaseLink"] = setting.AppSubURL + "/admin/hooks" def["BaseLinkNew"] = setting.AppSubURL + "/admin/default-hooks" + def["WebhookList"] = webhook_service.List() if err != nil { ctx.ServerError("GetWebhooksAdmin", err) return diff --git a/routers/web/admin/queue.go b/routers/web/admin/queue.go index d8c50730b1..246ab379b5 100644 --- a/routers/web/admin/queue.go +++ b/routers/web/admin/queue.go @@ -69,7 +69,7 @@ func QueueSet(ctx *context.Context) { } func QueueRemoveAllItems(ctx *context.Context) { - // Gitea's queue doesn't have transaction support + // Queue in Forgejo doesn't have transaction support // So in rare cases, the queue could be corrupted/out-of-sync // Site admin could remove all items from the queue to make it work again qid := ctx.ParamsInt64("qid") diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index 3dcf0d2aa8..15bd667a4f 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -403,7 +403,6 @@ func EditUserPost(ctx *context.Context) { ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplUserEdit, &form) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplUserEdit, &form) default: diff --git a/routers/web/auth/auth.go b/routers/web/auth/auth.go index 1c55256db4..50fd760d80 100644 --- a/routers/web/auth/auth.go +++ b/routers/web/auth/auth.go @@ -18,6 +18,7 @@ import ( "code.gitea.io/gitea/modules/auth/password" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/eventsource" + "code.gitea.io/gitea/modules/httplib" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/session" @@ -26,7 +27,6 @@ import ( "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/routers/utils" auth_service "code.gitea.io/gitea/services/auth" "code.gitea.io/gitea/services/auth/source/oauth2" "code.gitea.io/gitea/services/context" @@ -372,16 +372,15 @@ func handleSignInFull(ctx *context.Context, u *user_model.User, remember, obeyRe return setting.AppSubURL + "/" } - if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) { + redirectTo := ctx.GetSiteCookie("redirect_to") + if redirectTo != "" { middleware.DeleteRedirectToCookie(ctx.Resp) - if obeyRedirect { - ctx.RedirectToFirst(redirectTo) - } - return redirectTo } - if obeyRedirect { - ctx.Redirect(setting.AppSubURL + "/") + return ctx.RedirectToFirst(redirectTo) + } + if !httplib.IsRiskyRedirectURL(redirectTo) { + return redirectTo } return setting.AppSubURL + "/" } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index f5ca0bda5e..b48345684b 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -35,6 +35,7 @@ import ( "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/externalaccount" "code.gitea.io/gitea/services/forms" + remote_service "code.gitea.io/gitea/services/remote" user_service "code.gitea.io/gitea/services/user" "gitea.com/go-chi/binding" @@ -1202,9 +1203,21 @@ func handleOAuth2SignIn(ctx *context.Context, source *auth.Source, u *user_model ctx.Redirect(setting.AppSubURL + "/user/two_factor") } -// OAuth2UserLoginCallback attempts to handle the callback from the OAuth2 provider and if successful -// login the user func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (*user_model.User, goth.User, error) { + gothUser, err := oAuth2FetchUser(ctx, authSource, request, response) + if err != nil { + return nil, goth.User{}, err + } + + if _, _, err := remote_service.MaybePromoteRemoteUser(ctx, authSource, gothUser.UserID, gothUser.Email); err != nil { + return nil, goth.User{}, err + } + + u, err := oAuth2GothUserToUser(request.Context(), authSource, gothUser) + return u, gothUser, err +} + +func oAuth2FetchUser(ctx *context.Context, authSource *auth.Source, request *http.Request, response http.ResponseWriter) (goth.User, error) { oauth2Source := authSource.Cfg.(*oauth2.Source) // Make sure that the response is not an error response. @@ -1216,10 +1229,10 @@ func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, requ // Delete the goth session err := gothic.Logout(response, request) if err != nil { - return nil, goth.User{}, err + return goth.User{}, err } - return nil, goth.User{}, errCallback{ + return goth.User{}, errCallback{ Code: errorName, Description: errorDescription, } @@ -1232,24 +1245,28 @@ func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, requ log.Error("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) err = fmt.Errorf("OAuth2 Provider %s returned too long a token. Current max: %d. Either increase the [OAuth2] MAX_TOKEN_LENGTH or reduce the information returned from the OAuth2 provider", authSource.Name, setting.OAuth2.MaxTokenLength) } - return nil, goth.User{}, err + return goth.User{}, err } if oauth2Source.RequiredClaimName != "" { claimInterface, has := gothUser.RawData[oauth2Source.RequiredClaimName] if !has { - return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} + return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} } if oauth2Source.RequiredClaimValue != "" { groups := claimValueToStringSet(claimInterface) if !groups.Contains(oauth2Source.RequiredClaimValue) { - return nil, goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} + return goth.User{}, user_model.ErrUserProhibitLogin{Name: gothUser.UserID} } } } + return gothUser, nil +} + +func oAuth2GothUserToUser(ctx go_context.Context, authSource *auth.Source, gothUser goth.User) (*user_model.User, error) { user := &user_model.User{ LoginName: gothUser.UserID, LoginType: auth.OAuth2, @@ -1258,27 +1275,28 @@ func oAuth2UserLoginCallback(ctx *context.Context, authSource *auth.Source, requ hasUser, err := user_model.GetUser(ctx, user) if err != nil { - return nil, goth.User{}, err + return nil, err } if hasUser { - return user, gothUser, nil + return user, nil } + log.Debug("no user found for LoginName %v, LoginSource %v, LoginType %v", user.LoginName, user.LoginSource, user.LoginType) // search in external linked users externalLoginUser := &user_model.ExternalLoginUser{ ExternalID: gothUser.UserID, LoginSourceID: authSource.ID, } - hasUser, err = user_model.GetExternalLogin(request.Context(), externalLoginUser) + hasUser, err = user_model.GetExternalLogin(ctx, externalLoginUser) if err != nil { - return nil, goth.User{}, err + return nil, err } if hasUser { - user, err = user_model.GetUserByID(request.Context(), externalLoginUser.UserID) - return user, gothUser, err + user, err = user_model.GetUserByID(ctx, externalLoginUser.UserID) + return user, err } // no user found to login - return nil, gothUser, nil + return nil, nil } diff --git a/routers/web/auth/password.go b/routers/web/auth/password.go index c9e0386041..d15a8b814c 100644 --- a/routers/web/auth/password.go +++ b/routers/web/auth/password.go @@ -18,7 +18,6 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" - "code.gitea.io/gitea/routers/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/forms" "code.gitea.io/gitea/services/mailer" @@ -215,7 +214,6 @@ func ResetPasswdPost(ctx *context.Context) { case errors.Is(err, password.ErrIsPwned): ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplResetPassword, nil) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplResetPassword, nil) default: ctx.ServerError("UpdateAuth", err) @@ -299,7 +297,6 @@ func MustChangePasswordPost(ctx *context.Context) { ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned"), tplMustChangePassword, &form) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.Data["Err_Password"] = true ctx.RenderWithErr(ctx.Tr("auth.password_pwned_err"), tplMustChangePassword, &form) default: @@ -312,11 +309,9 @@ func MustChangePasswordPost(ctx *context.Context) { log.Trace("User updated password: %s", ctx.Doer.Name) - if redirectTo := ctx.GetSiteCookie("redirect_to"); len(redirectTo) > 0 && !utils.IsExternalURL(redirectTo) { + redirectTo := ctx.GetSiteCookie("redirect_to") + if redirectTo != "" { middleware.DeleteRedirectToCookie(ctx.Resp) - ctx.RedirectToFirst(redirectTo) - return } - - ctx.Redirect(setting.AppSubURL + "/") + ctx.RedirectToFirst(redirectTo) } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index db1dac0120..821228b347 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strconv" "strings" @@ -105,7 +104,7 @@ func Projects(ctx *context.Context) { } for _, project := range projects { - project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render? + project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description) } err = shared_user.LoadHeaderCount(ctx) @@ -195,14 +194,15 @@ func NewProjectPost(ctx *context.Context) { // ChangeProjectStatus updates the status of a project between "open" and "close" func ChangeProjectStatus(ctx *context.Context) { - toClose := false + var toClose bool switch ctx.Params(":action") { case "open": toClose = false case "close": toClose = true default: - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects") + ctx.JSONRedirect(ctx.ContextUser.HomeLink() + "/-/projects") + return } id := ctx.ParamsInt64(":id") @@ -210,7 +210,7 @@ func ChangeProjectStatus(ctx *context.Context) { ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } - ctx.Redirect(ctx.ContextUser.HomeLink() + "/-/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.JSONRedirect(fmt.Sprintf("%s/-/projects/%d", ctx.ContextUser.HomeLink(), id)) } // DeleteProject delete a project @@ -372,7 +372,7 @@ func ViewProject(ctx *context.Context) { } } - project.RenderedContent = templates.SanitizeHTML(project.Description) // FIXME: is it right? why not render? + project.RenderedContent = templates.RenderMarkdownToHtml(ctx, project.Description) ctx.Data["LinkedPRs"] = linkedPrsMap ctx.Data["PageIsViewProjects"] = true ctx.Data["CanWriteProjects"] = canWriteProjects(ctx) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 93c224b7be..b07209c779 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -35,6 +35,7 @@ import ( api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/routers/common" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/context/upload" "code.gitea.io/gitea/services/gitdiff" @@ -185,21 +186,10 @@ func setCsvCompareContext(ctx *context.Context) { } } -// CompareInfo represents the collected results from ParseCompareInfo -type CompareInfo struct { - HeadUser *user_model.User - HeadRepo *repo_model.Repository - HeadGitRepo *git.Repository - CompareInfo *git.CompareInfo - BaseBranch string - HeadBranch string - DirectComparison bool -} - // ParseCompareInfo parse compare info between two commit for preparing comparing references -func ParseCompareInfo(ctx *context.Context) *CompareInfo { +func ParseCompareInfo(ctx *context.Context) *common.CompareInfo { baseRepo := ctx.Repo.Repository - ci := &CompareInfo{} + ci := &common.CompareInfo{} fileOnly := ctx.FormBool("file-only") @@ -576,7 +566,7 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { // PrepareCompareDiff renders compare diff page func PrepareCompareDiff( ctx *context.Context, - ci *CompareInfo, + ci *common.CompareInfo, whitespaceBehavior git.TrustedCmdArgs, ) bool { var ( diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index da811c8a7c..b0b4b65331 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -2174,7 +2174,7 @@ func GetIssueInfo(ctx *context.Context) { } } - ctx.JSON(http.StatusOK, convert.ToIssue(ctx, issue)) + ctx.JSON(http.StatusOK, convert.ToIssue(ctx, ctx.Doer, issue)) } // UpdateIssueTitle change issue's title @@ -2708,7 +2708,7 @@ func SearchIssues(ctx *context.Context) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues)) } func getUserIDForFilter(ctx *context.Context, queryName string) int64 { @@ -2878,7 +2878,7 @@ func ListIssues(ctx *context.Context) { } ctx.SetTotalCountHeader(total) - ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, issues)) + ctx.JSON(http.StatusOK, convert.ToIssueList(ctx, ctx.Doer, issues)) } func BatchDeleteIssues(ctx *context.Context) { @@ -3160,12 +3160,6 @@ func UpdateCommentContent(ctx *context.Context) { oldContent := comment.Content comment.Content = ctx.FormString("content") - if len(comment.Content) == 0 { - ctx.JSON(http.StatusOK, map[string]any{ - "content": "", - }) - return - } if err = issue_service.UpdateComment(ctx, comment, ctx.Doer, oldContent); err != nil { ctx.ServerError("UpdateComment", err) return diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index aa42585590..a0c044fd64 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net/http" - "net/url" "strings" "code.gitea.io/gitea/models/db" @@ -180,14 +179,10 @@ func ChangeProjectStatus(ctx *context.Context) { id := ctx.ParamsInt64(":id") if err := project_model.ChangeProjectStatusByRepoIDAndID(ctx, ctx.Repo.Repository.ID, id, toClose); err != nil { - if project_model.IsErrProjectNotExist(err) { - ctx.NotFound("", err) - } else { - ctx.ServerError("ChangeProjectStatusByIDAndRepoID", err) - } + ctx.NotFoundOrServerError("ChangeProjectStatusByRepoIDAndID", project_model.IsErrProjectNotExist, err) return } - ctx.JSONRedirect(ctx.Repo.RepoLink + "/projects?state=" + url.QueryEscape(ctx.Params(":action"))) + ctx.JSONRedirect(fmt.Sprintf("%s/projects/%d", ctx.Repo.RepoLink, id)) } // DeleteProject delete a project diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 388fb6cb9b..3927e3d2d9 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -441,6 +441,20 @@ func NewRelease(ctx *context.Context) { } ctx.Data["Tags"] = tags + // We set the value of the hide_archive_link textbox depending on the latest release + latestRelease, err := repo_model.GetLatestReleaseByRepoID(ctx, ctx.Repo.Repository.ID) + if err != nil { + if repo_model.IsErrReleaseNotExist(err) { + ctx.Data["hide_archive_links"] = false + } else { + ctx.ServerError("GetLatestReleaseByRepoID", err) + return + } + } + if latestRelease != nil { + ctx.Data["hide_archive_links"] = latestRelease.HideArchiveLinks + } + ctx.HTML(http.StatusOK, tplReleaseNew) } @@ -525,17 +539,18 @@ func NewReleasePost(ctx *context.Context) { } rel = &repo_model.Release{ - RepoID: ctx.Repo.Repository.ID, - Repo: ctx.Repo.Repository, - PublisherID: ctx.Doer.ID, - Publisher: ctx.Doer, - Title: form.Title, - TagName: form.TagName, - Target: form.Target, - Note: form.Content, - IsDraft: len(form.Draft) > 0, - IsPrerelease: form.Prerelease, - IsTag: false, + RepoID: ctx.Repo.Repository.ID, + Repo: ctx.Repo.Repository, + PublisherID: ctx.Doer.ID, + Publisher: ctx.Doer, + Title: form.Title, + TagName: form.TagName, + Target: form.Target, + Note: form.Content, + IsDraft: len(form.Draft) > 0, + IsPrerelease: form.Prerelease, + HideArchiveLinks: form.HideArchiveLinks, + IsTag: false, } if err = releaseservice.CreateRelease(ctx.Repo.GitRepo, rel, attachmentUUIDs, msg); err != nil { @@ -565,9 +580,10 @@ func NewReleasePost(ctx *context.Context) { rel.IsDraft = len(form.Draft) > 0 rel.IsPrerelease = form.Prerelease rel.PublisherID = ctx.Doer.ID + rel.HideArchiveLinks = form.HideArchiveLinks rel.IsTag = false - if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil); err != nil { + if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, rel, attachmentUUIDs, nil, nil, true); err != nil { ctx.Data["Err_TagName"] = true ctx.ServerError("UpdateRelease", err) return @@ -602,6 +618,7 @@ func EditRelease(ctx *context.Context) { ctx.Data["title"] = rel.Title ctx.Data["content"] = rel.Note ctx.Data["prerelease"] = rel.IsPrerelease + ctx.Data["hide_archive_links"] = rel.HideArchiveLinks ctx.Data["IsDraft"] = rel.IsDraft rel.Repo = ctx.Repo.Repository @@ -648,6 +665,7 @@ func EditReleasePost(ctx *context.Context) { ctx.Data["title"] = rel.Title ctx.Data["content"] = rel.Note ctx.Data["prerelease"] = rel.IsPrerelease + ctx.Data["hide_archive_links"] = rel.HideArchiveLinks if ctx.HasError() { ctx.HTML(http.StatusOK, tplReleaseNew) @@ -673,8 +691,9 @@ func EditReleasePost(ctx *context.Context) { rel.Note = form.Content rel.IsDraft = len(form.Draft) > 0 rel.IsPrerelease = form.Prerelease + rel.HideArchiveLinks = form.HideArchiveLinks if err = releaseservice.UpdateRelease(ctx, ctx.Doer, ctx.Repo.GitRepo, - rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments); err != nil { + rel, addAttachmentUUIDs, delAttachmentUUIDs, editAttachments, false); err != nil { ctx.ServerError("UpdateRelease", err) return } diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 6e70428547..02df17a7e6 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -86,7 +86,7 @@ func Search(ctx *context.Context) { } } - ctx.Data["CodeIndexerEnabled"] = setting.Indexer.RepoIndexerEnabled + ctx.Data["CodeIndexerDisabled"] = !setting.Indexer.RepoIndexerEnabled ctx.Data["Repo"] = ctx.Repo.Repository ctx.Data["SourcePath"] = ctx.Repo.Repository.Link() ctx.Data["SearchResults"] = searchResults diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 25146779de..b2f5798a26 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -321,6 +321,9 @@ func RenameBranchPost(ctx *context.Context) { if errors.Is(err, git_model.ErrBranchIsProtected) { ctx.Flash.Error(ctx.Tr("repo.settings.rename_branch_failed_protected", form.To)) ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink)) + } else if git_model.IsErrBranchAlreadyExists(err) { + ctx.Flash.Error(ctx.Tr("repo.branch.branch_already_exists", form.To)) + ctx.Redirect(fmt.Sprintf("%s/branches", ctx.Repo.RepoLink)) } else { ctx.ServerError("RenameBranch", err) } diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index a65a100212..e98cd46c4e 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -556,7 +556,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { // The Open Group Base Specification: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html // empty: 0 lines; "a": 1 incomplete-line; "a\n": 1 line; "a\nb": 1 line, 1 incomplete-line; - // Gitea uses the definition (like most modern editors): + // Forgejo uses the definition (like most modern editors): // empty: 0 lines; "a": 1 line; "a\n": 2 lines; "a\nb": 2 lines; // When rendering, the last empty line is not rendered in UI, while the line-number is still counted, to tell users that the file contains a trailing EOL. // To make the UI more consistent, it could use an icon mark to indicate that there is no trailing EOL, and show line-number as the rendered lines. diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 2e4b9dae65..1893e91221 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -376,7 +376,7 @@ func Action(ctx *context.Context) { } if redirectViaJSON { - ctx.JSON(http.StatusOK, map[string]interface{}{ + ctx.JSON(http.StatusOK, map[string]any{ "redirect": ctx.ContextUser.HomeLink(), }) return diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 9cf93320a3..795ee59d58 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -74,7 +74,6 @@ func AccountPost(ctx *context.Context) { case errors.Is(err, password.ErrIsPwned): ctx.Flash.Error(ctx.Tr("auth.password_pwned")) case password.IsErrIsPwnedRequest(err): - log.Error("%s", err.Error()) ctx.Flash.Error(ctx.Tr("auth.password_pwned_err")) default: ctx.ServerError("UpdateAuth", err) diff --git a/routers/web/user/setting/profile.go b/routers/web/user/setting/profile.go index a39c118ddd..907f0f5061 100644 --- a/routers/web/user/setting/profile.go +++ b/routers/web/user/setting/profile.go @@ -95,7 +95,7 @@ func ProfilePost(ctx *context.Context) { opts := &user_service.UpdateOptions{ FullName: optional.Some(form.FullName), KeepEmailPrivate: optional.Some(form.KeepEmailPrivate), - Description: optional.Some(form.Description), + Description: optional.Some(form.Biography), Pronouns: optional.Some(form.Pronouns), Website: optional.Some(form.Website), Location: optional.Some(form.Location), diff --git a/routers/web/web.go b/routers/web/web.go index 71d5efef2c..154e1ce24b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -258,7 +258,7 @@ func Routes() *web.Route { routes.Get("/metrics", append(mid, Metrics)...) } - routes.Get("/robots.txt", append(mid, misc.RobotsTxt)...) + routes.Methods("GET,HEAD", "/robots.txt", append(mid, misc.RobotsTxt)...) routes.Get("/ssh_info", misc.SSHInfo) routes.Get("/api/healthz", healthcheck.Check) diff --git a/services/actions/auth_test.go b/services/actions/auth_test.go index f73ae8ae4c..12db2bae56 100644 --- a/services/actions/auth_test.go +++ b/services/actions/auth_test.go @@ -20,7 +20,7 @@ func TestCreateAuthorizationToken(t *testing.T) { assert.Nil(t, err) assert.NotEqual(t, "", token) claims := jwt.MapClaims{} - _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (interface{}, error) { + _, err = jwt.ParseWithClaims(token, claims, func(t *jwt.Token) (any, error) { return setting.GetGeneralTokenSigningSecret(), nil }) assert.Nil(t, err) diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 4236553927..bc2905e089 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -12,10 +12,10 @@ import ( "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" user_model "code.gitea.io/gitea/models/user" - git "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" webhook_module "code.gitea.io/gitea/modules/webhook" + commitstatus_service "code.gitea.io/gitea/services/repository/commitstatus" "github.com/nektos/act/pkg/jobparser" ) @@ -118,23 +118,16 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er } creator := user_model.NewActionsUser() - commitID, err := git.NewIDFromString(sha) - if err != nil { - return fmt.Errorf("HashTypeInterfaceFromHashString: %w", err) - } - if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ - Repo: repo, - SHA: commitID, - Creator: creator, - CommitStatus: &git_model.CommitStatus{ + if err := commitstatus_service.CreateCommitStatus(ctx, repo, creator, + sha, + &git_model.CommitStatus{ SHA: sha, TargetURL: fmt.Sprintf("%s/jobs/%d", run.Link(), index), Description: description, Context: ctxname, CreatorID: creator.ID, State: state, - }, - }); err != nil { + }); err != nil { return fmt.Errorf("NewCommitStatus: %w", err) } diff --git a/services/actions/notifier.go b/services/actions/notifier.go index eec5f814da..6551da39e7 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -49,7 +49,7 @@ func (n *actionsNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu newNotifyInputFromIssue(issue, webhook_module.HookEventIssues).WithPayload(&api.IssuePayload{ Action: api.HookIssueOpened, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, issue.Poster, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, issue.Poster, nil), }).Notify(withMethod(ctx, "NewIssue")) @@ -89,7 +89,7 @@ func (n *actionsNotifier) IssueChangeContent(ctx context.Context, doer *user_mod WithPayload(&api.IssuePayload{ Action: api.HookIssueEdited, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }). @@ -127,7 +127,7 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode } apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), } @@ -229,7 +229,7 @@ func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues WithPayload(&api.IssuePayload{ Action: action, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }). @@ -293,7 +293,7 @@ func notifyIssueCommentChange(ctx context.Context, doer *user_model.User, commen payload := &api.IssueCommentPayload{ Action: action, - Issue: convert.ToAPIIssue(ctx, comment.Issue), + Issue: convert.ToAPIIssue(ctx, doer, comment.Issue), Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment), Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index 0df7533496..365212d9c2 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -80,6 +80,11 @@ func newNotifyInput(repo *repo_model.Repository, doer *user_model.User, event we } } +func newNotifyInputForSchedules(repo *repo_model.Repository) *notifyInput { + // the doer here will be ignored as we force using action user when handling schedules + return newNotifyInput(repo, user_model.NewActionsUser(), webhook_module.HookEventSchedule) +} + func (input *notifyInput) WithDoer(doer *user_model.User) *notifyInput { input.Doer = doer return input @@ -520,7 +525,7 @@ func handleSchedules( RepoID: input.Repo.ID, OwnerID: input.Repo.OwnerID, WorkflowID: dwf.EntryName, - TriggerUserID: input.Doer.ID, + TriggerUserID: user_model.ActionsUserID, Ref: input.Repo.DefaultBranch, CommitSHA: commit.ID.String(), Event: input.Event, @@ -560,12 +565,9 @@ func DetectAndHandleSchedules(ctx context.Context, repo *repo_model.Repository) } // We need a notifyInput to call handleSchedules - // Here we use the commit author as the Doer of the notifyInput - commitUser, err := user_model.GetUserByEmail(ctx, commit.Author.Email) - if err != nil { - return fmt.Errorf("get user by email: %w", err) - } - notifyInput := newNotifyInput(repo, commitUser, webhook_module.HookEventSchedule) + // if repo is a mirror, commit author maybe an external user, + // so we use action user as the Doer of the notifyInput + notifyInput := newNotifyInputForSchedules(repo) return handleSchedules(ctx, scheduleWorkflows, commit, notifyInput, repo.DefaultBranch) } diff --git a/services/auth/source/oauth2/store.go b/services/auth/source/oauth2/store.go index 394bf99463..90fa965602 100644 --- a/services/auth/source/oauth2/store.go +++ b/services/auth/source/oauth2/store.go @@ -9,6 +9,7 @@ import ( "net/http" "code.gitea.io/gitea/modules/log" + session_module "code.gitea.io/gitea/modules/session" chiSession "gitea.com/go-chi/session" "github.com/gorilla/sessions" @@ -65,7 +66,7 @@ func (st *SessionsStore) Save(r *http.Request, w http.ResponseWriter, session *s chiStore := chiSession.GetSession(r) if session.IsNew { - _, _ = chiSession.RegenerateSession(w, r) + _, _ = session_module.RegenerateSession(w, r) session.IsNew = false } diff --git a/services/auth/source/remote/source.go b/services/auth/source/remote/source.go new file mode 100644 index 0000000000..4165858a56 --- /dev/null +++ b/services/auth/source/remote/source.go @@ -0,0 +1,33 @@ +// Copyright Earl Warren +// SPDX-License-Identifier: MIT + +package remote + +import ( + "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/modules/json" +) + +type Source struct { + URL string + MatchingSource string + + // reference to the authSource + authSource *auth.Source +} + +func (source *Source) FromDB(bs []byte) error { + return json.UnmarshalHandleDoubleEncode(bs, &source) +} + +func (source *Source) ToDB() ([]byte, error) { + return json.Marshal(source) +} + +func (source *Source) SetAuthSource(authSource *auth.Source) { + source.authSource = authSource +} + +func init() { + auth.RegisterTypeConfig(auth.Remote, &Source{}) +} diff --git a/services/context/captcha.go b/services/context/captcha.go index a1999900c9..fa8d779f56 100644 --- a/services/context/captcha.go +++ b/services/context/captcha.go @@ -79,11 +79,11 @@ func VerifyCaptcha(ctx *Context, tpl base.TplName, form any) { case setting.CfTurnstile: valid, err = turnstile.Verify(ctx, ctx.Req.Form.Get(cfTurnstileResponseField)) default: - ctx.ServerError("Unknown Captcha Type", fmt.Errorf("Unknown Captcha Type: %s", setting.Service.CaptchaType)) + ctx.ServerError("Unknown Captcha Type", fmt.Errorf("unknown Captcha Type: %s", setting.Service.CaptchaType)) return } if err != nil { - log.Debug("%v", err) + log.Debug("Captcha Verify failed: %v", err) } if !valid { diff --git a/services/context/context_response.go b/services/context/context_response.go index 372b4cb38b..2f2d7b0e1b 100644 --- a/services/context/context_response.go +++ b/services/context/context_response.go @@ -44,8 +44,10 @@ func RedirectToUser(ctx *Base, userName string, redirectUserID int64) { ctx.Redirect(path.Join(setting.AppSubURL, redirectPath), http.StatusTemporaryRedirect) } -// RedirectToFirst redirects to first not empty URL -func (ctx *Context) RedirectToFirst(location ...string) { +// RedirectToFirst redirects to first not empty URL which likely belongs to current site. +// If no suitable redirection is found, it redirects to the home. +// It returns the location it redirected to. +func (ctx *Context) RedirectToFirst(location ...string) string { for _, loc := range location { if len(loc) == 0 { continue @@ -56,10 +58,11 @@ func (ctx *Context) RedirectToFirst(location ...string) { } ctx.Redirect(loc) - return + return loc } ctx.Redirect(setting.AppSubURL + "/") + return setting.AppSubURL + "/" } const tplStatus500 base.TplName = "status/500" diff --git a/services/convert/convert.go b/services/convert/convert.go index 5b06b9b1a0..55996d8fe3 100644 --- a/services/convert/convert.go +++ b/services/convert/convert.go @@ -21,6 +21,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" @@ -105,33 +106,46 @@ func ToBranch(ctx context.Context, repo *repo_model.Repository, branchName strin return branch, nil } +// getWhitelistEntities returns the names of the entities that are in the whitelist +func getWhitelistEntities[T *user_model.User | *organization.Team](entities []T, whitelistIDs []int64) []string { + whitelistUserIDsSet := container.SetOf(whitelistIDs...) + whitelistNames := make([]string, 0) + for _, entity := range entities { + switch v := any(entity).(type) { + case *user_model.User: + if whitelistUserIDsSet.Contains(v.ID) { + whitelistNames = append(whitelistNames, v.Name) + } + case *organization.Team: + if whitelistUserIDsSet.Contains(v.ID) { + whitelistNames = append(whitelistNames, v.Name) + } + } + } + + return whitelistNames +} + // ToBranchProtection convert a ProtectedBranch to api.BranchProtection -func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch) *api.BranchProtection { - pushWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.WhitelistUserIDs) +func ToBranchProtection(ctx context.Context, bp *git_model.ProtectedBranch, repo *repo_model.Repository) *api.BranchProtection { + readers, err := access_model.GetRepoReaders(ctx, repo) if err != nil { - log.Error("GetUserNamesByIDs (WhitelistUserIDs): %v", err) + log.Error("GetRepoReaders: %v", err) } - mergeWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.MergeWhitelistUserIDs) + + pushWhitelistUsernames := getWhitelistEntities(readers, bp.WhitelistUserIDs) + mergeWhitelistUsernames := getWhitelistEntities(readers, bp.MergeWhitelistUserIDs) + approvalsWhitelistUsernames := getWhitelistEntities(readers, bp.ApprovalsWhitelistUserIDs) + + teamReaders, err := organization.OrgFromUser(repo.Owner).TeamsWithAccessToRepo(ctx, repo.ID, perm.AccessModeRead) if err != nil { - log.Error("GetUserNamesByIDs (MergeWhitelistUserIDs): %v", err) - } - approvalsWhitelistUsernames, err := user_model.GetUserNamesByIDs(ctx, bp.ApprovalsWhitelistUserIDs) - if err != nil { - log.Error("GetUserNamesByIDs (ApprovalsWhitelistUserIDs): %v", err) - } - pushWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.WhitelistTeamIDs) - if err != nil { - log.Error("GetTeamNamesByID (WhitelistTeamIDs): %v", err) - } - mergeWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.MergeWhitelistTeamIDs) - if err != nil { - log.Error("GetTeamNamesByID (MergeWhitelistTeamIDs): %v", err) - } - approvalsWhitelistTeams, err := organization.GetTeamNamesByID(ctx, bp.ApprovalsWhitelistTeamIDs) - if err != nil { - log.Error("GetTeamNamesByID (ApprovalsWhitelistTeamIDs): %v", err) + log.Error("Repo.Owner.TeamsWithAccessToRepo: %v", err) } + pushWhitelistTeams := getWhitelistEntities(teamReaders, bp.WhitelistTeamIDs) + mergeWhitelistTeams := getWhitelistEntities(teamReaders, bp.MergeWhitelistTeamIDs) + approvalsWhitelistTeams := getWhitelistEntities(teamReaders, bp.ApprovalsWhitelistTeamIDs) + branchName := "" if !git_model.IsRuleNameSpecial(bp.RuleName) { branchName = bp.RuleName diff --git a/services/convert/issue.go b/services/convert/issue.go index c6e06180c8..54b00cd88e 100644 --- a/services/convert/issue.go +++ b/services/convert/issue.go @@ -18,19 +18,19 @@ import ( api "code.gitea.io/gitea/modules/structs" ) -func ToIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { - return toIssue(ctx, issue, WebAssetDownloadURL) +func ToIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue { + return toIssue(ctx, doer, issue, WebAssetDownloadURL) } // ToAPIIssue converts an Issue to API format // it assumes some fields assigned with values: // Required - Poster, Labels, // Optional - Milestone, Assignee, PullRequest -func ToAPIIssue(ctx context.Context, issue *issues_model.Issue) *api.Issue { - return toIssue(ctx, issue, APIAssetDownloadURL) +func ToAPIIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue) *api.Issue { + return toIssue(ctx, doer, issue, APIAssetDownloadURL) } -func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { +func toIssue(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, getDownloadURL func(repo *repo_model.Repository, attach *repo_model.Attachment) string) *api.Issue { if err := issue.LoadLabels(ctx); err != nil { return &api.Issue{} } @@ -44,7 +44,7 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func apiIssue := &api.Issue{ ID: issue.ID, Index: issue.Index, - Poster: ToUser(ctx, issue.Poster, nil), + Poster: ToUser(ctx, issue.Poster, doer), Title: issue.Title, Body: issue.Content, Attachments: toAttachments(issue.Repo, issue.Attachments, getDownloadURL), @@ -114,25 +114,25 @@ func toIssue(ctx context.Context, issue *issues_model.Issue, getDownloadURL func } // ToIssueList converts an IssueList to API format -func ToIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue { +func ToIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue { result := make([]*api.Issue, len(il)) for i := range il { - result[i] = ToIssue(ctx, il[i]) + result[i] = ToIssue(ctx, doer, il[i]) } return result } // ToAPIIssueList converts an IssueList to API format -func ToAPIIssueList(ctx context.Context, il issues_model.IssueList) []*api.Issue { +func ToAPIIssueList(ctx context.Context, doer *user_model.User, il issues_model.IssueList) []*api.Issue { result := make([]*api.Issue, len(il)) for i := range il { - result[i] = ToAPIIssue(ctx, il[i]) + result[i] = ToAPIIssue(ctx, doer, il[i]) } return result } // ToTrackedTime converts TrackedTime to API format -func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api.TrackedTime) { +func ToTrackedTime(ctx context.Context, doer *user_model.User, t *issues_model.TrackedTime) (apiT *api.TrackedTime) { apiT = &api.TrackedTime{ ID: t.ID, IssueID: t.IssueID, @@ -141,7 +141,7 @@ func ToTrackedTime(ctx context.Context, t *issues_model.TrackedTime) (apiT *api. Created: t.Created, } if t.Issue != nil { - apiT.Issue = ToAPIIssue(ctx, t.Issue) + apiT.Issue = ToAPIIssue(ctx, doer, t.Issue) } if t.User != nil { apiT.UserName = t.User.Name @@ -192,10 +192,10 @@ func ToStopWatches(ctx context.Context, sws []*issues_model.Stopwatch) (api.Stop } // ToTrackedTimeList converts TrackedTimeList to API format -func ToTrackedTimeList(ctx context.Context, tl issues_model.TrackedTimeList) api.TrackedTimeList { +func ToTrackedTimeList(ctx context.Context, doer *user_model.User, tl issues_model.TrackedTimeList) api.TrackedTimeList { result := make([]*api.TrackedTime, 0, len(tl)) for _, t := range tl { - result = append(result, ToTrackedTime(ctx, t)) + result = append(result, ToTrackedTime(ctx, doer, t)) } return result } diff --git a/services/convert/issue_comment.go b/services/convert/issue_comment.go index b034a50897..9ffaf1e84c 100644 --- a/services/convert/issue_comment.go +++ b/services/convert/issue_comment.go @@ -120,7 +120,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu return nil } - comment.TrackedTime = ToTrackedTime(ctx, c.Time) + comment.TrackedTime = ToTrackedTime(ctx, doer, c.Time) } if c.RefIssueID != 0 { @@ -129,7 +129,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu log.Error("GetIssueByID(%d): %v", c.RefIssueID, err) return nil } - comment.RefIssue = ToAPIIssue(ctx, issue) + comment.RefIssue = ToAPIIssue(ctx, doer, issue) } if c.RefCommentID != 0 { @@ -180,7 +180,7 @@ func ToTimelineComment(ctx context.Context, repo *repo_model.Repository, c *issu } if c.DependentIssue != nil { - comment.DependentIssue = ToAPIIssue(ctx, c.DependentIssue) + comment.DependentIssue = ToAPIIssue(ctx, doer, c.DependentIssue) } return comment diff --git a/services/convert/pull.go b/services/convert/pull.go index 6d98121ed5..775bf3806d 100644 --- a/services/convert/pull.go +++ b/services/convert/pull.go @@ -33,7 +33,7 @@ func ToAPIPullRequest(ctx context.Context, pr *issues_model.PullRequest, doer *u return nil } - apiIssue := ToAPIIssue(ctx, pr.Issue) + apiIssue := ToAPIIssue(ctx, doer, pr.Issue) if err := pr.LoadBaseRepo(ctx); err != nil { log.Error("GetRepositoryById[%d]: %v", pr.ID, err) return nil diff --git a/services/convert/release.go b/services/convert/release.go index fb8bd45678..8c0f61b56c 100644 --- a/services/convert/release.go +++ b/services/convert/release.go @@ -22,6 +22,7 @@ func ToAPIRelease(ctx context.Context, repo *repo_model.Repository, r *repo_mode HTMLURL: r.HTMLURL(), TarURL: r.TarURL(), ZipURL: r.ZipURL(), + HideArchiveLinks: r.HideArchiveLinks, UploadURL: r.APIUploadURL(), IsDraft: r.IsDraft, IsPrerelease: r.IsPrerelease, diff --git a/services/convert/repository.go b/services/convert/repository.go index 466d19d563..35becd96d0 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -77,9 +77,13 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR } } hasWiki := false + globallyEditableWiki := false var externalWiki *api.ExternalWiki - if _, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { + if wikiUnit, err := repo.GetUnit(ctx, unit_model.TypeWiki); err == nil { hasWiki = true + if wikiUnit.DefaultPermissions == repo_model.UnitAccessModeWrite { + globallyEditableWiki = true + } } else if unit, err := repo.GetUnit(ctx, unit_model.TypeExternalWiki); err == nil { hasWiki = true config := unit.ExternalWikiConfig() @@ -211,6 +215,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR InternalTracker: internalTracker, HasWiki: hasWiki, WikiBranch: repo.WikiBranch, + GloballyEditableWiki: globallyEditableWiki, HasProjects: hasProjects, HasReleases: hasReleases, HasPackages: hasPackages, @@ -232,6 +237,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR MirrorInterval: mirrorInterval, MirrorUpdated: mirrorUpdated, RepoTransfer: transfer, + ObjectFormatName: repo.ObjectFormatName, } } diff --git a/services/convert/user.go b/services/convert/user.go index 98db53705b..789bc51097 100644 --- a/services/convert/user.go +++ b/services/convert/user.go @@ -76,6 +76,7 @@ func toUser(ctx context.Context, user *user_model.User, signed, authed bool) *ap if authed { result.IsAdmin = user.IsAdmin result.LoginName = user.LoginName + result.SourceID = user.LoginSource result.LastLogin = user.LastLoginUnix.AsTime() result.Language = user.Language result.IsActive = user.IsActive diff --git a/services/cron/tasks_extended.go b/services/cron/tasks_extended.go index 569e8fbd24..e1ba5274e6 100644 --- a/services/cron/tasks_extended.go +++ b/services/cron/tasks_extended.go @@ -147,7 +147,7 @@ func registerUpdateGiteaChecker() { } RegisterTaskFatal("update_checker", &UpdateCheckerConfig{ BaseConfig: BaseConfig{ - Enabled: false, + Enabled: true, RunAtStart: false, Schedule: "@every 168h", }, diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 5a80c327fb..ff758c9254 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -562,15 +562,16 @@ type UpdateAllowEditsForm struct { // NewReleaseForm form for creating release type NewReleaseForm struct { - TagName string `binding:"Required;GitRefName;MaxSize(255)"` - Target string `form:"tag_target" binding:"Required;MaxSize(255)"` - Title string `binding:"MaxSize(255)"` - Content string - Draft string - TagOnly string - Prerelease bool - AddTagMsg bool - Files []string + TagName string `binding:"Required;GitRefName;MaxSize(255)"` + Target string `form:"tag_target" binding:"Required;MaxSize(255)"` + Title string `binding:"MaxSize(255)"` + Content string + Draft string + TagOnly string + Prerelease bool + AddTagMsg bool + HideArchiveLinks bool + Files []string } // Validate validates the fields @@ -581,11 +582,12 @@ func (f *NewReleaseForm) Validate(req *http.Request, errs binding.Errors) bindin // EditReleaseForm form for changing release type EditReleaseForm struct { - Title string `form:"title" binding:"Required;MaxSize(255)"` - Content string `form:"content"` - Draft string `form:"draft"` - Prerelease bool `form:"prerelease"` - Files []string + Title string `form:"title" binding:"Required;MaxSize(255)"` + Content string `form:"content"` + Draft string `form:"draft"` + Prerelease bool `form:"prerelease"` + HideArchiveLinks bool + Files []string } // Validate validates the fields diff --git a/services/forms/user_form.go b/services/forms/user_form.go index 196b092990..4e603a3115 100644 --- a/services/forms/user_form.go +++ b/services/forms/user_form.go @@ -219,7 +219,7 @@ type UpdateProfileForm struct { Website string `binding:"ValidSiteUrl;MaxSize(255)"` Location string `binding:"MaxSize(50)"` Pronouns string `binding:"MaxSize(50)"` - Description string `binding:"MaxSize(255)"` + Biography string `binding:"MaxSize(255)"` Visibility structs.VisibleType KeepActivityPrivate bool } diff --git a/services/mailer/incoming/incoming_handler.go b/services/mailer/incoming/incoming_handler.go index dc0b539822..325b94ae09 100644 --- a/services/mailer/incoming/incoming_handler.go +++ b/services/mailer/incoming/incoming_handler.go @@ -82,31 +82,34 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u return nil } + log.Trace("incoming mail related to %T", ref) + + attachmentIDs := make([]string, 0, len(content.Attachments)) + if setting.Attachment.Enabled { + for _, attachment := range content.Attachments { + a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{ + Name: attachment.Name, + UploaderID: doer.ID, + RepoID: issue.Repo.ID, + }) + if err != nil { + if upload.IsErrFileTypeForbidden(err) { + log.Info("Skipping disallowed attachment type: %s", attachment.Name) + continue + } + return err + } + attachmentIDs = append(attachmentIDs, a.UUID) + } + } + + if content.Content == "" && len(attachmentIDs) == 0 { + log.Trace("incoming mail has no content and no attachement", ref) + return nil + } + switch r := ref.(type) { case *issues_model.Issue: - attachmentIDs := make([]string, 0, len(content.Attachments)) - if setting.Attachment.Enabled { - for _, attachment := range content.Attachments { - a, err := attachment_service.UploadAttachment(ctx, bytes.NewReader(attachment.Content), setting.Attachment.AllowedTypes, int64(len(attachment.Content)), &repo_model.Attachment{ - Name: attachment.Name, - UploaderID: doer.ID, - RepoID: issue.Repo.ID, - }) - if err != nil { - if upload.IsErrFileTypeForbidden(err) { - log.Info("Skipping disallowed attachment type: %s", attachment.Name) - continue - } - return err - } - attachmentIDs = append(attachmentIDs, a.UUID) - } - } - - if content.Content == "" && len(attachmentIDs) == 0 { - return nil - } - _, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs) if err != nil { return fmt.Errorf("CreateIssueComment failed: %w", err) @@ -114,11 +117,13 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u case *issues_model.Comment: comment := r - if content.Content == "" { - return nil - } - - if comment.Type == issues_model.CommentTypeCode { + switch comment.Type { + case issues_model.CommentTypeComment, issues_model.CommentTypeReview: + _, err = issue_service.CreateIssueComment(ctx, doer, issue.Repo, issue, content.Content, attachmentIDs) + if err != nil { + return fmt.Errorf("CreateIssueComment failed: %w", err) + } + case issues_model.CommentTypeCode: _, err := pull_service.CreateCodeComment( ctx, doer, @@ -130,12 +135,16 @@ func (h *ReplyHandler) Handle(ctx context.Context, content *MailContent, doer *u false, // not pending review but a single review comment.ReviewID, "", - nil, + attachmentIDs, ) if err != nil { return fmt.Errorf("CreateCodeComment failed: %w", err) } + default: + log.Trace("incoming mail related to comment of type %v is ignored", comment.Type) } + default: + log.Trace("incoming mail related to %T is ignored", ref) } return nil } diff --git a/services/mailer/mail.go b/services/mailer/mail.go index 26aebd2583..86bd40ff29 100644 --- a/services/mailer/mail.go +++ b/services/mailer/mail.go @@ -389,6 +389,10 @@ func createReference(issue *issues_model.Issue, comment *issues_model.Comment, a return fmt.Sprintf("<%s/%s/%d%s@%s>", issue.Repo.FullName(), path, issue.Index, extra, setting.Domain) } +func createMessageIDForRelease(rel *repo_model.Release) string { + return fmt.Sprintf("<%s/releases/%d@%s>", rel.Repo.FullName(), rel.ID, setting.Domain) +} + func generateAdditionalHeaders(ctx *mailCommentContext, reason string, recipient *user_model.User) map[string]string { repo := ctx.Issue.Repo diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 6682774a04..2b0e7cfdc0 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -86,11 +86,11 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo msgs := make([]*Message, 0, len(tos)) publisherName := rel.Publisher.DisplayName() - relURL := "<" + rel.HTMLURL() + ">" + msgID := createMessageIDForRelease(rel) for _, to := range tos { msg := NewMessageFrom(to, publisherName, setting.MailService.FromEmail, subject, mailBody.String()) msg.Info = subject - msg.SetHeader("Message-ID", relURL) + msg.SetHeader("Message-ID", msgID) msgs = append(msgs, msg) } diff --git a/services/mailer/mailer_test.go b/services/mailer/mailer_test.go index 375ca35daa..253454e89c 100644 --- a/services/mailer/mailer_test.go +++ b/services/mailer/mailer_test.go @@ -7,6 +7,7 @@ import ( "testing" "time" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/modules/setting" "github.com/stretchr/testify/assert" @@ -36,3 +37,17 @@ func TestGenerateMessageID(t *testing.T) { gm = m.ToMessage() assert.Equal(t, "", gm.GetHeader("Message-ID")[0]) } + +func TestGenerateMessageIDForRelease(t *testing.T) { + setting.Domain = "localhost" + + rel := repo_model.Release{ + ID: 42, + Repo: &repo_model.Repository{ + OwnerName: "test", + Name: "tag-test", + }, + } + m := createMessageIDForRelease(&rel) + assert.Equal(t, "", m) +} diff --git a/services/migrations/gitbucket.go b/services/migrations/gitbucket.go index 5f11555839..4fe9e30a39 100644 --- a/services/migrations/gitbucket.go +++ b/services/migrations/gitbucket.go @@ -72,6 +72,11 @@ func (g *GitBucketDownloader) LogString() string { // NewGitBucketDownloader creates a GitBucket downloader func NewGitBucketDownloader(ctx context.Context, baseURL, userName, password, token, repoOwner, repoName string) *GitBucketDownloader { githubDownloader := NewGithubDownloaderV3(ctx, baseURL, userName, password, token, repoOwner, repoName) + // Gitbucket 4.40 uses different internal hard-coded perPage values. + // Issues, PRs, and other major parts use 25. Release page uses 10. + // Some API doesn't support paging yet. Sounds difficult, but using + // minimum number among them worked out very well. + githubDownloader.maxPerPage = 10 githubDownloader.SkipReactions = true githubDownloader.SkipReviews = true return &GitBucketDownloader{ diff --git a/services/mirror/mirror_pull.go b/services/mirror/mirror_pull.go index 2a38d4ba55..fa23986c54 100644 --- a/services/mirror/mirror_pull.go +++ b/services/mirror/mirror_pull.go @@ -13,6 +13,7 @@ import ( system_model "code.gitea.io/gitea/models/system" "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" + giturl "code.gitea.io/gitea/modules/git/url" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" @@ -30,10 +31,15 @@ const gitShortEmptySha = "0000000" // UpdateAddress writes new address to Git repository and database func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error { + u, err := giturl.Parse(addr) + if err != nil { + return fmt.Errorf("invalid addr: %v", err) + } + remoteName := m.GetRemoteName() repoPath := m.GetRepository(ctx).RepoPath() // Remove old remote - _, _, err := git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) + _, _, err = git.NewCommand(ctx, "remote", "rm").AddDynamicArguments(remoteName).RunStdString(&git.RunOpts{Dir: repoPath}) if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { return err } @@ -70,7 +76,9 @@ func UpdateAddress(ctx context.Context, m *repo_model.Mirror, addr string) error } } - m.Repo.OriginalURL = addr + // erase authentication before storing in database + u.User = nil + m.Repo.OriginalURL = u.String() return repo_model.UpdateRepositoryCols(ctx, m.Repo, "original_url") } @@ -449,19 +457,17 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { return false } - var gitRepo *git.Repository - if len(results) == 0 { - log.Trace("SyncMirrors [repo: %-v]: no branches updated", m.Repo) - } else { - log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) - gitRepo, err = gitrepo.OpenRepository(ctx, m.Repo) - if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) - return false - } - defer gitRepo.Close() + gitRepo, err := gitrepo.OpenRepository(ctx, m.Repo) + if err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to OpenRepository: %v", m.Repo, err) + return false + } + defer gitRepo.Close() + log.Trace("SyncMirrors [repo: %-v]: %d branches updated", m.Repo, len(results)) + if len(results) > 0 { if ok := checkAndUpdateEmptyRepository(ctx, m, gitRepo, results); !ok { + log.Error("SyncMirrors [repo: %-v]: checkAndUpdateEmptyRepository: %v", m.Repo, err) return false } } @@ -534,16 +540,24 @@ func SyncPullMirror(ctx context.Context, repoID int64) bool { } log.Trace("SyncMirrors [repo: %-v]: done notifying updated branches/tags - now updating last commit time", m.Repo) - // Get latest commit date and update to current repository updated time - commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) + isEmpty, err := gitRepo.IsEmpty() if err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) + log.Error("SyncMirrors [repo: %-v]: unable to check empty git repo: %v", m.Repo, err) return false } + if !isEmpty { + // Get latest commit date and update to current repository updated time + commitDate, err := git.GetLatestCommitTime(ctx, m.Repo.RepoPath()) + if err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to GetLatestCommitDate: %v", m.Repo, err) + return false + } + + if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { + log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) + return false + } - if err = repo_model.UpdateRepositoryUpdatedTime(ctx, m.RepoID, commitDate); err != nil { - log.Error("SyncMirrors [repo: %-v]: unable to update repository 'updated_unix': %v", m.Repo, err) - return false } log.Trace("SyncMirrors [repo: %-v]: Successfully updated", m.Repo) diff --git a/services/notify/notify.go b/services/notify/notify.go index 9cb329d302..5ed63646aa 100644 --- a/services/notify/notify.go +++ b/services/notify/notify.go @@ -91,7 +91,7 @@ func AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues // NewPullRequest notifies new pull request to notifiers func NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) { if err := pr.LoadIssue(ctx); err != nil { - log.Error("%v", err) + log.Error("LoadIssue failed: %v", err) return } if err := pr.Issue.LoadPoster(ctx); err != nil { @@ -112,7 +112,7 @@ func PullRequestSynchronized(ctx context.Context, doer *user_model.User, pr *iss // PullRequestReview notifies new pull request review func PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, review *issues_model.Review, comment *issues_model.Comment, mentions []*user_model.User) { if err := review.LoadReviewer(ctx); err != nil { - log.Error("%v", err) + log.Error("LoadReviewer failed: %v", err) return } for _, notifier := range notifiers { diff --git a/services/pull/merge.go b/services/pull/merge.go index 7f79eca2aa..2989d77c6a 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -187,7 +187,7 @@ func Merge(ctx context.Context, pr *issues_model.PullRequest, doer *user_model.U } defer func() { - go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") + AddTestPullRequestTask(ctx, doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "", 0) }() pr.MergedCommitID, err = doMergeAndPush(ctx, pr, doer, mergeStyle, expectedHeadCommitID, message) diff --git a/services/pull/pull.go b/services/pull/pull.go index 3d0d38f488..720efdb0cb 100644 --- a/services/pull/pull.go +++ b/services/pull/pull.go @@ -26,6 +26,7 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/sync" @@ -295,113 +296,130 @@ func checkForInvalidation(ctx context.Context, requests issues_model.PullRequest // AddTestPullRequestTask adds new test tasks by given head/base repository and head/base branch, // and generate new patch for testing as needed. -func AddTestPullRequestTask(doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string) { - log.Trace("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: finding pull requests", repoID, branch) - graceful.GetManager().RunWithShutdownContext(func(ctx context.Context) { +func AddTestPullRequestTask(ctx context.Context, doer *user_model.User, repoID int64, branch string, isSync bool, oldCommitID, newCommitID string, timeNano int64) { + description := fmt.Sprintf("AddTestPullRequestTask [head_repo_id: %d, head_branch: %s]: only pull requests created before nano time %d will be considered", repoID, branch, timeNano) + log.Trace(description) + go graceful.GetManager().RunWithShutdownContext(func(shutdownCtx context.Context) { + // make it a process to allow for cancellation (especially during integration tests where no global shutdown happens) + ctx, _, finished := process.GetManager().AddContext(shutdownCtx, description) + defer finished() // There is no sensible way to shut this down ":-(" // If you don't let it run all the way then you will lose data - // TODO: graceful: AddTestPullRequestTask needs to become a queue! + // TODO: graceful: TestPullRequest needs to become a queue! - // GetUnmergedPullRequestsByHeadInfo() only return open and unmerged PR. - prs, err := issues_model.GetUnmergedPullRequestsByHeadInfo(ctx, repoID, branch) - if err != nil { - log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) - return - } + TestPullRequest(ctx, doer, repoID, timeNano, branch, isSync, oldCommitID, newCommitID) + }) +} - for _, pr := range prs { - log.Trace("Updating PR[%d]: composing new test task", pr.ID) - if pr.Flow == issues_model.PullRequestFlowGithub { - if err := PushToBaseRepo(ctx, pr); err != nil { - log.Error("PushToBaseRepo: %v", err) - continue - } - } else { +func TestPullRequest(ctx context.Context, doer *user_model.User, repoID, olderThan int64, branch string, isSync bool, oldCommitID, newCommitID string) { + // Only consider PR that are older than olderThan, which is the time at + // which the newCommitID was added to repoID. + // + // * commit C is pushed + // * the git hook queues AddTestPullRequestTask for processing and returns with success + // * TestPullRequest is not called yet + // * a pull request P with commit C as the head is created + // * TestPullRequest runs and ignores P because it was created after the commit was received + // + // In other words, a PR must not be updated based on events that happened before it existed + prs, err := issues_model.GetUnmergedPullRequestsByHeadInfoMax(ctx, repoID, olderThan, branch) + if err != nil { + log.Error("Find pull requests [head_repo_id: %d, head_branch: %s]: %v", repoID, branch, err) + return + } + + for _, pr := range prs { + log.Trace("Updating PR[id=%d,index=%d]: composing new test task", pr.ID, pr.Index) + if pr.Flow == issues_model.PullRequestFlowGithub { + if err := PushToBaseRepo(ctx, pr); err != nil { + log.Error("PushToBaseRepo: %v", err) continue } - - AddToTaskQueue(ctx, pr) - comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) - if err == nil && comment != nil { - notify_service.PullRequestPushCommits(ctx, doer, pr, comment) - } + } else { + continue } - if isSync { - requests := issues_model.PullRequestList(prs) - if err = requests.LoadAttributes(ctx); err != nil { - log.Error("PullRequestList.LoadAttributes: %v", err) - } - if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil { - log.Error("checkForInvalidation: %v", invalidationErr) - } - if err == nil { - for _, pr := range prs { - objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) - if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { - changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) - if err != nil { - log.Error("checkIfPRContentChanged: %v", err) - } - if changed { - // Mark old reviews as stale if diff to mergebase has changed - if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil { - log.Error("MarkReviewsAsStale: %v", err) - } + AddToTaskQueue(ctx, pr) + comment, err := CreatePushPullComment(ctx, doer, pr, oldCommitID, newCommitID) + if err == nil && comment != nil { + notify_service.PullRequestPushCommits(ctx, doer, pr, comment) + } + } - // dismiss all approval reviews if protected branch rule item enabled. - pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) - if err != nil { - log.Error("GetFirstMatchProtectedBranchRule: %v", err) - } - if pb != nil && pb.DismissStaleApprovals { - if err := DismissApprovalReviews(ctx, doer, pr); err != nil { - log.Error("DismissApprovalReviews: %v", err) - } - } + if isSync { + requests := issues_model.PullRequestList(prs) + if err = requests.LoadAttributes(ctx); err != nil { + log.Error("PullRequestList.LoadAttributes: %v", err) + } + if invalidationErr := checkForInvalidation(ctx, requests, repoID, doer, branch); invalidationErr != nil { + log.Error("checkForInvalidation: %v", invalidationErr) + } + if err == nil { + for _, pr := range prs { + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) + if newCommitID != "" && newCommitID != objectFormat.EmptyObjectID().String() { + changed, err := checkIfPRContentChanged(ctx, pr, oldCommitID, newCommitID) + if err != nil { + log.Error("checkIfPRContentChanged: %v", err) + } + if changed { + // Mark old reviews as stale if diff to mergebase has changed + if err := issues_model.MarkReviewsAsStale(ctx, pr.IssueID); err != nil { + log.Error("MarkReviewsAsStale: %v", err) } - if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil { - log.Error("MarkReviewsAsNotStale: %v", err) - } - divergence, err := GetDiverging(ctx, pr) + + // dismiss all approval reviews if protected branch rule item enabled. + pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) if err != nil { - log.Error("GetDiverging: %v", err) - } else { - err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) - if err != nil { - log.Error("UpdateCommitDivergence: %v", err) + log.Error("GetFirstMatchProtectedBranchRule: %v", err) + } + if pb != nil && pb.DismissStaleApprovals { + if err := DismissApprovalReviews(ctx, doer, pr); err != nil { + log.Error("DismissApprovalReviews: %v", err) } } } - - notify_service.PullRequestSynchronized(ctx, doer, pr) + if err := issues_model.MarkReviewsAsNotStale(ctx, pr.IssueID, newCommitID); err != nil { + log.Error("MarkReviewsAsNotStale: %v", err) + } + divergence, err := GetDiverging(ctx, pr) + if err != nil { + log.Error("GetDiverging: %v", err) + } else { + err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) + if err != nil { + log.Error("UpdateCommitDivergence: %v", err) + } + } } + + notify_service.PullRequestSynchronized(ctx, doer, pr) } } + } - log.Trace("AddTestPullRequestTask [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) - prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch) + log.Trace("TestPullRequest [base_repo_id: %d, base_branch: %s]: finding pull requests", repoID, branch) + prs, err = issues_model.GetUnmergedPullRequestsByBaseInfo(ctx, repoID, branch) + if err != nil { + log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) + return + } + for _, pr := range prs { + divergence, err := GetDiverging(ctx, pr) if err != nil { - log.Error("Find pull requests [base_repo_id: %d, base_branch: %s]: %v", repoID, branch, err) - return - } - for _, pr := range prs { - divergence, err := GetDiverging(ctx, pr) - if err != nil { - if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { - log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) - } else { - log.Error("GetDiverging: %v", err) - } + if git_model.IsErrBranchNotExist(err) && !git.IsBranchExist(ctx, pr.HeadRepo.RepoPath(), pr.HeadBranch) { + log.Warn("Cannot test PR %s/%d: head_branch %s no longer exists", pr.BaseRepo.Name, pr.IssueID, pr.HeadBranch) } else { - err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) - if err != nil { - log.Error("UpdateCommitDivergence: %v", err) - } + log.Error("GetDiverging: %v", err) + } + } else { + err = pr.UpdateCommitDivergence(ctx, divergence.Ahead, divergence.Behind) + if err != nil { + log.Error("UpdateCommitDivergence: %v", err) } - AddToTaskQueue(ctx, pr) } - }) + AddToTaskQueue(ctx, pr) + } } // checkIfPRContentChanged checks if diff to target branch has changed by push diff --git a/services/pull/update.go b/services/pull/update.go index bc8c4a25e5..1de125eb4d 100644 --- a/services/pull/update.go +++ b/services/pull/update.go @@ -36,7 +36,7 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model. if rebase { defer func() { - go AddTestPullRequestTask(doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "") + AddTestPullRequestTask(ctx, doer, pr.BaseRepo.ID, pr.BaseBranch, false, "", "", 0) }() return updateHeadByRebaseOnToBase(ctx, pr, doer, message) @@ -75,7 +75,7 @@ func Update(ctx context.Context, pr *issues_model.PullRequest, doer *user_model. _, err = doMergeAndPush(ctx, reversePR, doer, repo_model.MergeStyleMerge, "", message) defer func() { - go AddTestPullRequestTask(doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "") + AddTestPullRequestTask(ctx, doer, reversePR.HeadRepo.ID, reversePR.HeadBranch, false, "", "", 0) }() return err diff --git a/services/release/release.go b/services/release/release.go index b387ccb5c4..5062af1436 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -199,7 +199,7 @@ func CreateNewTag(ctx context.Context, doer *user_model.User, repo *repo_model.R // delAttachmentUUIDs accept a slice of attachments' uuids which will be deleted from the release // editAttachments accept a map of attachment uuid to new attachment name which will be updated with attachments. func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repository, rel *repo_model.Release, - addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string, + addAttachmentUUIDs, delAttachmentUUIDs []string, editAttachments map[string]string, createdFromTag bool, ) error { if rel.ID == 0 { return errors.New("UpdateRelease only accepts an exist release") @@ -292,11 +292,11 @@ func UpdateRelease(ctx context.Context, doer *user_model.User, gitRepo *git.Repo } if !rel.IsDraft { - if !isCreated { - notify_service.UpdateRelease(gitRepo.Ctx, doer, rel) + if createdFromTag || isCreated { + notify_service.NewRelease(gitRepo.Ctx, rel) return nil } - notify_service.NewRelease(gitRepo.Ctx, rel) + notify_service.UpdateRelease(gitRepo.Ctx, doer, rel) } return nil } diff --git a/services/release/release_test.go b/services/release/release_test.go index 3d0681f1e1..eac1879f87 100644 --- a/services/release/release_test.go +++ b/services/release/release_test.go @@ -159,7 +159,7 @@ func TestRelease_Update(t *testing.T) { releaseCreatedUnix := release.CreatedUnix time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp release.Note = "Changed note" - assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil)) + assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil, false)) release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID) assert.NoError(t, err) assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix)) @@ -183,7 +183,7 @@ func TestRelease_Update(t *testing.T) { releaseCreatedUnix = release.CreatedUnix time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp release.Title = "Changed title" - assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil)) + assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil, false)) release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID) assert.NoError(t, err) assert.Less(t, int64(releaseCreatedUnix), int64(release.CreatedUnix)) @@ -208,7 +208,7 @@ func TestRelease_Update(t *testing.T) { time.Sleep(2 * time.Second) // sleep 2 seconds to ensure a different timestamp release.Title = "Changed title" release.Note = "Changed note" - assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil)) + assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil, false)) release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID) assert.NoError(t, err) assert.Equal(t, int64(releaseCreatedUnix), int64(release.CreatedUnix)) @@ -233,7 +233,7 @@ func TestRelease_Update(t *testing.T) { release.IsDraft = false tagName := release.TagName - assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil)) + assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, nil, false)) release, err = repo_model.GetReleaseByID(db.DefaultContext, release.ID) assert.NoError(t, err) assert.Equal(t, tagName, release.TagName) @@ -247,7 +247,7 @@ func TestRelease_Update(t *testing.T) { }, strings.NewReader(samplePayload), int64(len([]byte(samplePayload)))) assert.NoError(t, err) - assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, []string{attach.UUID}, nil, nil)) + assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, []string{attach.UUID}, nil, nil, false)) assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Len(t, release.Attachments, 1) assert.EqualValues(t, attach.UUID, release.Attachments[0].UUID) @@ -257,7 +257,7 @@ func TestRelease_Update(t *testing.T) { // update the attachment name assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, nil, map[string]string{ attach.UUID: "test2.txt", - })) + }, false)) release.Attachments = nil assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Len(t, release.Attachments, 1) @@ -266,7 +266,7 @@ func TestRelease_Update(t *testing.T) { assert.EqualValues(t, "test2.txt", release.Attachments[0].Name) // delete the attachment - assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, []string{attach.UUID}, nil)) + assert.NoError(t, UpdateRelease(db.DefaultContext, user, gitRepo, release, nil, []string{attach.UUID}, nil, false)) release.Attachments = nil assert.NoError(t, repo_model.GetReleaseAttachments(db.DefaultContext, release)) assert.Empty(t, release.Attachments) diff --git a/services/remote/promote.go b/services/remote/promote.go new file mode 100644 index 0000000000..6709b4cc1d --- /dev/null +++ b/services/remote/promote.go @@ -0,0 +1,133 @@ +// Copyright Earl Warren +// SPDX-License-Identifier: MIT + +package remote + +import ( + "context" + + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/services/auth/source/oauth2" + remote_source "code.gitea.io/gitea/services/auth/source/remote" +) + +type Reason int + +const ( + ReasonNoMatch Reason = iota + ReasonNotAuth2 + ReasonBadAuth2 + ReasonLoginNameNotExists + ReasonNotRemote + ReasonEmailIsSet + ReasonNoSource + ReasonSourceWrongType + ReasonCanPromote + ReasonPromoted + ReasonUpdateFail + ReasonErrorLoginName + ReasonErrorGetSource +) + +func NewReason(level log.Level, reason Reason, message string, args ...any) Reason { + log.Log(1, level, message, args...) + return reason +} + +func getUsersByLoginName(ctx context.Context, name string) ([]*user_model.User, error) { + if len(name) == 0 { + return nil, user_model.ErrUserNotExist{Name: name} + } + + users := make([]*user_model.User, 0, 5) + + return users, db.GetEngine(ctx). + Table("user"). + Where("login_name = ? AND login_type = ? AND type = ?", name, auth_model.Remote, user_model.UserTypeRemoteUser). + Find(&users) +} + +// The remote user has: +// +// Type UserTypeRemoteUser +// LogingType Remote +// LoginName set to the unique identifier of the originating authentication source +// LoginSource set to the Remote source that can be matched against an OAuth2 source +// +// If the source from which an authentification happens is OAuth2, an existing +// remote user will be promoted to an OAuth2 user provided: +// +// user.LoginName is the same as goth.UserID (argument loginName) +// user.LoginSource has a MatchingSource equals to the name of the OAuth2 provider +// +// Once promoted, the user will be logged in without further interaction from the +// user and will own all repositories, issues, etc. associated with it. +func MaybePromoteRemoteUser(ctx context.Context, source *auth_model.Source, loginName, email string) (promoted bool, reason Reason, err error) { + user, reason, err := getRemoteUserToPromote(ctx, source, loginName, email) + if err != nil || user == nil { + return false, reason, err + } + promote := &user_model.User{ + ID: user.ID, + Type: user_model.UserTypeIndividual, + Email: email, + LoginSource: source.ID, + LoginType: source.Type, + } + reason = NewReason(log.DEBUG, ReasonPromoted, "promote user %v: LoginName %v => %v, LoginSource %v => %v, LoginType %v => %v, Email %v => %v", user.ID, user.LoginName, promote.LoginName, user.LoginSource, promote.LoginSource, user.LoginType, promote.LoginType, user.Email, promote.Email) + if err := user_model.UpdateUserCols(ctx, promote, "type", "email", "login_source", "login_type"); err != nil { + return false, ReasonUpdateFail, err + } + return true, reason, nil +} + +func getRemoteUserToPromote(ctx context.Context, source *auth_model.Source, loginName, email string) (*user_model.User, Reason, error) { + if !source.IsOAuth2() { + return nil, NewReason(log.DEBUG, ReasonNotAuth2, "source %v is not OAuth2", source), nil + } + oauth2Source, ok := source.Cfg.(*oauth2.Source) + if !ok { + return nil, NewReason(log.ERROR, ReasonBadAuth2, "source claims to be OAuth2 but is not"), nil + } + + users, err := getUsersByLoginName(ctx, loginName) + if err != nil { + return nil, NewReason(log.ERROR, ReasonErrorLoginName, "getUserByLoginName('%s') %v", loginName, err), err + } + if len(users) == 0 { + return nil, NewReason(log.ERROR, ReasonLoginNameNotExists, "no user with LoginType UserTypeRemoteUser and LoginName '%s'", loginName), nil + } + + reason := ReasonNoSource + for _, u := range users { + userSource, err := auth_model.GetSourceByID(ctx, u.LoginSource) + if err != nil { + if auth_model.IsErrSourceNotExist(err) { + reason = NewReason(log.DEBUG, ReasonNoSource, "source id = %v for user %v not found %v", u.LoginSource, u.ID, err) + continue + } + return nil, NewReason(log.ERROR, ReasonErrorGetSource, "GetSourceByID('%s') %v", u.LoginSource, err), err + } + if u.Email != "" { + reason = NewReason(log.DEBUG, ReasonEmailIsSet, "the user email is already set to '%s'", u.Email) + continue + } + remoteSource, ok := userSource.Cfg.(*remote_source.Source) + if !ok { + reason = NewReason(log.DEBUG, ReasonSourceWrongType, "expected a remote source but got %T %v", userSource, userSource) + continue + } + + if oauth2Source.Provider != remoteSource.MatchingSource { + reason = NewReason(log.DEBUG, ReasonNoMatch, "skip OAuth2 source %s because it is different from %s which is the expected match for the remote source %s", oauth2Source.Provider, remoteSource.MatchingSource, remoteSource.URL) + continue + } + + return u, ReasonCanPromote, nil + } + + return nil, reason, nil +} diff --git a/services/repository/commitstatus/commitstatus.go b/services/repository/commitstatus/commitstatus.go index 0bb738e2ad..bd40a75dc8 100644 --- a/services/repository/commitstatus/commitstatus.go +++ b/services/repository/commitstatus/commitstatus.go @@ -7,6 +7,7 @@ import ( "context" "crypto/sha256" "fmt" + "slices" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" @@ -15,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/cache" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/services/automerge" @@ -25,12 +27,41 @@ func getCacheKey(repoID int64, brancheName string) string { return fmt.Sprintf("commit_status:%x", hashBytes) } -func updateCommitStatusCache(ctx context.Context, repoID int64, branchName string, status api.CommitStatusState) error { - c := cache.GetCache() - return c.Put(getCacheKey(repoID, branchName), string(status), 3*24*60) +type commitStatusCacheValue struct { + State string `json:"state"` + TargetURL string `json:"target_url"` } -func deleteCommitStatusCache(ctx context.Context, repoID int64, branchName string) error { +func getCommitStatusCache(repoID int64, branchName string) *commitStatusCacheValue { + c := cache.GetCache() + statusStr, ok := c.Get(getCacheKey(repoID, branchName)).(string) + if ok && statusStr != "" { + var cv commitStatusCacheValue + err := json.Unmarshal([]byte(statusStr), &cv) + if err == nil && cv.State != "" { + return &cv + } + if err != nil { + log.Warn("getCommitStatusCache: json.Unmarshal failed: %v", err) + } + } + return nil +} + +func updateCommitStatusCache(repoID int64, branchName string, state api.CommitStatusState, targetURL string) error { + c := cache.GetCache() + bs, err := json.Marshal(commitStatusCacheValue{ + State: state.String(), + TargetURL: targetURL, + }) + if err != nil { + log.Warn("updateCommitStatusCache: json.Marshal failed: %v", err) + return nil + } + return c.Put(getCacheKey(repoID, branchName), string(bs), 3*24*60) +} + +func deleteCommitStatusCache(repoID int64, branchName string) error { c := cache.GetCache() return c.Delete(getCacheKey(repoID, branchName)) } @@ -59,13 +90,19 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato sha = commit.ID.String() } - if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ - Repo: repo, - Creator: creator, - SHA: commit.ID, - CommitStatus: status, + if err := db.WithTx(ctx, func(ctx context.Context) error { + if err := git_model.NewCommitStatus(ctx, git_model.NewCommitStatusOptions{ + Repo: repo, + Creator: creator, + SHA: commit.ID, + CommitStatus: status, + }); err != nil { + return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) + } + + return git_model.UpdateCommitStatusSummary(ctx, repo.ID, commit.ID.String()) }); err != nil { - return fmt.Errorf("NewCommitStatus[repo_id: %d, user_id: %d, sha: %s]: %w", repo.ID, creator.ID, sha, err) + return err } defaultBranchCommit, err := gitRepo.GetBranchCommit(repo.DefaultBranch) @@ -74,7 +111,7 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato } if commit.ID.String() == defaultBranchCommit.ID.String() { // since one commit status updated, the combined commit status should be invalid - if err := deleteCommitStatusCache(ctx, repo.ID, repo.DefaultBranch); err != nil { + if err := deleteCommitStatusCache(repo.ID, repo.DefaultBranch); err != nil { log.Error("deleteCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) } } @@ -91,12 +128,12 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato // FindReposLastestCommitStatuses loading repository default branch latest combinded commit status with cache func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Repository) ([]*git_model.CommitStatus, error) { results := make([]*git_model.CommitStatus, len(repos)) - c := cache.GetCache() - for i, repo := range repos { - status, ok := c.Get(getCacheKey(repo.ID, repo.DefaultBranch)).(string) - if ok && status != "" { - results[i] = &git_model.CommitStatus{State: api.CommitStatusState(status)} + if cv := getCommitStatusCache(repo.ID, repo.DefaultBranch); cv != nil { + results[i] = &git_model.CommitStatus{ + State: api.CommitStatusState(cv.State), + TargetURL: cv.TargetURL, + } } } @@ -114,8 +151,35 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep return nil, fmt.Errorf("FindBranchesByRepoAndBranchName: %v", err) } + var repoSHAs []git_model.RepoSHA + for id, sha := range repoIDsToLatestCommitSHAs { + repoSHAs = append(repoSHAs, git_model.RepoSHA{RepoID: id, SHA: sha}) + } + + summaryResults, err := git_model.GetLatestCommitStatusForRepoAndSHAs(ctx, repoSHAs) + if err != nil { + return nil, fmt.Errorf("GetLatestCommitStatusForRepoAndSHAs: %v", err) + } + + for _, summary := range summaryResults { + for i, repo := range repos { + if repo.ID == summary.RepoID { + results[i] = summary + _ = slices.DeleteFunc(repoSHAs, func(repoSHA git_model.RepoSHA) bool { + return repoSHA.RepoID == repo.ID + }) + if results[i].State != "" { + if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil { + log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) + } + } + break + } + } + } + // call the database O(1) times to get the commit statuses for all repos - repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoIDsToLatestCommitSHAs, db.ListOptionsAll) + repoToItsLatestCommitStatuses, err := git_model.GetLatestCommitStatusForPairs(ctx, repoSHAs) if err != nil { return nil, fmt.Errorf("GetLatestCommitStatusForPairs: %v", err) } @@ -123,8 +187,8 @@ func FindReposLastestCommitStatuses(ctx context.Context, repos []*repo_model.Rep for i, repo := range repos { if results[i] == nil { results[i] = git_model.CalcCommitStatus(repoToItsLatestCommitStatuses[repo.ID]) - if results[i] != nil { - if err := updateCommitStatusCache(ctx, repo.ID, repo.DefaultBranch, results[i].State); err != nil { + if results[i] != nil && results[i].State != "" { + if err := updateCommitStatusCache(repo.ID, repo.DefaultBranch, results[i].State, results[i].TargetURL); err != nil { log.Error("updateCommitStatusCache[%d:%s] failed: %v", repo.ID, repo.DefaultBranch, err) } } diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index 7c9f535ae0..516e7f1475 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -20,6 +20,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" + "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" api "code.gitea.io/gitea/modules/structs" @@ -108,8 +109,9 @@ func GetContributorStats(ctx context.Context, cache cache.Cache, repo *repo_mode switch v := cache.Get(cacheKey).(type) { case error: return nil, v - case map[string]*ContributorData: - return v, nil + case string: + var cachedStats map[string]*ContributorData + return cachedStats, json.Unmarshal([]byte(v), &cachedStats) default: return nil, fmt.Errorf("unexpected type in cache detected") } @@ -309,7 +311,16 @@ func generateContributorStats(genDone chan struct{}, cache cache.Cache, cacheKey total.TotalCommits++ } - _ = cache.Put(cacheKey, contributorsCommitStats, contributorStatsCacheTimeout) + data, err := json.Marshal(contributorsCommitStats) + if err != nil { + err := fmt.Errorf("couldn't marshal the data: %w", err) + _ = cache.Put(cacheKey, err, contributorStatsCacheTimeout) + return + } + + // Store the data as an string, to make it uniform what data type is returned + // from caches. + _ = cache.Put(cacheKey, string(data), contributorStatsCacheTimeout) generateLock.Delete(cacheKey) if genDone != nil { genDone <- struct{}{} diff --git a/services/repository/contributors_graph_test.go b/services/repository/contributors_graph_test.go index 3801a5eee4..2c6102005d 100644 --- a/services/repository/contributors_graph_test.go +++ b/services/repository/contributors_graph_test.go @@ -11,6 +11,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/json" "gitea.com/go-chi/cache" "github.com/stretchr/testify/assert" @@ -32,8 +33,14 @@ func TestRepository_ContributorsGraph(t *testing.T) { assert.ErrorAs(t, err, &git.ErrNotExist{}) generateContributorStats(nil, mockCache, "key2", repo, "master") - data, isData := mockCache.Get("key2").(map[string]*ContributorData) + dataString, isData := mockCache.Get("key2").(string) assert.True(t, isData) + // 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) + + var data map[string]*ContributorData + assert.NoError(t, json.Unmarshal([]byte(dataString), &data)) + var keys []string for k := range data { keys = append(keys, k) diff --git a/services/repository/delete.go b/services/repository/delete.go index 4ff42e627c..21d9ebcf0b 100644 --- a/services/repository/delete.go +++ b/services/repository/delete.go @@ -163,6 +163,7 @@ func DeleteRepositoryDirectly(ctx context.Context, doer *user_model.User, repoID &actions_model.ActionSchedule{RepoID: repoID}, &actions_model.ActionArtifact{RepoID: repoID}, &repo_model.RepoArchiveDownloadCount{RepoID: repoID}, + &actions_model.ActionRunnerToken{RepoID: repoID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } diff --git a/services/repository/files/cherry_pick.go b/services/repository/files/cherry_pick.go index 613b46d8f6..451a182155 100644 --- a/services/repository/files/cherry_pick.go +++ b/services/repository/files/cherry_pick.go @@ -28,7 +28,7 @@ func CherryPick(ctx context.Context, repo *repo_model.Repository, doer *user_mod t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { - log.Error("%v", err) + log.Error("NewTemporaryUploadRepository failed: %v", err) } defer t.Close() if err := t.Clone(opts.OldBranch, false); err != nil { diff --git a/services/repository/files/patch.go b/services/repository/files/patch.go index f6d5643dc9..e5f7e2af96 100644 --- a/services/repository/files/patch.go +++ b/services/repository/files/patch.go @@ -111,7 +111,7 @@ func ApplyDiffPatch(ctx context.Context, repo *repo_model.Repository, doer *user t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { - log.Error("%v", err) + log.Error("NewTemporaryUploadRepository failed: %v", err) } defer t.Close() if err := t.Clone(opts.OldBranch, true); err != nil { diff --git a/services/repository/files/temp_repo.go b/services/repository/files/temp_repo.go index 9fcd335c55..50b936cfa7 100644 --- a/services/repository/files/temp_repo.go +++ b/services/repository/files/temp_repo.go @@ -136,12 +136,19 @@ func (t *TemporaryUploadRepository) LsFiles(filenames ...string) ([]string, erro // RemoveFilesFromIndex removes the given files from the index func (t *TemporaryUploadRepository) RemoveFilesFromIndex(filenames ...string) error { + objectFormat, err := t.gitRepo.GetObjectFormat() + if err != nil { + return err + } + stdOut := new(bytes.Buffer) stdErr := new(bytes.Buffer) stdIn := new(bytes.Buffer) for _, file := range filenames { if file != "" { - stdIn.WriteString("0 0000000000000000000000000000000000000000\t") + stdIn.WriteString("0 ") + stdIn.WriteString(objectFormat.EmptyObjectID().String()) + stdIn.WriteByte('\t') stdIn.WriteString(file) stdIn.WriteByte('\000') } diff --git a/services/repository/files/temp_repo_test.go b/services/repository/files/temp_repo_test.go new file mode 100644 index 0000000000..2e31996c40 --- /dev/null +++ b/services/repository/files/temp_repo_test.go @@ -0,0 +1,28 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package files + +import ( + "testing" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/git" + + "github.com/stretchr/testify/assert" +) + +func TestRemoveFilesFromIndexSha256(t *testing.T) { + if git.CheckGitVersionAtLeast("2.42") != nil { + t.Skip("skipping because installed Git version doesn't support SHA256") + } + unittest.PrepareTestEnv(t) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + + temp, err := NewTemporaryUploadRepository(db.DefaultContext, repo) + assert.NoError(t, err) + assert.NoError(t, temp.Init("sha256")) + assert.NoError(t, temp.RemoveFilesFromIndex("README.md")) +} diff --git a/services/repository/files/update.go b/services/repository/files/update.go index a6c813f024..e677949e86 100644 --- a/services/repository/files/update.go +++ b/services/repository/files/update.go @@ -143,7 +143,7 @@ func ChangeRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use t, err := NewTemporaryUploadRepository(ctx, repo) if err != nil { - log.Error("%v", err) + log.Error("NewTemporaryUploadRepository failed: %v", err) } defer t.Close() hasOldBranch := true diff --git a/services/repository/fork.go b/services/repository/fork.go index f9c13a109e..5346d880f6 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -54,7 +54,7 @@ type ForkRepoOptions struct { // ForkRepository forks a repository func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts ForkRepoOptions) (*repo_model.Repository, error) { // Fork is prohibited, if user has reached maximum limit of repositories - if !owner.CanForkRepo() { + if !doer.IsAdmin && !owner.CanForkRepo() { return nil, repo_model.ErrReachLimitOfRepo{ Limit: owner.MaxRepoCreation, } diff --git a/services/repository/lfs_test.go b/services/repository/lfs_test.go index ee0b8f6b89..52ee05a147 100644 --- a/services/repository/lfs_test.go +++ b/services/repository/lfs_test.go @@ -28,9 +28,13 @@ func TestGarbageCollectLFSMetaObjects(t *testing.T) { err := storage.Init() assert.NoError(t, err) - repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1") + repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "lfs") assert.NoError(t, err) + validLFSObjects, err := db.GetEngine(db.DefaultContext).Count(git_model.LFSMetaObject{RepositoryID: repo.ID}) + assert.NoError(t, err) + assert.Greater(t, validLFSObjects, int64(1)) + // add lfs object lfsContent := []byte("gitea1") lfsOid := storeObjectInRepo(t, repo.ID, &lfsContent) @@ -39,13 +43,18 @@ func TestGarbageCollectLFSMetaObjects(t *testing.T) { err = repo_service.GarbageCollectLFSMetaObjects(context.Background(), repo_service.GarbageCollectLFSMetaObjectsOptions{ AutoFix: true, OlderThan: time.Now().Add(7 * 24 * time.Hour).Add(5 * 24 * time.Hour), - UpdatedLessRecentlyThan: time.Now().Add(7 * 24 * time.Hour).Add(3 * 24 * time.Hour), + UpdatedLessRecentlyThan: time.Time{}, // ensure that the models/fixtures/lfs_meta_object.yml objects are considered as well + LogDetail: t.Logf, }) assert.NoError(t, err) // lfs meta has been deleted _, err = git_model.GetLFSMetaObjectByOid(db.DefaultContext, repo.ID, lfsOid) assert.ErrorIs(t, err, git_model.ErrLFSObjectNotExist) + + remainingLFSObjects, err := db.GetEngine(db.DefaultContext).Count(git_model.LFSMetaObject{RepositoryID: repo.ID}) + assert.NoError(t, err) + assert.Equal(t, validLFSObjects-1, remainingLFSObjects) } func storeObjectInRepo(t *testing.T, repositoryID int64, content *[]byte) string { diff --git a/services/repository/push.go b/services/repository/push.go index 0aeb4c830b..51c3a935d1 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -166,7 +166,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { branch := opts.RefFullName.BranchName() if !opts.IsDelRef() { log.Trace("TriggerTask '%s/%s' by %s", repo.Name, branch, pusher.Name) - go pull_service.AddTestPullRequestTask(pusher, repo.ID, branch, true, opts.OldCommitID, opts.NewCommitID) + pull_service.AddTestPullRequestTask(ctx, pusher, repo.ID, branch, true, opts.OldCommitID, opts.NewCommitID, opts.TimeNano) newCommit, err := gitRepo.GetCommit(opts.NewCommitID) if err != nil { diff --git a/services/user/delete.go b/services/user/delete.go index 001b3e3002..e890990994 100644 --- a/services/user/delete.go +++ b/services/user/delete.go @@ -95,6 +95,7 @@ func deleteUser(ctx context.Context, u *user_model.User, purge bool) (err error) &actions_model.ActionRunner{OwnerID: u.ID}, &user_model.BlockedUser{BlockID: u.ID}, &user_model.BlockedUser{UserID: u.ID}, + &actions_model.ActionRunnerToken{OwnerID: u.ID}, ); err != nil { return fmt.Errorf("deleteBeans: %w", err) } diff --git a/services/webhook/deliver.go b/services/webhook/deliver.go index d488b1725a..8db1c72572 100644 --- a/services/webhook/deliver.go +++ b/services/webhook/deliver.go @@ -78,7 +78,13 @@ func Deliver(ctx context.Context, t *webhook_model.HookTask) error { } if authorization != "" { req.Header.Set("Authorization", authorization) - t.RequestInfo.Headers["Authorization"] = "******" + redacted := "******" + if strings.HasPrefix(authorization, "Bearer ") { + redacted = "Bearer " + redacted + } else if strings.HasPrefix(authorization, "Basic ") { + redacted = "Basic " + redacted + } + t.RequestInfo.Headers["Authorization"] = redacted } t.ResponseInfo = &webhook_model.HookResponse{ diff --git a/services/webhook/deliver_test.go b/services/webhook/deliver_test.go index c689332666..0311d810e6 100644 --- a/services/webhook/deliver_test.go +++ b/services/webhook/deliver_test.go @@ -132,7 +132,7 @@ func TestWebhookDeliverAuthorizationHeader(t *testing.T) { } assert.True(t, hookTask.IsSucceed) - assert.Equal(t, "******", hookTask.RequestInfo.Headers["Authorization"]) + assert.Equal(t, "Bearer ******", hookTask.RequestInfo.Headers["Authorization"]) } func TestWebhookDeliverHookTask(t *testing.T) { @@ -152,7 +152,6 @@ func TestWebhookDeliverHookTask(t *testing.T) { case "/webhook/6db5dc1e282529a8c162c7fe93dd2667494eeb51": // Version 2 - assert.Equal(t, "push", r.Header.Get("X-GitHub-Event")) assert.Equal(t, "application/json", r.Header.Get("Content-Type")) body, err := io.ReadAll(r.Body) assert.NoError(t, err) diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go index 697e33e94c..24d6cc6d99 100644 --- a/services/webhook/matrix.go +++ b/services/webhook/matrix.go @@ -42,16 +42,15 @@ func (matrixHandler) UnmarshalForm(bind func(any)) forms.WebhookForm { HomeserverURL string `binding:"Required;ValidUrl"` RoomID string `binding:"Required"` MessageType int - - // enforce requirement of authorization_header - // (value will still be set in the embedded WebhookCoreForm) - AuthorizationHeader string `binding:"Required"` + AccessToken string `binding:"Required"` } bind(&form) + form.AuthorizationHeader = "Bearer " + strings.TrimSpace(form.AccessToken) + // https://spec.matrix.org/v1.10/client-server-api/#sending-events-to-a-room return forms.WebhookForm{ WebhookCoreForm: form.WebhookCoreForm, - URL: fmt.Sprintf("%s/_matrix/client/r0/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)), + URL: fmt.Sprintf("%s/_matrix/client/v3/rooms/%s/send/m.room.message", form.HomeserverURL, url.PathEscape(form.RoomID)), ContentType: webhook_model.ContentTypeJSON, Secret: "", HTTPMethod: http.MethodPut, @@ -91,7 +90,7 @@ func (matrixHandler) NewRequest(ctx context.Context, w *webhook_model.Webhook, t } req.Header.Set("Content-Type", "application/json") - return req, body, shared.AddDefaultHeaders(req, []byte(w.Secret), t, body) // likely useless, but has always been sent historially + return req, body, nil } const matrixPayloadSizeLimit = 1024 * 64 diff --git a/services/webhook/matrix_test.go b/services/webhook/matrix_test.go index 0269ccccea..7031a45bec 100644 --- a/services/webhook/matrix_test.go +++ b/services/webhook/matrix_test.go @@ -201,7 +201,7 @@ func TestMatrixJSONPayload(t *testing.T) { RepoID: 3, IsActive: true, Type: webhook_module.MATRIX, - URL: "https://matrix.example.com/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message", + URL: "https://matrix.example.com/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message", Meta: `{"message_type":0}`, // text } task := &webhook_model.HookTask{ @@ -217,8 +217,7 @@ func TestMatrixJSONPayload(t *testing.T) { require.NoError(t, err) assert.Equal(t, "PUT", req.Method) - assert.Equal(t, "/_matrix/client/r0/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) - assert.Equal(t, "sha256=", req.Header.Get("X-Hub-Signature-256")) + assert.Equal(t, "/_matrix/client/v3/rooms/ROOM_ID/send/m.room.message/6db5dc1e282529a8c162c7fe93dd2667494eeb51", req.URL.Path) assert.Equal(t, "application/json", req.Header.Get("Content-Type")) var body MatrixPayload err = json.NewDecoder(req.Body).Decode(&body) diff --git a/services/webhook/notifier.go b/services/webhook/notifier.go index 03c5bede8b..a9b3422653 100644 --- a/services/webhook/notifier.go +++ b/services/webhook/notifier.go @@ -67,7 +67,7 @@ func (m *webhookNotifier) IssueClearLabels(ctx context.Context, doer *user_model err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{ Action: api.HookIssueLabelCleared, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -168,7 +168,7 @@ func (m *webhookNotifier) IssueChangeAssignee(ctx context.Context, doer *user_mo permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), } @@ -214,7 +214,7 @@ func (m *webhookNotifier) IssueChangeTitle(ctx context.Context, doer *user_model From: oldTitle, }, }, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -250,7 +250,7 @@ func (m *webhookNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode } else { apiIssue := &api.IssuePayload{ Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), CommitID: commitID, @@ -281,7 +281,7 @@ func (m *webhookNotifier) NewIssue(ctx context.Context, issue *issues_model.Issu if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssues, &api.IssuePayload{ Action: api.HookIssueOpened, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, issue.Poster, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, issue.Poster, nil), }); err != nil { @@ -349,7 +349,7 @@ func (m *webhookNotifier) IssueChangeContent(ctx context.Context, doer *user_mod From: oldContent, }, }, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -384,7 +384,7 @@ func (m *webhookNotifier) UpdateComment(ctx context.Context, doer *user_model.Us permission, _ := access_model.GetUserRepoPermission(ctx, c.Issue.Repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: c.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentEdited, - Issue: convert.ToAPIIssue(ctx, c.Issue), + Issue: convert.ToAPIIssue(ctx, doer, c.Issue), Comment: convert.ToAPIComment(ctx, c.Issue.Repo, c), Changes: &api.ChangesPayload{ Body: &api.ChangesFromPayload{ @@ -412,7 +412,7 @@ func (m *webhookNotifier) CreateIssueComment(ctx context.Context, doer *user_mod permission, _ := access_model.GetUserRepoPermission(ctx, repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentCreated, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Comment: convert.ToAPIComment(ctx, repo, comment), Repository: convert.ToRepo(ctx, repo, permission), Sender: convert.ToUser(ctx, doer, nil), @@ -449,7 +449,7 @@ func (m *webhookNotifier) DeleteComment(ctx context.Context, doer *user_model.Us permission, _ := access_model.GetUserRepoPermission(ctx, comment.Issue.Repo, doer) if err := PrepareWebhooks(ctx, EventSource{Repository: comment.Issue.Repo}, eventType, &api.IssueCommentPayload{ Action: api.HookIssueCommentDeleted, - Issue: convert.ToAPIIssue(ctx, comment.Issue), + Issue: convert.ToAPIIssue(ctx, doer, comment.Issue), Comment: convert.ToAPIComment(ctx, comment.Issue.Repo, comment), Repository: convert.ToRepo(ctx, comment.Issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), @@ -533,7 +533,7 @@ func (m *webhookNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueLabel, &api.IssuePayload{ Action: api.HookIssueLabelUpdated, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) @@ -575,7 +575,7 @@ func (m *webhookNotifier) IssueChangeMilestone(ctx context.Context, doer *user_m err = PrepareWebhooks(ctx, EventSource{Repository: issue.Repo}, webhook_module.HookEventIssueMilestone, &api.IssuePayload{ Action: hookAction, Index: issue.Index, - Issue: convert.ToAPIIssue(ctx, issue), + Issue: convert.ToAPIIssue(ctx, doer, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), Sender: convert.ToUser(ctx, doer, nil), }) diff --git a/services/webhook/slack.go b/services/webhook/slack.go index c835d59984..3af483a90e 100644 --- a/services/webhook/slack.go +++ b/services/webhook/slack.go @@ -356,7 +356,7 @@ var slackChannel = regexp.MustCompile(`^#?[a-z0-9_-]{1,80}$`) // IsValidSlackChannel validates a channel name conforms to what slack expects: // https://api.slack.com/methods/conversations.rename#naming // Conversation names can only contain lowercase letters, numbers, hyphens, and underscores, and must be 80 characters or less. -// Gitea accepts if it starts with a #. +// Forgejo accepts if it starts with a #. func IsValidSlackChannel(name string) bool { return slackChannel.MatchString(name) } diff --git a/services/webhook/sourcehut/builds.go b/services/webhook/sourcehut/builds.go index 1561b9e6e6..e7501110a2 100644 --- a/services/webhook/sourcehut/builds.go +++ b/services/webhook/sourcehut/builds.go @@ -49,6 +49,7 @@ type buildsForm struct { ManifestPath string `binding:"Required"` Visibility string `binding:"Required;In(PUBLIC,UNLISTED,PRIVATE)"` Secrets bool + AccessToken string `binding:"Required"` } var _ binding.Validator = &buildsForm{} @@ -63,13 +64,7 @@ func (f *buildsForm) Validate(req *http.Request, errs binding.Errors) binding.Er Message: ctx.Locale.TrString("repo.settings.add_webhook.invalid_path"), }) } - if !strings.HasPrefix(f.AuthorizationHeader, "Bearer ") { - errs = append(errs, binding.Error{ - FieldNames: []string{"AuthorizationHeader"}, - Classification: "", - Message: ctx.Locale.TrString("form.required_prefix", "Bearer "), - }) - } + f.AuthorizationHeader = "Bearer " + strings.TrimSpace(f.AccessToken) return errs } diff --git a/services/webhook/webhook.go b/services/webhook/webhook.go index dc68cae84d..cf4f2fdfd2 100644 --- a/services/webhook/webhook.go +++ b/services/webhook/webhook.go @@ -243,7 +243,7 @@ func PrepareWebhooks(ctx context.Context, source EventSource, event webhook_modu } // Add any admin-defined system webhooks - systemHooks, err := webhook_model.GetSystemWebhooks(ctx, optional.Some(true)) + systemHooks, err := webhook_model.GetSystemWebhooks(ctx, true) if err != nil { return fmt.Errorf("GetSystemWebhooks: %w", err) } diff --git a/services/wiki/wiki.go b/services/wiki/wiki.go index f2505e82cf..ec5d0fc9e2 100644 --- a/services/wiki/wiki.go +++ b/services/wiki/wiki.go @@ -209,7 +209,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model if isOldWikiExist { err := gitRepo.RemoveFilesFromIndex(oldWikiPath) if err != nil { - log.Error("%v", err) + log.Error("RemoveFilesFromIndex failed: %v", err) return err } } @@ -219,18 +219,18 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model objectHash, err := gitRepo.HashObject(strings.NewReader(content)) if err != nil { - log.Error("%v", err) + log.Error("HashObject failed: %v", err) return err } if err := gitRepo.AddObjectToIndex("100644", objectHash, newWikiPath); err != nil { - log.Error("%v", err) + log.Error("AddObjectToIndex failed: %v", err) return err } tree, err := gitRepo.WriteTree() if err != nil { - log.Error("%v", err) + log.Error("WriteTree failed: %v", err) return err } @@ -255,7 +255,7 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model commitHash, err := gitRepo.CommitTree(doer.NewGitSig(), committer, tree, commitTreeOpts) if err != nil { - log.Error("%v", err) + log.Error("CommitTree failed: %v", err) return err } @@ -270,11 +270,11 @@ func updateWikiPage(ctx context.Context, doer *user_model.User, repo *repo_model 0, ), }); err != nil { - log.Error("%v", err) + log.Error("Push failed: %v", err) if git.IsErrPushOutOfDate(err) || git.IsErrPushRejected(err) { return err } - return fmt.Errorf("Push: %w", err) + return fmt.Errorf("failed to push: %w", err) } return nil diff --git a/templates/admin/notice.tmpl b/templates/admin/notice.tmpl index 5ea003e5ec..33d8a2f963 100644 --- a/templates/admin/notice.tmpl +++ b/templates/admin/notice.tmpl @@ -62,10 +62,7 @@ {{template "admin/layout_footer" .}} diff --git a/templates/api/packages/pypi/simple.tmpl b/templates/api/packages/pypi/simple.tmpl index 77cb035600..181f8266e6 100644 --- a/templates/api/packages/pypi/simple.tmpl +++ b/templates/api/packages/pypi/simple.tmpl @@ -8,7 +8,7 @@ {{range .PackageDescriptors}} {{$p := .}} {{range .Files}} - {{.File.Name}}
    + {{.File.Name}}
    {{end}} {{end}} diff --git a/templates/devtest/label.tmpl b/templates/devtest/label.tmpl new file mode 100644 index 0000000000..c4b52a3e23 --- /dev/null +++ b/templates/devtest/label.tmpl @@ -0,0 +1,27 @@ +{{template "base/head" .}} + +
    +
    +

    Label

    +
    + simple label + red label + green label +
    +
    + basic label + basic red label + basic green label +
    +
    + long content must be in a non-flex "gt-ellipsis" element, otherwise it won't get ellipsis. very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label +
    +
    + very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label +
    +
    + very looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong label +
    +
    +
    +{{template "base/footer" .}} diff --git a/templates/explore/repo_list.tmpl b/templates/explore/repo_list.tmpl index a59235abc1..6381f1217e 100644 --- a/templates/explore/repo_list.tmpl +++ b/templates/explore/repo_list.tmpl @@ -38,15 +38,15 @@ {{end}} {{if not $.DisableStars}} - + {{svg "octicon-star" 16}} - {{if ge .NumStars 1000}}data-tooltip-content="{{.NumStars}}"{{end}}{{CountFmt .NumStars}} + {{CountFmt .NumStars}} {{end}} {{if not $.DisableForks}} - + {{svg "octicon-git-branch" 16}} - {{if ge .NumForks 1000}}data-tooltip-content="{{.NumForks}}"{{end}}{{CountFmt .NumForks}} + {{CountFmt .NumForks}} {{end}} diff --git a/templates/install.tmpl b/templates/install.tmpl index 6bb37d0a0c..d49de33a3f 100644 --- a/templates/install.tmpl +++ b/templates/install.tmpl @@ -150,7 +150,7 @@
    - +
    {{ctx.Locale.Tr "install.enable_update_checker_helper_forgejo"}}
    diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl index ec9cfece9a..80dde1c4d2 100644 --- a/templates/org/projects/list.tmpl +++ b/templates/org/projects/list.tmpl @@ -13,11 +13,9 @@
    {{template "shared/user/profile_big_avatar" .}}
    -
    -
    +
    {{template "user/overview/header" .}} -
    - {{template "projects/list" .}} + {{template "projects/list" .}}
    diff --git a/templates/org/projects/view.tmpl b/templates/org/projects/view.tmpl index 495204b06d..e1ab81c4cd 100644 --- a/templates/org/projects/view.tmpl +++ b/templates/org/projects/view.tmpl @@ -1,7 +1,7 @@ {{template "base/head" .}}
    {{template "shared/user/org_profile_avatar" .}} -
    +
    {{template "user/overview/header" .}}
    diff --git a/templates/package/content/nuget.tmpl b/templates/package/content/nuget.tmpl index fadeaffe10..ea665c7bbc 100644 --- a/templates/package/content/nuget.tmpl +++ b/templates/package/content/nuget.tmpl @@ -16,12 +16,11 @@
    - {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes}} + {{if or .PackageDescriptor.Metadata.Description .PackageDescriptor.Metadata.ReleaseNotes .PackageDescriptor.Metadata.Readme}}

    {{ctx.Locale.Tr "packages.about"}}

    -
    - {{if .PackageDescriptor.Metadata.Description}}{{.PackageDescriptor.Metadata.Description}}{{end}} - {{if .PackageDescriptor.Metadata.ReleaseNotes}}{{.PackageDescriptor.Metadata.ReleaseNotes}}{{end}} -
    + {{if .PackageDescriptor.Metadata.Description}}
    {{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Description}}
    {{end}} + {{if .PackageDescriptor.Metadata.Readme}}
    {{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.Readme}}
    {{end}} + {{if .PackageDescriptor.Metadata.ReleaseNotes}}
    {{RenderMarkdownToHtml $.Context .PackageDescriptor.Metadata.ReleaseNotes}}
    {{end}} {{end}} {{if .PackageDescriptor.Metadata.Dependencies}} diff --git a/templates/projects/view.tmpl b/templates/projects/view.tmpl index 33dd758c79..f9b85360e0 100644 --- a/templates/projects/view.tmpl +++ b/templates/projects/view.tmpl @@ -66,13 +66,13 @@
    {{range .Columns}} -
    +
    {{.NumIssues ctx}}
    - {{.Title}} + {{.Title}}
    {{if $canWriteProject}} {{end}}
    - -
    - +
    {{range (index $.IssuesMap .ID)}}
    diff --git a/templates/repo/actions/runs_list.tmpl b/templates/repo/actions/runs_list.tmpl index ac5049cf56..20330b5d62 100644 --- a/templates/repo/actions/runs_list.tmpl +++ b/templates/repo/actions/runs_list.tmpl @@ -1,4 +1,4 @@ -
    +
    {{if not .Runs}}
    {{svg "octicon-no-entry" 48}} @@ -28,14 +28,14 @@
    {{if .RefLink}} - {{.PrettyRef}} + {{.PrettyRef}} {{else}} - {{.PrettyRef}} + {{.PrettyRef}} {{end}} -
    -
    -
    {{svg "octicon-calendar" 16}}{{TimeSinceUnix .Updated ctx.Locale}}
    -
    {{svg "octicon-stopwatch" 16}}{{.Duration}}
    +
    +
    {{svg "octicon-calendar" 16}}{{TimeSinceUnix .Updated ctx.Locale}}
    +
    {{svg "octicon-stopwatch" 16}}{{.Duration}}
    +
    {{end}} diff --git a/templates/repo/blame.tmpl b/templates/repo/blame.tmpl index 45cf841748..01978dacf7 100644 --- a/templates/repo/blame.tmpl +++ b/templates/repo/blame.tmpl @@ -28,7 +28,7 @@
    -
    +
    {{if .IsFileTooLarge}} diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 49a0b445b1..7fec88cb79 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -18,10 +18,10 @@ {{end}} {{end}}
    -
    +

    {{RenderCommitMessage $.Context .Commit.Message ($.Repository.ComposeMetas ctx)}}{{template "repo/commit_statuses" dict "Status" .CommitStatus "Statuses" .CommitStatuses}}

    {{if not $.PageIsWiki}} -
    +
    {{ctx.Locale.Tr "repo.diff.browse_source"}} diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 2bc3597101..c8c695e332 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -13,17 +13,19 @@ {{$commitRepoLink := $.RepoLink}}{{if $.CommitRepoLink}}{{$commitRepoLink = $.CommitRepoLink}}{{end}} {{range .Commits}}
    -
    - {{$userName := .Author.Name}} - {{if .User}} - {{if and .User.FullName DefaultShowFullName}} - {{$userName = .User.FullName}} + +
    + {{$userName := .Author.Name}} + {{if .User}} + {{if and .User.FullName DefaultShowFullName}} + {{$userName = .User.FullName}} + {{end}} + {{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}{{$userName}} + {{else}} + {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}} + {{$userName}} {{end}} - {{ctx.AvatarUtils.Avatar .User 28 "tw-mr-2"}}{{$userName}} - {{else}} - {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 28 "tw-mr-2"}} - {{$userName}} - {{end}} +
    {{$class := "ui sha label"}} diff --git a/templates/repo/commits_list_small.tmpl b/templates/repo/commits_list_small.tmpl index d96b314d01..6ca6dd5cdc 100644 --- a/templates/repo/commits_list_small.tmpl +++ b/templates/repo/commits_list_small.tmpl @@ -6,14 +6,23 @@
    {{svg "octicon-git-commit"}} {{if .User}} - {{ctx.AvatarUtils.Avatar .User}} + {{ctx.AvatarUtils.Avatar .User 20}} {{else}} - {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name}} + {{ctx.AvatarUtils.AvatarByEmail .Author.Email .Author.Name 20}} {{end}} {{$commitLink:= printf "%s/commit/%s" $.comment.Issue.PullRequest.BaseRepo.Link (PathEscape .ID.String)}} - + {{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}} + + {{if IsMultilineCommitMessage .Message}} + + {{end}} + {{if IsMultilineCommitMessage .Message}} +
    {{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}
    + {{end}} + + {{template "repo/commit_statuses" dict "Status" .Status "Statuses" .Statuses}} {{$class := "ui sha label"}} {{if .Signature}} @@ -37,14 +46,6 @@ {{end}} - - {{RenderCommitMessageLinkSubject $.root.Context .Message $commitLink ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}} - {{if IsMultilineCommitMessage .Message}} - - {{end}} - {{if IsMultilineCommitMessage .Message}} -
    {{RenderCommitBody $.root.Context .Message ($.comment.Issue.PullRequest.BaseRepo.ComposeMetas ctx)}}
    - {{end}}
    {{end}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 9ede83dcb7..71154f9768 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -111,7 +111,7 @@ {{$isReviewFile := and $.IsSigned $.PageIsPullFiles (not $.IsArchived) $.IsShowingAllCommits}}

    -
    +
    - {{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}}{{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} - - {{if $file.IsGenerated}} - {{ctx.Locale.Tr "repo.diff.generated"}} - {{end}} - {{if $file.IsVendored}} - {{ctx.Locale.Tr "repo.diff.vendored"}} - {{end}} - {{if and $file.Mode $file.OldMode}} - {{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}} - {{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} - {{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}} - {{else if $file.Mode}} - {{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} - {{end}} + {{if $file.IsRenamed}}{{$file.OldName}} → {{end}}{{$file.Name}} + {{if .IsLFSFile}} ({{ctx.Locale.Tr "repo.stored_lfs"}}){{end}} + + {{if $file.IsGenerated}} + {{ctx.Locale.Tr "repo.diff.generated"}} + {{end}} + {{if $file.IsVendored}} + {{ctx.Locale.Tr "repo.diff.vendored"}} + {{end}} + {{if and $file.Mode $file.OldMode}} + {{$old := ctx.Locale.Tr ($file.ModeTranslationKey $file.OldMode)}} + {{$new := ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} + {{ctx.Locale.Tr "git.filemode.changed_filemode" $old $new}} + {{else if $file.Mode}} + {{ctx.Locale.Tr ($file.ModeTranslationKey $file.Mode)}} + {{end}} +
    {{if $showFileViewToggle}} diff --git a/templates/repo/editor/commit_form.tmpl b/templates/repo/editor/commit_form.tmpl index b2fe5b1cfb..9f81b1d3a0 100644 --- a/templates/repo/editor/commit_form.tmpl +++ b/templates/repo/editor/commit_form.tmpl @@ -9,7 +9,7 @@ {{ctx.Locale.Tr "repo.editor.commit_changes"}} {{- end}}

    - +
    diff --git a/templates/repo/editor/edit.tmpl b/templates/repo/editor/edit.tmpl index 1f5652f6b5..71cecf1514 100644 --- a/templates/repo/editor/edit.tmpl +++ b/templates/repo/editor/edit.tmpl @@ -15,7 +15,7 @@ {{range $i, $v := .TreeNames}} {{if eq $i $l}} - + {{svg "octicon-info"}} {{else}} {{$v}} diff --git a/templates/repo/editor/upload.tmpl b/templates/repo/editor/upload.tmpl index 0a7c49dae3..5725020406 100644 --- a/templates/repo/editor/upload.tmpl +++ b/templates/repo/editor/upload.tmpl @@ -13,7 +13,7 @@ {{range $i, $v := .TreeNames}} {{if eq $i $l}} - + {{svg "octicon-info"}} {{else}} {{$v}} diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index f10efa1e3d..bbc3b9c008 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -2,7 +2,7 @@ {{with .Repository}}
    -
    +
    {{template "repo/icon" .}}
    @@ -174,12 +174,14 @@ {{end}} {{if .Permission.IsAdmin}} + {{$highlightSettings := true}} {{if and .SignedUser.EnableRepoUnitHints (not (.Repository.AllUnitsEnabled ctx))}} + {{$highlightSettings = false}} {{svg "octicon-diff-added"}} {{ctx.Locale.Tr "repo.settings.units.add_more"}} {{end}} - + {{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}} {{end}} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index d02eb7d7a2..3c994af194 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -58,8 +58,11 @@
    {{end}} {{template "repo/sub_menu" .}} + {{$n := len .TreeNames}} + {{$l := Eval $n "-" 1}} + {{$isHomepage := (eq $n 0)}}
    -
    +
    {{template "repo/branch_dropdown" dict "root" . "ContainerClasses" "tw-mr-1"}} {{if and .CanCompareOrPull .IsViewBranch (not .Repository.IsArchived)}} {{$cmpBranch := ""}} @@ -74,9 +77,7 @@ {{end}} - {{$n := len .TreeNames}} - {{$l := Eval $n "-" 1}} - {{if eq $n 0}} + {{if $isHomepage}} {{ctx.Locale.Tr "repo.find_file.go_to_file"}} {{end}} @@ -100,20 +101,20 @@ {{end}} - {{if and (eq $n 0) (.Repository.IsTemplate)}} + {{if and $isHomepage (.Repository.IsTemplate)}} {{ctx.Locale.Tr "repo.use_template"}} {{end}} - {{if ne $n 0}} + {{if (not $isHomepage)}} {{StringUtils.EllipsisString .Repository.Name 30}} {{- range $i, $v := .TreeNames -}} / {{- if eq $i $l -}} - {{StringUtils.EllipsisString $v 30}} + {{$v}} {{- else -}} - {{$p := index $.Paths $i}}{{StringUtils.EllipsisString $v 30}} + {{$p := index $.Paths $i}}{{$v}} {{- end -}} {{- end -}} @@ -121,7 +122,7 @@
    - {{if eq $n 0}} + {{if $isHomepage}}
    {{template "repo/clone_buttons" .}}
    {{template "repo/cite/cite_modal" .}} {{end}} - {{if and (ne $n 0) (not .IsViewFile) (not .IsBlame)}} + {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}} {{svg "octicon-history" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.file_history"}} diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl index 5bae6fc6d5..c8e2fbe845 100644 --- a/templates/repo/issue/milestone_issues.tmpl +++ b/templates/repo/issue/milestone_issues.tmpl @@ -3,16 +3,16 @@ {{template "repo/header" .}}
    {{template "base/alert" .}} -
    +

    {{.Milestone.Name}}

    {{if not .Repository.IsArchived}} -
    +
    {{if or .CanWriteIssues .CanWritePulls}} {{if .Milestone.IsClosed}} - {{ctx.Locale.Tr "repo.milestones.open"}} + {{ctx.Locale.Tr "repo.milestones.open"}} {{else}} - {{ctx.Locale.Tr "repo.milestones.close"}} + {{ctx.Locale.Tr "repo.milestones.close"}} {{end}} {{ctx.Locale.Tr "repo.milestones.edit"}} diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl index bce7ad8717..2a5cf84b35 100644 --- a/templates/repo/issue/milestones.tmpl +++ b/templates/repo/issue/milestones.tmpl @@ -4,11 +4,13 @@
    {{template "base/alert" .}} -
    +
    {{template "repo/issue/navbar" .}} {{template "repo/issue/search" .}} {{if and (or .CanWriteIssues .CanWritePulls) (not .Repository.IsArchived)}} - {{ctx.Locale.Tr "repo.milestones.new"}} + {{end}}
    diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index a5fd02c190..e9e92db07d 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -177,11 +177,11 @@ {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}} - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) $createdStr}} {{else if and (not .AddedLabels) .RemovedLabels}} - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink) $createdStr}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context ctx.Locale .AddedLabels $.RepoLink .Issue.IsPull) (RenderLabels $.Context ctx.Locale .RemovedLabels $.RepoLink .Issue.IsPull) $createdStr}} {{end}}
    diff --git a/templates/repo/issue/view_content/reference_issue_dialog.tmpl b/templates/repo/issue/view_content/reference_issue_dialog.tmpl index 5f338f6768..c7a471f55e 100644 --- a/templates/repo/issue/view_content/reference_issue_dialog.tmpl +++ b/templates/repo/issue/view_content/reference_issue_dialog.tmpl @@ -2,29 +2,27 @@
    {{ctx.Locale.Tr "repo.issues.context.reference_issue"}}
    -
    -
    +
    + {{.CsrfTokenHtml}} -
    -
    - {{ctx.Locale.Tr "repository"}} - -
    -
    - {{ctx.Locale.Tr "repo.milestones.title"}} - -
    -
    - {{ctx.Locale.Tr "repo.issues.reference_issue.body"}} - -
    -
    - +
    + +
    +
    + + +
    +
    + + +
    +
    + +
    diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 1c103f09f0..cbea32d303 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -114,7 +114,7 @@
    {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .HasMerged) (not .Issue.IsClosed)}} -
    + -
    + {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .Repository.IsArchived)}} -
    +
    diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl index cfb3ec1d3d..bc25563d48 100644 --- a/templates/repo/pulse.tmpl +++ b/templates/repo/pulse.tmpl @@ -25,12 +25,14 @@
    {{if gt .Activity.ActivePRCount 0}}
    - - + {{if gt .Activity.MergedPRPerc 0}} + + {{end}} +
    {{else}}
    - +
    {{end}} {{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount}} @@ -40,8 +42,10 @@
    {{if gt .Activity.ActiveIssueCount 0}}
    - - + {{if gt .Activity.ClosedIssuePerc 0}} + + {{end}} +
    {{else}}
    @@ -108,7 +112,7 @@ {{end}} {{if gt .Activity.PublishedReleaseCount 0}} -

    +

    {{svg "octicon-tag" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.releases_published_by" (ctx.Locale.TrN .Activity.PublishedReleaseCount "repo.activity.title.releases_1" "repo.activity.title.releases_n" .Activity.PublishedReleaseCount) @@ -130,7 +134,7 @@ {{end}} {{if gt .Activity.MergedPRCount 0}} -

    +

    {{svg "octicon-git-pull-request" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.prs_merged_by" (ctx.Locale.TrN .Activity.MergedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.MergedPRCount) @@ -149,7 +153,7 @@ {{end}} {{if gt .Activity.OpenedPRCount 0}} -

    +

    {{svg "octicon-git-branch" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.prs_opened_by" (ctx.Locale.TrN .Activity.OpenedPRCount "repo.activity.title.prs_1" "repo.activity.title.prs_n" .Activity.OpenedPRCount) @@ -168,7 +172,7 @@ {{end}} {{if gt .Activity.ClosedIssueCount 0}} -

    +

    {{svg "octicon-issue-closed" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.issues_closed_from" (ctx.Locale.TrN .Activity.ClosedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.ClosedIssueCount) @@ -187,7 +191,7 @@ {{end}} {{if gt .Activity.OpenedIssueCount 0}} -

    +

    {{svg "octicon-issue-opened" 16 "tw-mr-2"}} {{ctx.Locale.Tr "repo.activity.title.issues_created_by" (ctx.Locale.TrN .Activity.OpenedIssueCount "repo.activity.title.issues_1" "repo.activity.title.issues_n" .Activity.OpenedIssueCount) @@ -206,7 +210,7 @@ {{end}} {{if gt .Activity.UnresolvedIssueCount 0}} -

    +

    {{svg "octicon-comment-discussion" 16 "tw-mr-2"}} {{ctx.Locale.TrN .Activity.UnresolvedIssueCount "repo.activity.title.unresolved_conv_1" "repo.activity.title.unresolved_conv_n" .Activity.UnresolvedIssueCount}}

    diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 2d38cacfcd..4755da091a 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -61,46 +61,49 @@
    {{$release.RenderedNote}}
    -
    -
    - - {{ctx.Locale.Tr "repo.release.downloads"}} - - -
    + {{$hasReleaseAttachment := gt (len $release.Attachments) 0}} + {{$hasArchiveLinks := and (not $.DisableDownloadSourceArchives) (not $release.IsDraft) (not $release.HideArchiveLinks) ($.Permission.CanRead $.UnitTypeCode)}} + {{if or $hasArchiveLinks $hasReleaseAttachment}} +
    +
    + + {{ctx.Locale.Tr "repo.release.downloads"}} + + +
    + {{end}}
    diff --git a/templates/repo/release/new.tmpl b/templates/repo/release/new.tmpl index 425cc2c87a..e1881b1d94 100644 --- a/templates/repo/release/new.tmpl +++ b/templates/repo/release/new.tmpl @@ -57,6 +57,7 @@ "TextareaPlaceholder" (ctx.Locale.Tr "repo.release.message") "TextareaAriaLabel" (ctx.Locale.Tr "repo.release.message") "DropzoneParentContainer" "form" + "EasyMDE" true )}}
    {{range .attachments}} @@ -97,6 +98,15 @@
    {{ctx.Locale.Tr "repo.release.prerelease_helper"}} + {{if not .DisableDownloadSourceArchives}} +
    +
    + + +
    +
    + {{ctx.Locale.Tr "repo.release.hide_archive_links_helper"}} + {{end}}
    {{if .PageIsEditRelease}} diff --git a/templates/repo/settings/lfs_file.tmpl b/templates/repo/settings/lfs_file.tmpl index 43afba96c3..5bcd2af5bf 100644 --- a/templates/repo/settings/lfs_file.tmpl +++ b/templates/repo/settings/lfs_file.tmpl @@ -11,7 +11,7 @@ {{ctx.Locale.Tr "repo.settings.lfs_findcommits"}}
    -
    +
    {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}}
    {{if .IsMarkup}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 50134cf8d7..a47fda3109 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -9,8 +9,8 @@ {{.CsrfTokenHtml}}
    - - + +
    @@ -180,7 +180,7 @@
    - {{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName false}} + {{$address := MirrorRemoteAddress $.Context .Repository .PullMirror.GetRemoteName}}
    @@ -563,8 +563,8 @@
    - - + +
    @@ -594,8 +594,8 @@
    - - + +
    @@ -626,8 +626,8 @@
    - - + +
    @@ -696,8 +696,8 @@
    - - + +
    @@ -729,8 +729,8 @@
    - - + +
    diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 2680b374cd..dfbc45dd61 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -11,13 +11,13 @@ {{end}} {{if not .ReadmeInList}} -
    -
    +
    +
    {{template "repo/latest_commit" .}}
    {{if .LatestCommit}} {{if .LatestCommit.Committer}} -
    +
    {{TimeSince .LatestCommit.Committer.When ctx.Locale}}
    {{end}} @@ -93,7 +93,7 @@ {{end}}
    -
    +
    {{if not (or .IsMarkup .IsRenderedHTML)}} {{template "repo/unicode_escape_prompt" dict "EscapeStatus" .EscapeStatus "root" $}} {{end}} diff --git a/templates/repo/view_list.tmpl b/templates/repo/view_list.tmpl index 7c463c50a6..fb257bd474 100644 --- a/templates/repo/view_list.tmpl +++ b/templates/repo/view_list.tmpl @@ -1,8 +1,12 @@ - diff --git a/templates/repo/wiki/new.tmpl b/templates/repo/wiki/new.tmpl index 2f51a5fe2e..81433db003 100644 --- a/templates/repo/wiki/new.tmpl +++ b/templates/repo/wiki/new.tmpl @@ -29,6 +29,7 @@ "TextareaPlaceholder" (ctx.Locale.Tr "repo.wiki.page_content") "TextareaAriaLabel" (ctx.Locale.Tr "repo.wiki.page_content") "TextareaContent" $content + "EasyMDE" true )}}
    diff --git a/templates/shared/combomarkdowneditor.tmpl b/templates/shared/combomarkdowneditor.tmpl index f4d51f510b..fb11a75bec 100644 --- a/templates/shared/combomarkdowneditor.tmpl +++ b/templates/shared/combomarkdowneditor.tmpl @@ -10,6 +10,7 @@ Template Attributes: * TextareaAriaLabel: aria-label attribute for the textarea * DropzoneParentContainer: container for file upload (leave it empty if no upload) * DisableAutosize: whether to disable automatic height resizing +* EasyMDE: whether to display button for switching to legacy editor */}}
    {{if .MarkdownPreviewUrl}} @@ -41,7 +42,9 @@ Template Attributes:
    - + {{if .EasyMDE}} + + {{end}}
    diff --git a/templates/shared/search/code/results.tmpl b/templates/shared/search/code/results.tmpl index 022192a150..a98a662654 100644 --- a/templates/shared/search/code/results.tmpl +++ b/templates/shared/search/code/results.tmpl @@ -24,10 +24,10 @@ {{else}} {{.Filename}} {{end}} - {{ctx.Locale.Tr "repo.diff.view_file"}} + {{ctx.Locale.Tr "repo.diff.view_file"}}
    - {{template "shared/searchfile" dict "RepoLink" $repo.Link "CodeIndexerDisabled" $.CodeIndexerDisabled "SearchResult" .}} + {{template "shared/searchfile" dict "RepoLink" $repo.Link "SearchResult" .}}
    {{template "shared/searchbottom" dict "root" $ "result" .}}
    diff --git a/templates/shared/search/code/search.tmpl b/templates/shared/search/code/search.tmpl index 4a9d905b9d..37a23dc3d6 100644 --- a/templates/shared/search/code/search.tmpl +++ b/templates/shared/search/code/search.tmpl @@ -1,9 +1,5 @@ - {{if not $.CodeIndexerDisabled}} - {{template "shared/search/combo_fuzzy" dict "Value" .Keyword "Disabled" .CodeIndexerUnavailable "IsFuzzy" .IsFuzzy "Placeholder" (ctx.Locale.Tr "search.code_kind")}} - {{else}} - {{template "shared/search/combo" dict "Value" .Keyword "Placeholder" (ctx.Locale.Tr "search.code_kind")}} - {{end}} + {{template "shared/search/combo_fuzzy" dict "Value" .Keyword "Disabled" .CodeIndexerUnavailable "IsFuzzy" .IsFuzzy "Placeholder" (ctx.Locale.Tr "search.code_kind")}}
    @@ -12,8 +8,8 @@

    {{ctx.Locale.Tr "search.code_search_unavailable"}}

    {{else}} - {{if not .CodeIndexerEnabled}} -
    + {{if .CodeIndexerDisabled}} +

    {{ctx.Locale.Tr "search.code_search_by_git_grep"}}

    {{end}} diff --git a/templates/shared/searchfile.tmpl b/templates/shared/searchfile.tmpl index f2c1369555..280584e4d1 100644 --- a/templates/shared/searchfile.tmpl +++ b/templates/shared/searchfile.tmpl @@ -4,7 +4,7 @@ {{range .SearchResult.Lines}}
    diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 19d3c0b992..de293b2170 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -5269,6 +5269,51 @@ } } }, + "/repos/{owner}/{repo}/compare/{basehead}": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "Get", + "commit", + "comparison" + ], + "summary": "Get commit comparison information", + "operationId": "information", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "compare two branches or commits", + "name": "basehead", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/Compare" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/contents": { "get": { "produces": [ @@ -19076,6 +19121,25 @@ }, "x-go-package": "code.gitea.io/gitea/modules/structs" }, + "Compare": { + "type": "object", + "title": "Compare represents a comparison between two commits.", + "properties": { + "commits": { + "type": "array", + "items": { + "$ref": "#/definitions/Commit" + }, + "x-go-name": "Commits" + }, + "total_commits": { + "type": "integer", + "format": "int64", + "x-go-name": "TotalCommits" + } + }, + "x-go-package": "code.gitea.io/gitea/modules/structs" + }, "ContentsResponse": { "description": "ContentsResponse contains information about a repo's entry's (dir, file, symlink, submodule) metadata and content", "type": "object", @@ -19885,6 +19949,10 @@ "type": "boolean", "x-go-name": "IsDraft" }, + "hide_archive_links": { + "type": "boolean", + "x-go-name": "HideArchiveLinks" + }, "name": { "type": "string", "x-go-name": "Title" @@ -20790,6 +20858,10 @@ "type": "boolean", "x-go-name": "IsDraft" }, + "hide_archive_links": { + "type": "boolean", + "x-go-name": "HideArchiveLinks" + }, "name": { "type": "string", "x-go-name": "Title" @@ -20894,6 +20966,11 @@ "external_wiki": { "$ref": "#/definitions/ExternalWiki" }, + "globally_editable_wiki": { + "description": "set the globally editable state of the wiki", + "type": "boolean", + "x-go-name": "GloballyEditableWiki" + }, "has_actions": { "description": "either `true` to enable actions unit, or `false` to disable them.", "type": "boolean", @@ -21043,10 +21120,6 @@ "EditUserOption": { "description": "EditUserOption edit user options", "type": "object", - "required": [ - "source_id", - "login_name" - ], "properties": { "active": { "type": "boolean", @@ -23561,6 +23634,10 @@ "type": "boolean", "x-go-name": "IsDraft" }, + "hide_archive_links": { + "type": "boolean", + "x-go-name": "HideArchiveLinks" + }, "html_url": { "type": "string", "x-go-name": "HTMLURL" @@ -23813,6 +23890,10 @@ "type": "string", "x-go-name": "FullName" }, + "globally_editable_wiki": { + "type": "boolean", + "x-go-name": "GloballyEditableWiki" + }, "has_actions": { "type": "boolean", "x-go-name": "HasActions" @@ -24667,6 +24748,12 @@ "type": "boolean", "x-go-name": "Restricted" }, + "source_id": { + "description": "The ID of the user's Authentication Source", + "type": "integer", + "format": "int64", + "x-go-name": "SourceID" + }, "starred_repos_count": { "type": "integer", "format": "int64", @@ -25147,6 +25234,12 @@ } } }, + "Compare": { + "description": "", + "schema": { + "$ref": "#/definitions/Compare" + } + }, "ContentsListResponse": { "description": "ContentsListResponse", "schema": { diff --git a/templates/user/dashboard/dashboard.tmpl b/templates/user/dashboard/dashboard.tmpl index d4553ea61b..030fd49940 100644 --- a/templates/user/dashboard/dashboard.tmpl +++ b/templates/user/dashboard/dashboard.tmpl @@ -1,15 +1,13 @@ {{template "base/head" .}}
    {{template "user/dashboard/navbar" .}} -
    +
    {{template "base/alert" .}} -
    -
    - {{template "user/heatmap" .}} - {{template "user/dashboard/feeds" .}} -
    - {{template "user/dashboard/repolist" .}} +
    + {{template "user/heatmap" .}} + {{template "user/dashboard/feeds" .}}
    + {{template "user/dashboard/repolist" .}}
    {{template "base/footer" .}} diff --git a/templates/user/dashboard/repolist.tmpl b/templates/user/dashboard/repolist.tmpl index 34f9b67f8e..2781f710ed 100644 --- a/templates/user/dashboard/repolist.tmpl +++ b/templates/user/dashboard/repolist.tmpl @@ -52,4 +52,4 @@ data.organizationId = {{.ContextUser.ID}}; window.config.pageData.dashboardRepoList = data; -
    +
    diff --git a/templates/user/notification/notification_div.tmpl b/templates/user/notification/notification_div.tmpl index 04e79ba749..99da119901 100644 --- a/templates/user/notification/notification_div.tmpl +++ b/templates/user/notification/notification_div.tmpl @@ -2,7 +2,7 @@
    {{$notificationUnreadCount := call .NotificationUnreadCount}}
    -
    diff --git a/templates/user/notification/notification_subscriptions.tmpl b/templates/user/notification/notification_subscriptions.tmpl index a5a965ca52..0a3ae99b40 100644 --- a/templates/user/notification/notification_subscriptions.tmpl +++ b/templates/user/notification/notification_subscriptions.tmpl @@ -1,15 +1,20 @@ {{template "base/head" .}}
    - + +
    + + + {{ctx.Locale.Tr "repo.settings.matrix.access_token_helper"}} +
    - + + {{ctx.Locale.Tr "repo.settings.matrix.room_id_helper" ("!opaque_id:example.org"|SafeHTML)}}
    @@ -21,5 +28,5 @@
    - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/msteams.tmpl b/templates/webhook/new/msteams.tmpl index f668342ac8..535d0fc3f1 100644 --- a/templates/webhook/new/msteams.tmpl +++ b/templates/webhook/new/msteams.tmpl @@ -5,5 +5,5 @@
    - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/packagist.tmpl b/templates/webhook/new/packagist.tmpl index 5cf6ed0012..04240bbf93 100644 --- a/templates/webhook/new/packagist.tmpl +++ b/templates/webhook/new/packagist.tmpl @@ -13,5 +13,5 @@
    - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/slack.tmpl b/templates/webhook/new/slack.tmpl index d24e659097..cfaeb41f20 100644 --- a/templates/webhook/new/slack.tmpl +++ b/templates/webhook/new/slack.tmpl @@ -22,5 +22,5 @@
    - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/sourcehut_builds.tmpl b/templates/webhook/new/sourcehut_builds.tmpl index 1d6333fe79..3bcbe1bf6e 100644 --- a/templates/webhook/new/sourcehut_builds.tmpl +++ b/templates/webhook/new/sourcehut_builds.tmpl @@ -2,12 +2,12 @@
    {{.CsrfTokenHtml}}
    - - + +
    - +
    @@ -29,5 +29,11 @@ {{ctx.Locale.Tr "repo.settings.sourcehut_builds.secrets_helper"}}
    - {{template "repo/settings/webhook/settings" .}} + +
    + + + {{ctx.Locale.Tr "repo.settings.sourcehut_builds.access_token_helper" "https://meta.sr.ht/oauth2/personal-token?grants=builds.sr.ht/JOBS:RW" "https://meta.sr.ht/oauth2/personal-token?grants=builds.sr.ht/JOBS:RW+builds.sr.ht/SECRETS:RO"}} +
    + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/telegram.tmpl b/templates/webhook/new/telegram.tmpl index ea677ca4c3..3627dff36a 100644 --- a/templates/webhook/new/telegram.tmpl +++ b/templates/webhook/new/telegram.tmpl @@ -13,5 +13,5 @@
    - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/webhook/new/wechatwork.tmpl b/templates/webhook/new/wechatwork.tmpl index 15050d71bd..ae9d36a7a6 100644 --- a/templates/webhook/new/wechatwork.tmpl +++ b/templates/webhook/new/wechatwork.tmpl @@ -5,5 +5,5 @@
    - {{template "repo/settings/webhook/settings" .}} + {{template "webhook/shared-settings" .}} diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/webhook/shared-settings.tmpl similarity index 94% rename from templates/repo/settings/webhook/settings.tmpl rename to templates/webhook/shared-settings.tmpl index 0a39643260..80caad7279 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/webhook/shared-settings.tmpl @@ -258,14 +258,15 @@ {{ctx.Locale.Tr "repo.settings.branch_filter_desc"}} - -
    - - - {{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}} +{{$skipAuthorizationHeader := or (eq .HookType "sourcehut_builds") (eq .HookType "matrix")}} +{{if not $skipAuthorizationHeader}} + +
    + + {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | SafeHTML)}} - {{end}} -
    +
    +{{end}}
    diff --git a/tests/e2e/debugserver_test.go b/tests/e2e/debugserver_test.go index f0f54665e1..b1496b22a1 100644 --- a/tests/e2e/debugserver_test.go +++ b/tests/e2e/debugserver_test.go @@ -10,6 +10,7 @@ package e2e import ( + "fmt" "net/url" "os" "os/signal" @@ -24,7 +25,7 @@ func TestDebugserver(t *testing.T) { signal.Notify(done, syscall.SIGINT, syscall.SIGTERM) onGiteaRun(t, func(*testing.T, *url.URL) { - println(setting.AppURL) + fmt.Println(setting.AppURL) <-done }) } diff --git a/tests/e2e/e2e_test.go b/tests/e2e/e2e_test.go index 304dac0f45..97f6844abc 100644 --- a/tests/e2e/e2e_test.go +++ b/tests/e2e/e2e_test.go @@ -60,8 +60,7 @@ func TestMain(m *testing.M) { exitVal := m.Run() if err := testlogger.WriterCloser.Reset(); err != nil { - fmt.Printf("testlogger.WriterCloser.Reset: %v\n", err) - os.Exit(1) + fmt.Printf("testlogger.WriterCloser.Reset: error ignored: %v\n", err) } if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil { fmt.Printf("util.RemoveAll: %v\n", err) @@ -92,6 +91,9 @@ func TestE2e(t *testing.T) { if _, set := os.LookupEnv("ACCEPT_VISUAL"); set { runArgs = append(runArgs, "--update-snapshots") } + if project := os.Getenv("PLAYWRIGHT_PROJECT"); project != "" { + runArgs = append(runArgs, "--project="+project) + } // Create new test for each input file for _, path := range paths { diff --git a/tests/integration/actions_commit_status_test.go b/tests/integration/actions_commit_status_test.go new file mode 100644 index 0000000000..3d191a283f --- /dev/null +++ b/tests/integration/actions_commit_status_test.go @@ -0,0 +1,48 @@ +// Copyright 20124 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/url" + "testing" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/actions" + "code.gitea.io/gitea/services/automerge" + + "github.com/stretchr/testify/assert" +) + +func TestActionsAutomerge(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + assert.True(t, setting.Actions.Enabled, "Actions should be enabled") + + ctx := db.DefaultContext + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) + job := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRunJob{ID: 292}) + + assert.False(t, pr.HasMerged, "PR should not be merged") + assert.Equal(t, issues_model.PullRequestStatusMergeable, pr.Status, "PR should be mergable") + + scheduled, err := automerge.ScheduleAutoMerge(ctx, user, pr, repo_model.MergeStyleMerge, "Dummy") + + assert.NoError(t, err, "PR should be scheduled for automerge") + assert.True(t, scheduled, "PR should be scheduled for automerge") + + actions.CreateCommitStatus(ctx, job) + + pr = unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) + + assert.True(t, pr.HasMerged, "PR should be merged") + }, + ) +} diff --git a/tests/integration/api_admin_test.go b/tests/integration/api_admin_test.go index e8954f5b20..0e97e606f9 100644 --- a/tests/integration/api_admin_test.go +++ b/tests/integration/api_admin_test.go @@ -196,19 +196,13 @@ func TestAPIEditUser(t *testing.T) { urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2") req := NewRequestWithValues(t, "PATCH", urlStr, map[string]string{ - // required - "login_name": "user2", - "source_id": "0", - // to change "full_name": "Full Name User 2", }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) empty := "" req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - LoginName: "user2", - SourceID: 0, - Email: &empty, + Email: &empty, }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusBadRequest) @@ -220,10 +214,6 @@ func TestAPIEditUser(t *testing.T) { assert.False(t, user2.IsRestricted) bTrue := true req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - // required - LoginName: "user2", - SourceID: 0, - // to change Restricted: &bTrue, }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) @@ -231,6 +221,45 @@ func TestAPIEditUser(t *testing.T) { assert.True(t, user2.IsRestricted) } +func TestAPIEditUserWithLoginName(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + adminUsername := "user1" + token := getUserToken(t, adminUsername, auth_model.AccessTokenScopeWriteAdmin) + urlStr := fmt.Sprintf("/api/v1/admin/users/%s", "user2") + + loginName := "user2" + loginSource := int64(0) + + t.Run("login_name only", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ + LoginName: &loginName, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("source_id only", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ + SourceID: &loginSource, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusUnprocessableEntity) + }) + + t.Run("login_name & source_id", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ + LoginName: &loginName, + SourceID: &loginSource, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + }) +} + func TestAPICreateRepoForUser(t *testing.T) { defer tests.PrepareTestEnv(t)() adminUsername := "user1" @@ -375,18 +404,14 @@ func TestAPIEditUser_NotAllowedEmailDomain(t *testing.T) { newEmail := "user2@example1.com" req := NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - LoginName: "user2", - SourceID: 0, - Email: &newEmail, + Email: &newEmail, }).AddTokenAuth(token) resp := MakeRequest(t, req, http.StatusOK) assert.Equal(t, "the domain of user email user2@example1.com conflicts with EMAIL_DOMAIN_ALLOWLIST or EMAIL_DOMAIN_BLOCKLIST", resp.Header().Get("X-Gitea-Warning")) originalEmail := "user2@example.com" req = NewRequestWithJSON(t, "PATCH", urlStr, api.EditUserOption{ - LoginName: "user2", - SourceID: 0, - Email: &originalEmail, + Email: &originalEmail, }).AddTokenAuth(token) MakeRequest(t, req, http.StatusOK) } diff --git a/tests/integration/api_comment_attachment_test.go b/tests/integration/api_comment_attachment_test.go index b6f3d3bc81..d4368d51fe 100644 --- a/tests/integration/api_comment_attachment_test.go +++ b/tests/integration/api_comment_attachment_test.go @@ -1,6 +1,5 @@ // Copyright 2021 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. +// SPDX-License-Identifier: MIT package integration diff --git a/tests/integration/api_fork_test.go b/tests/integration/api_fork_test.go index 87d2a10152..b80b4c6645 100644 --- a/tests/integration/api_fork_test.go +++ b/tests/integration/api_fork_test.go @@ -5,10 +5,14 @@ package integration import ( + "fmt" "net/http" "net/url" "testing" + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" @@ -16,6 +20,65 @@ import ( "code.gitea.io/gitea/tests" ) +func TestAPIForkAsAdminIgnoringLimits(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Repository.AllowForkWithoutMaximumLimit, false)() + defer test.MockVariableValue(&setting.Repository.MaxCreationLimit, 0)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + userSession := loginUser(t, user.Name) + userToken := getTokenForLoggedInUser(t, userSession, auth_model.AccessTokenScopeWriteRepository) + adminUser := unittest.AssertExistsAndLoadBean(t, &user_model.User{IsAdmin: true}) + adminSession := loginUser(t, adminUser.Name) + adminToken := getTokenForLoggedInUser(t, adminSession, + auth_model.AccessTokenScopeWriteRepository, + auth_model.AccessTokenScopeWriteOrganization) + + originForkURL := "/api/v1/repos/user12/repo10/forks" + orgName := "fork-org" + + // Create an organization + req := NewRequestWithJSON(t, "POST", "/api/v1/orgs", &api.CreateOrgOption{ + UserName: orgName, + }).AddTokenAuth(adminToken) + MakeRequest(t, req, http.StatusCreated) + + // Create a team + teamToCreate := &api.CreateTeamOption{ + Name: "testers", + IncludesAllRepositories: true, + Permission: "write", + Units: []string{"repo.code", "repo.issues"}, + } + + req = NewRequestWithJSON(t, "POST", fmt.Sprintf("/api/v1/orgs/%s/teams", orgName), &teamToCreate).AddTokenAuth(adminToken) + resp := MakeRequest(t, req, http.StatusCreated) + var team api.Team + DecodeJSON(t, resp, &team) + + // Add user2 to the team + req = NewRequestf(t, "PUT", "/api/v1/teams/%d/members/user2", team.ID).AddTokenAuth(adminToken) + MakeRequest(t, req, http.StatusNoContent) + + t.Run("forking as regular user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithJSON(t, "POST", originForkURL, &api.CreateForkOption{ + Organization: &orgName, + }).AddTokenAuth(userToken) + MakeRequest(t, req, http.StatusConflict) + }) + + t.Run("forking as an instance admin", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithJSON(t, "POST", originForkURL, &api.CreateForkOption{ + Organization: &orgName, + }).AddTokenAuth(adminToken) + MakeRequest(t, req, http.StatusAccepted) + }) +} + func TestCreateForkNoLogin(t *testing.T) { defer tests.PrepareTestEnv(t)() req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{}) diff --git a/tests/integration/api_issue_attachment_test.go b/tests/integration/api_issue_attachment_test.go index 375fe9ced8..b6a0cca6d5 100644 --- a/tests/integration/api_issue_attachment_test.go +++ b/tests/integration/api_issue_attachment_test.go @@ -1,6 +1,5 @@ // Copyright 2021 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. +// SPDX-License-Identifier: MIT package integration diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go index 869d90066a..55cce50c7b 100644 --- a/tests/integration/api_packages_cargo_test.go +++ b/tests/integration/api_packages_cargo_test.go @@ -1,6 +1,5 @@ // Copyright 2021 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. +// SPDX-License-Identifier: MIT package integration diff --git a/tests/integration/api_packages_pypi_test.go b/tests/integration/api_packages_pypi_test.go index a090b31e20..e973f6a52a 100644 --- a/tests/integration/api_packages_pypi_test.go +++ b/tests/integration/api_packages_pypi_test.go @@ -164,7 +164,7 @@ func TestPackagePyPI(t *testing.T) { nodes := htmlDoc.doc.Find("a").Nodes assert.Len(t, nodes, 2) - hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256-%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256)) + hrefMatcher := regexp.MustCompile(fmt.Sprintf(`%s/files/%s/%s/test\..+#sha256=%s`, root, regexp.QuoteMeta(packageName), regexp.QuoteMeta(packageVersion), hashSHA256)) for _, a := range nodes { for _, att := range a.Attr { diff --git a/tests/integration/api_releases_test.go b/tests/integration/api_releases_test.go index 5dc51044eb..0a392d0a15 100644 --- a/tests/integration/api_releases_test.go +++ b/tests/integration/api_releases_test.go @@ -133,14 +133,18 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { assert.Equal(t, newRelease.TagName, release.TagName) assert.Equal(t, newRelease.Title, release.Title) assert.Equal(t, newRelease.Note, release.Note) + assert.False(t, newRelease.HideArchiveLinks) + + hideArchiveLinks := true req = NewRequestWithJSON(t, "PATCH", urlStr, &api.EditReleaseOption{ - TagName: release.TagName, - Title: release.Title, - Note: "updated", - IsDraft: &release.IsDraft, - IsPrerelease: &release.IsPrerelease, - Target: release.Target, + TagName: release.TagName, + Title: release.Title, + Note: "updated", + IsDraft: &release.IsDraft, + IsPrerelease: &release.IsPrerelease, + Target: release.Target, + HideArchiveLinks: &hideArchiveLinks, }).AddTokenAuth(token) resp = MakeRequest(t, req, http.StatusOK) @@ -152,6 +156,7 @@ func TestAPICreateAndUpdateRelease(t *testing.T) { } unittest.AssertExistsAndLoadBean(t, rel) assert.EqualValues(t, rel.Note, newRelease.Note) + assert.True(t, newRelease.HideArchiveLinks) } func TestAPICreateReleaseToDefaultBranch(t *testing.T) { diff --git a/tests/integration/api_repo_compare_test.go b/tests/integration/api_repo_compare_test.go new file mode 100644 index 0000000000..f3188eb49f --- /dev/null +++ b/tests/integration/api_repo_compare_test.go @@ -0,0 +1,38 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "testing" + + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestAPICompareBranches(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + // Login as User2. + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + repoName := "repo20" + + req := NewRequestf(t, "GET", "/api/v1/repos/user2/%s/compare/add-csv...remove-files-b", repoName). + AddTokenAuth(token) + resp := MakeRequest(t, req, http.StatusOK) + + var apiResp *api.Compare + DecodeJSON(t, resp, &apiResp) + + assert.Equal(t, 2, apiResp.TotalCommits) + assert.Len(t, apiResp.Commits, 2) +} diff --git a/tests/integration/api_repo_test.go b/tests/integration/api_repo_test.go index 8ae2622976..2fb89cfa6e 100644 --- a/tests/integration/api_repo_test.go +++ b/tests/integration/api_repo_test.go @@ -701,3 +701,14 @@ func TestAPIRepoGetAssignees(t *testing.T) { DecodeJSON(t, resp, &assignees) assert.Len(t, assignees, 1) } + +func TestAPIViewRepoObjectFormat(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + var repo api.Repository + + req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1") + resp := MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &repo) + assert.EqualValues(t, "sha1", repo.ObjectFormatName) +} diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go index f776b35325..0e01b504cc 100644 --- a/tests/integration/api_user_search_test.go +++ b/tests/integration/api_user_search_test.go @@ -10,7 +10,9 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -57,6 +59,25 @@ func TestAPIUserSearchNotLoggedIn(t *testing.T) { } } +func TestAPIUserSearchPaged(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.API.DefaultPagingNum, 5)() + + req := NewRequest(t, "GET", "/api/v1/users/search?limit=1") + resp := MakeRequest(t, req, http.StatusOK) + + var limitedResults SearchResults + DecodeJSON(t, resp, &limitedResults) + assert.Len(t, limitedResults.Data, 1) + + req = NewRequest(t, "GET", "/api/v1/users/search") + resp = MakeRequest(t, req, http.StatusOK) + + var results SearchResults + DecodeJSON(t, resp, &results) + assert.Len(t, results.Data, 5) +} + func TestAPIUserSearchSystemUsers(t *testing.T) { defer tests.PrepareTestEnv(t)() for _, systemUser := range []*user_model.User{ diff --git a/tests/integration/api_wiki_test.go b/tests/integration/api_wiki_test.go index c61b4a061b..400cf068b4 100644 --- a/tests/integration/api_wiki_test.go +++ b/tests/integration/api_wiki_test.go @@ -15,6 +15,8 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" api "code.gitea.io/gitea/modules/structs" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -286,6 +288,63 @@ func TestAPIEditOtherWikiPage(t *testing.T) { testCreateWiki(http.StatusCreated) } +func TestAPISetWikiGlobalEditability(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + session := loginUser(t, user.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + // Create a new repository for testing purposes + repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{ + unit_model.TypeCode, + unit_model.TypeWiki, + }, nil, nil) + defer f() + urlStr := fmt.Sprintf("/api/v1/repos/%s", repo.FullName()) + + assertGlobalEditability := func(t *testing.T, editability bool) { + t.Helper() + + req := NewRequest(t, "GET", urlStr) + resp := MakeRequest(t, req, http.StatusOK) + + var opts api.Repository + DecodeJSON(t, resp, &opts) + + assert.Equal(t, opts.GloballyEditableWiki, editability) + } + + t.Run("api includes GloballyEditableWiki", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + assertGlobalEditability(t, false) + }) + + t.Run("api can turn on GloballyEditableWiki", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + globallyEditable := true + req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditRepoOption{ + GloballyEditableWiki: &globallyEditable, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + assertGlobalEditability(t, true) + }) + + t.Run("disabling the wiki disables GloballyEditableWiki", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + hasWiki := false + req := NewRequestWithJSON(t, "PATCH", urlStr, &api.EditRepoOption{ + HasWiki: &hasWiki, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + assertGlobalEditability(t, false) + }) +} + func TestAPIListPageRevisions(t *testing.T) { defer tests.PrepareTestEnv(t)() username := "user2" @@ -324,3 +383,28 @@ func TestAPIListPageRevisions(t *testing.T) { assert.Equal(t, dummyrevisions, revisions) } + +func TestAPIWikiNonMasterBranch(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) + repo, _, f := CreateDeclarativeRepoWithOptions(t, user, DeclarativeRepoOptions{ + WikiBranch: optional.Some("main"), + }) + defer f() + + uris := []string{ + "revisions/Home", + "pages", + "page/Home", + } + baseURL := fmt.Sprintf("/api/v1/repos/%s/wiki", repo.FullName()) + for _, uri := range uris { + t.Run(uri, func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestf(t, "GET", "%s/%s", baseURL, uri) + MakeRequest(t, req, http.StatusOK) + }) + } +} diff --git a/tests/integration/cmd_admin_test.go b/tests/integration/cmd_admin_test.go new file mode 100644 index 0000000000..6a85460450 --- /dev/null +++ b/tests/integration/cmd_admin_test.go @@ -0,0 +1,146 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/url" + "testing" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + + "github.com/stretchr/testify/assert" +) + +func Test_Cmd_AdminUser(t *testing.T) { + onGiteaRun(t, func(*testing.T, *url.URL) { + for _, testCase := range []struct { + name string + options []string + mustChangePassword bool + }{ + { + name: "default", + options: []string{}, + mustChangePassword: true, + }, + { + name: "--must-change-password=false", + options: []string{"--must-change-password=false"}, + mustChangePassword: false, + }, + { + name: "--must-change-password=true", + options: []string{"--must-change-password=true"}, + mustChangePassword: true, + }, + { + name: "--must-change-password", + options: []string{"--must-change-password"}, + mustChangePassword: true, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + name := "testuser" + + options := []string{"user", "create", "--username", name, "--password", "password", "--email", name + "@example.com"} + options = append(options, testCase.options...) + output, err := runMainApp("admin", options...) + assert.NoError(t, err) + assert.Contains(t, output, "has been successfully created") + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name}) + assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword) + + options = []string{"user", "change-password", "--username", name, "--password", "password"} + options = append(options, testCase.options...) + output, err = runMainApp("admin", options...) + assert.NoError(t, err) + assert.Contains(t, output, "has been successfully updated") + user = unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name}) + assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword) + + _, err = runMainApp("admin", "user", "delete", "--username", name) + assert.NoError(t, err) + unittest.AssertNotExistsBean(t, &user_model.User{Name: name}) + }) + } + }) +} + +func Test_Cmd_AdminFirstUser(t *testing.T) { + onGiteaRun(t, func(*testing.T, *url.URL) { + for _, testCase := range []struct { + name string + options []string + mustChangePassword bool + isAdmin bool + }{ + { + name: "default", + options: []string{}, + mustChangePassword: false, + isAdmin: false, + }, + { + name: "--must-change-password=false", + options: []string{"--must-change-password=false"}, + mustChangePassword: false, + isAdmin: false, + }, + { + name: "--must-change-password=true", + options: []string{"--must-change-password=true"}, + mustChangePassword: true, + isAdmin: false, + }, + { + name: "--must-change-password", + options: []string{"--must-change-password"}, + mustChangePassword: true, + isAdmin: false, + }, + { + name: "--admin default", + options: []string{"--admin"}, + mustChangePassword: false, + isAdmin: true, + }, + { + name: "--admin --must-change-password=false", + options: []string{"--admin", "--must-change-password=false"}, + mustChangePassword: false, + isAdmin: true, + }, + { + name: "--admin --must-change-password=true", + options: []string{"--admin", "--must-change-password=true"}, + mustChangePassword: true, + isAdmin: true, + }, + { + name: "--admin --must-change-password", + options: []string{"--admin", "--must-change-password"}, + mustChangePassword: true, + isAdmin: true, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + db.GetEngine(db.DefaultContext).Exec("DELETE FROM `user`") + db.GetEngine(db.DefaultContext).Exec("DELETE FROM `email_address`") + assert.Equal(t, int64(0), user_model.CountUsers(db.DefaultContext, nil)) + name := "testuser" + + options := []string{"user", "create", "--username", name, "--password", "password", "--email", name + "@example.com"} + options = append(options, testCase.options...) + output, err := runMainApp("admin", options...) + assert.NoError(t, err) + assert.Contains(t, output, "has been successfully created") + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: name}) + assert.Equal(t, testCase.mustChangePassword, user.MustChangePassword) + assert.Equal(t, testCase.isAdmin, user.IsAdmin) + }) + } + }) +} diff --git a/tests/integration/easymde_test.go b/tests/integration/easymde_test.go new file mode 100644 index 0000000000..c8203d36be --- /dev/null +++ b/tests/integration/easymde_test.go @@ -0,0 +1,25 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "net/http" + "testing" +) + +func TestEasyMDESwitch(t *testing.T) { + session := loginUser(t, "user2") + testEasyMDESwitch(t, session, "user2/glob/issues/1", false) + testEasyMDESwitch(t, session, "user2/glob/issues/new", false) + testEasyMDESwitch(t, session, "user2/glob/wiki?action=_new", true) + testEasyMDESwitch(t, session, "user2/glob/releases/new", true) +} + +func testEasyMDESwitch(t *testing.T, session *TestSession, url string, expected bool) { + t.Helper() + req := NewRequest(t, "GET", url) + resp := session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body) + doc.AssertElement(t, ".combo-markdown-editor button.markdown-switch-easymde", expected) +} diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index f2f312b8a4..ae0aea237b 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -187,6 +187,19 @@ func TestEditFileToNewBranch(t *testing.T) { }) } +func TestEditorAddTranslation(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + session := loginUser(t, "user2") + req := NewRequest(t, "GET", "/user2/repo1/_new/master") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + placeholder, ok := htmlDoc.Find("input[name='commit_summary']").Attr("placeholder") + assert.True(t, ok) + assert.EqualValues(t, `Add ""`, placeholder) +} + func TestCommitMail(t *testing.T) { onGiteaRun(t, func(t *testing.T, _ *url.URL) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) @@ -391,7 +404,7 @@ func TestCommitMail(t *testing.T) { t.Run("Upload", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - // Upload two seperate times, so we have two different 'uploads' that can + // Upload two separate times, so we have two different 'uploads' that can // be used indepently of each other. uploadFile := func(t *testing.T, name, content string) string { t.Helper() diff --git a/tests/integration/explore_code_test.go b/tests/integration/explore_code_test.go new file mode 100644 index 0000000000..cf3939b093 --- /dev/null +++ b/tests/integration/explore_code_test.go @@ -0,0 +1,25 @@ +package integration + +import ( + "net/http" + "testing" + + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestExploreCodeSearchIndexer(t *testing.T) { + defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Indexer.RepoIndexerEnabled, true)() + + req := NewRequest(t, "GET", "/explore/code") + resp := MakeRequest(t, req, http.StatusOK) + + doc := NewHTMLParser(t, resp.Body) + msg := doc.Find(".explore").Find(".ui.container").Find(".ui.message[data-test-tag=grep]") + + assert.EqualValues(t, 0, len(msg.Nodes)) +} diff --git a/tests/integration/git_push_test.go b/tests/integration/git_push_test.go index 0a35724807..838ee0ff79 100644 --- a/tests/integration/git_push_test.go +++ b/tests/integration/git_push_test.go @@ -7,12 +7,17 @@ import ( "fmt" "net/url" "testing" + "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/test" repo_service "code.gitea.io/gitea/services/repository" "github.com/stretchr/testify/assert" @@ -146,3 +151,90 @@ func runTestGitPush(t *testing.T, u *url.URL, gitOperation func(t *testing.T, gi require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID)) } + +func TestOptionsGitPush(t *testing.T) { + onGiteaRun(t, testOptionsGitPush) +} + +func testOptionsGitPush(t *testing.T, u *url.URL) { + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_service.CreateRepoOptions{ + Name: "repo-to-push", + Description: "test git push", + AutoInit: false, + DefaultBranch: "main", + IsPrivate: false, + }) + require.NoError(t, err) + require.NotEmpty(t, repo) + + gitPath := t.TempDir() + + doGitInitTestRepository(gitPath)(t) + + u.Path = repo.FullName() + ".git" + u.User = url.UserPassword(user.LowerName, userPassword) + doGitAddRemote(gitPath, "origin", u)(t) + + t.Run("Unknown push options are rejected", func(t *testing.T) { + logChecker, cleanup := test.NewLogChecker(log.DEFAULT, log.TRACE) + logChecker.Filter("unknown option").StopMark("Git push options validation") + defer cleanup() + branchName := "branch0" + doGitCreateBranch(gitPath, branchName)(t) + doGitPushTestRepositoryFail(gitPath, "origin", branchName, "-o", "repo.template=false", "-o", "uknownoption=randomvalue")(t) + logFiltered, logStopped := logChecker.Check(5 * time.Second) + assert.True(t, logStopped) + assert.True(t, logFiltered[0]) + }) + + t.Run("Owner sets private & template to true via push options", func(t *testing.T) { + branchName := "branch1" + doGitCreateBranch(gitPath, branchName)(t) + doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t) + repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, user.Name, "repo-to-push") + require.NoError(t, err) + require.True(t, repo.IsPrivate) + require.True(t, repo.IsTemplate) + }) + + t.Run("Owner sets private & template to false via push options", func(t *testing.T) { + branchName := "branch2" + doGitCreateBranch(gitPath, branchName)(t) + doGitPushTestRepository(gitPath, "origin", branchName, "-o", "repo.private=false", "-o", "repo.template=false")(t) + repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, user.Name, "repo-to-push") + require.NoError(t, err) + require.False(t, repo.IsPrivate) + require.False(t, repo.IsTemplate) + }) + + // create a collaborator with write access + collaborator := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 5}) + u.User = url.UserPassword(collaborator.LowerName, userPassword) + doGitAddRemote(gitPath, "collaborator", u)(t) + repo_module.AddCollaborator(db.DefaultContext, repo, collaborator) + + t.Run("Collaborator with write access is allowed to push", func(t *testing.T) { + branchName := "branch3" + doGitCreateBranch(gitPath, branchName)(t) + doGitPushTestRepository(gitPath, "collaborator", branchName)(t) + }) + + t.Run("Collaborator with write access fails to change private & template via push options", func(t *testing.T) { + logChecker, cleanup := test.NewLogChecker(log.DEFAULT, log.TRACE) + logChecker.Filter("permission denied for changing repo settings").StopMark("Git push options validation") + defer cleanup() + branchName := "branch4" + doGitCreateBranch(gitPath, branchName)(t) + doGitPushTestRepositoryFail(gitPath, "collaborator", branchName, "-o", "repo.private=true", "-o", "repo.template=true")(t) + repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, user.Name, "repo-to-push") + require.NoError(t, err) + require.False(t, repo.IsPrivate) + require.False(t, repo.IsTemplate) + logFiltered, logStopped := logChecker.Check(5 * time.Second) + assert.True(t, logStopped) + assert.True(t, logFiltered[0]) + }) + + require.NoError(t, repo_service.DeleteRepositoryDirectly(db.DefaultContext, user, repo.ID)) +} diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 708f9a820d..f833366568 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -1076,6 +1076,7 @@ func TestDataAsync_Issue29101(t *testing.T) { gitRepo, err := gitrepo.OpenRepository(db.DefaultContext, repo) assert.NoError(t, err) + defer gitRepo.Close() commit, err := gitRepo.GetCommit(sha) assert.NoError(t, err) diff --git a/tests/integration/incoming_email_test.go b/tests/integration/incoming_email_test.go index 1284833864..543e620dbf 100644 --- a/tests/integration/incoming_email_test.go +++ b/tests/integration/incoming_email_test.go @@ -76,14 +76,11 @@ func TestIncomingEmail(t *testing.T) { t.Run("Handler", func(t *testing.T) { t.Run("Reply", func(t *testing.T) { - t.Run("Comment", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + checkReply := func(t *testing.T, payload []byte, issue *issues_model.Issue, commentType issues_model.CommentType) { + t.Helper() handler := &incoming.ReplyHandler{} - payload, err := incoming_payload.CreateReferencePayload(issue) - assert.NoError(t, err) - assert.Error(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, nil, payload)) assert.NoError(t, handler.Handle(db.DefaultContext, &incoming.MailContent{}, user, payload)) @@ -101,7 +98,7 @@ func TestIncomingEmail(t *testing.T) { comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ IssueID: issue.ID, - Type: issues_model.CommentTypeComment, + Type: commentType, }) assert.NoError(t, err) assert.NotEmpty(t, comments) @@ -113,6 +110,14 @@ func TestIncomingEmail(t *testing.T) { attachment := comment.Attachments[0] assert.Equal(t, content.Attachments[0].Name, attachment.Name) assert.EqualValues(t, 4, attachment.Size) + } + t.Run("Issue", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + payload, err := incoming_payload.CreateReferencePayload(issue) + assert.NoError(t, err) + + checkReply(t, payload, issue, issues_model.CommentTypeComment) }) t.Run("CodeComment", func(t *testing.T) { @@ -121,33 +126,22 @@ func TestIncomingEmail(t *testing.T) { comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 6}) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) - handler := &incoming.ReplyHandler{} - content := &incoming.MailContent{ - Content: "code reply by mail", - Attachments: []*incoming.Attachment{ - { - Name: "attachment.txt", - Content: []byte("test"), - }, - }, - } + payload, err := incoming_payload.CreateReferencePayload(comment) + assert.NoError(t, err) + + checkReply(t, payload, issue, issues_model.CommentTypeCode) + }) + + t.Run("Comment", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + comment := unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: 2}) + issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: comment.IssueID}) payload, err := incoming_payload.CreateReferencePayload(comment) assert.NoError(t, err) - assert.NoError(t, handler.Handle(db.DefaultContext, content, user, payload)) - - comments, err := issues_model.FindComments(db.DefaultContext, &issues_model.FindCommentsOptions{ - IssueID: issue.ID, - Type: issues_model.CommentTypeCode, - }) - assert.NoError(t, err) - assert.NotEmpty(t, comments) - comment = comments[len(comments)-1] - assert.Equal(t, user.ID, comment.PosterID) - assert.Equal(t, content.Content, comment.Content) - assert.NoError(t, comment.LoadAttachments(db.DefaultContext)) - assert.Empty(t, comment.Attachments) + checkReply(t, payload, issue, issues_model.CommentTypeComment) }) }) diff --git a/tests/integration/integration_test.go b/tests/integration/integration_test.go index e147d6a21b..f5b87231f3 100644 --- a/tests/integration/integration_test.go +++ b/tests/integration/integration_test.go @@ -36,22 +36,26 @@ import ( "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/testlogger" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers" + "code.gitea.io/gitea/services/auth/source/remote" gitea_context "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" user_service "code.gitea.io/gitea/services/user" + wiki_service "code.gitea.io/gitea/services/wiki" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" gouuid "github.com/google/uuid" "github.com/markbates/goth" "github.com/markbates/goth/gothic" - goth_gitlab "github.com/markbates/goth/providers/gitlab" + goth_gitlab "github.com/markbates/goth/providers/github" + goth_github "github.com/markbates/goth/providers/gitlab" "github.com/santhosh-tekuri/jsonschema/v5" "github.com/stretchr/testify/assert" ) @@ -184,8 +188,7 @@ func TestMain(m *testing.M) { exitCode := m.Run() if err := testlogger.WriterCloser.Reset(); err != nil { - fmt.Printf("testlogger.WriterCloser.Reset: %v\n", err) - os.Exit(1) + fmt.Printf("testlogger.WriterCloser.Reset: error ignored: %v\n", err) } if err = util.RemoveAll(setting.Indexer.IssuePath); err != nil { @@ -337,6 +340,36 @@ func authSourcePayloadGitLabCustom(name string) map[string]string { return payload } +func authSourcePayloadGitHub(name string) map[string]string { + payload := authSourcePayloadOAuth2(name) + payload["oauth2_provider"] = "github" + return payload +} + +func authSourcePayloadGitHubCustom(name string) map[string]string { + payload := authSourcePayloadGitHub(name) + payload["oauth2_use_custom_url"] = "on" + payload["oauth2_auth_url"] = goth_github.AuthURL + payload["oauth2_token_url"] = goth_github.TokenURL + payload["oauth2_profile_url"] = goth_github.ProfileURL + return payload +} + +func createRemoteAuthSource(t *testing.T, name, url, matchingSource string) *auth.Source { + assert.NoError(t, auth.CreateSource(context.Background(), &auth.Source{ + Type: auth.Remote, + Name: name, + IsActive: true, + Cfg: &remote.Source{ + URL: url, + MatchingSource: matchingSource, + }, + })) + source, err := auth.GetSourceByName(context.Background(), name) + assert.NoError(t, err) + return source +} + func createUser(ctx context.Context, t testing.TB, user *user_model.User) func() { user.MustChangePassword = false user.LowerName = strings.ToLower(user.Name) @@ -622,7 +655,7 @@ func VerifyJSONSchema(t testing.TB, resp *httptest.ResponseRecorder, schemaFile schema, err := jsonschema.Compile(schemaFilePath) assert.NoError(t, err) - var data interface{} + var data any err = json.Unmarshal(resp.Body.Bytes(), &data) assert.NoError(t, err) @@ -653,15 +686,27 @@ func GetHTMLTitle(t testing.TB, session *TestSession, urlStr string) string { return doc.Find("head title").Text() } -func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, enabledUnits, disabledUnits []unit_model.Type, files []*files_service.ChangeRepoFile) (*repo_model.Repository, string, func()) { +type DeclarativeRepoOptions struct { + Name optional.Option[string] + EnabledUnits optional.Option[[]unit_model.Type] + DisabledUnits optional.Option[[]unit_model.Type] + Files optional.Option[[]*files_service.ChangeRepoFile] + WikiBranch optional.Option[string] +} + +func CreateDeclarativeRepoWithOptions(t *testing.T, owner *user_model.User, opts DeclarativeRepoOptions) (*repo_model.Repository, string, func()) { t.Helper() - repoName := name - if repoName == "" { + // Not using opts.Name.ValueOrDefault() here to avoid unnecessarily + // generating an UUID when a name is specified. + var repoName string + if opts.Name.Has() { + repoName = opts.Name.Value() + } else { repoName = gouuid.NewString() } - // Create a new repository + // Create the repository repo, err := repo_service.CreateRepository(db.DefaultContext, owner, owner, repo_service.CreateRepoOptions{ Name: repoName, Description: "Temporary Repo", @@ -674,21 +719,31 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en assert.NoError(t, err) assert.NotEmpty(t, repo) - if enabledUnits != nil || disabledUnits != nil { - units := make([]repo_model.RepoUnit, len(enabledUnits)) - for i, unitType := range enabledUnits { - units[i] = repo_model.RepoUnit{ + // Populate `enabledUnits` if we have any enabled. + var enabledUnits []repo_model.RepoUnit + if opts.EnabledUnits.Has() { + units := opts.EnabledUnits.Value() + enabledUnits = make([]repo_model.RepoUnit, len(units)) + + for i, unitType := range units { + enabledUnits[i] = repo_model.RepoUnit{ RepoID: repo.ID, Type: unitType, } } + } - err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, units, disabledUnits) + // Adjust the repo units according to our parameters. + if opts.EnabledUnits.Has() || opts.DisabledUnits.Has() { + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, enabledUnits, opts.DisabledUnits.ValueOrDefault(nil)) assert.NoError(t, err) } + // Add files, if any. var sha string - if len(files) > 0 { + if opts.Files.Has() { + files := opts.Files.Value() + resp, err := files_service.ChangeRepoFiles(git.DefaultContext, repo, owner, &files_service.ChangeRepoFilesOptions{ Files: files, Message: "add files", @@ -713,7 +768,46 @@ func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, en sha = resp.Commit.SHA } + // If there's a Wiki branch specified, create a wiki, and a default wiki page. + if opts.WikiBranch.Has() { + // Set the wiki branch in the database first + repo.WikiBranch = opts.WikiBranch.Value() + err := repo_model.UpdateRepositoryCols(db.DefaultContext, repo, "wiki_branch") + assert.NoError(t, err) + + // Initialize the wiki + err = wiki_service.InitWiki(db.DefaultContext, repo) + assert.NoError(t, err) + + // Add a new wiki page + err = wiki_service.AddWikiPage(db.DefaultContext, owner, repo, "Home", "Welcome to the wiki!", "Add a Home page") + assert.NoError(t, err) + } + + // Return the repo, the top commit, and a defer-able function to delete the + // repo. return repo, sha, func() { repo_service.DeleteRepository(db.DefaultContext, owner, repo, false) } } + +func CreateDeclarativeRepo(t *testing.T, owner *user_model.User, name string, enabledUnits, disabledUnits []unit_model.Type, files []*files_service.ChangeRepoFile) (*repo_model.Repository, string, func()) { + t.Helper() + + var opts DeclarativeRepoOptions + + if name != "" { + opts.Name = optional.Some(name) + } + if enabledUnits != nil { + opts.EnabledUnits = optional.Some(enabledUnits) + } + if disabledUnits != nil { + opts.DisabledUnits = optional.Some(disabledUnits) + } + if files != nil { + opts.Files = optional.Some(files) + } + + return CreateDeclarativeRepoWithOptions(t, owner, opts) +} diff --git a/tests/integration/issue_test.go b/tests/integration/issue_test.go index 84487a847a..49d1cbb016 100644 --- a/tests/integration/issue_test.go +++ b/tests/integration/issue_test.go @@ -307,6 +307,16 @@ func TestIssueCommentUpdate(t *testing.T) { comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) assert.Equal(t, modifiedContent, comment.Content) + + // make the comment empty + req = NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/comments/%d", "user2", "repo1", commentID), map[string]string{ + "_csrf": GetCSRF(t, session, issueURL), + "content": "", + }) + session.MakeRequest(t, req, http.StatusOK) + + comment = unittest.AssertExistsAndLoadBean(t, &issues_model.Comment{ID: commentID}) + assert.Equal(t, "", comment.Content) } func TestIssueReaction(t *testing.T) { diff --git a/tests/integration/lfs_view_test.go b/tests/integration/lfs_view_test.go index 1775fa629f..9dfcb3e698 100644 --- a/tests/integration/lfs_view_test.go +++ b/tests/integration/lfs_view_test.go @@ -80,4 +80,26 @@ func TestLFSRender(t *testing.T) { content := doc.Find("div.file-view").Text() assert.Contains(t, content, "Testing READMEs in LFS") }) + + t.Run("/settings/lfs/pointers", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // visit /user2/lfs/settings/lfs/pointer + req := NewRequest(t, "GET", "/user2/lfs/settings/lfs/pointers") + resp := session.MakeRequest(t, req, http.StatusOK) + + // follow the first link to /user2/lfs/settings/lfs/find?oid=.... + filesTable := NewHTMLParser(t, resp.Body).doc.Find("#lfs-files-table") + assert.Contains(t, filesTable.Text(), "Find commits") + lfsFind := filesTable.Find(`.primary.button[href^="/user2"]`) + assert.Greater(t, lfsFind.Length(), 0) + lfsFindPath, exists := lfsFind.First().Attr("href") + assert.True(t, exists) + + assert.Contains(t, lfsFindPath, "oid=") + req = NewRequest(t, "GET", lfsFindPath) + resp = session.MakeRequest(t, req, http.StatusOK) + doc := NewHTMLParser(t, resp.Body).doc + assert.Equal(t, 1, doc.Find(`.sha.label[href="/user2/lfs/commit/73cf03db6ece34e12bf91e8853dc58f678f2f82d"]`).Length(), "could not find link to commit") + }) } diff --git a/tests/integration/markup_external_test.go b/tests/integration/markup_external_test.go index 5f102f8d62..e50f5c1356 100644 --- a/tests/integration/markup_external_test.go +++ b/tests/integration/markup_external_test.go @@ -1,6 +1,5 @@ // Copyright 2022 The Gitea Authors. All rights reserved. -// Use of this source code is governed by a MIT-style -// license that can be found in the LICENSE file. +// SPDX-License-Identifier: MIT package integration diff --git a/tests/integration/pull_request_task_test.go b/tests/integration/pull_request_task_test.go new file mode 100644 index 0000000000..4366d97c39 --- /dev/null +++ b/tests/integration/pull_request_task_test.go @@ -0,0 +1,109 @@ +// Copyright 2024 The Forgejo Authors +// SPDX-License-Identifier: MIT + +package integration + +import ( + "context" + "testing" + "time" + + "code.gitea.io/gitea/models/db" + issues_model "code.gitea.io/gitea/models/issues" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/modules/timeutil" + pull_service "code.gitea.io/gitea/services/pull" + repo_service "code.gitea.io/gitea/services/repository" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestPullRequestSynchronized(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // unmerged pull request of user2/repo1 from branch2 to master + pull := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ID: 2}) + // tip of tests/gitea-repositories-meta/user2/repo1 branch2 + pull.HeadCommitID = "985f0301dba5e7b34be866819cd15ad3d8f508ee" + pull.LoadIssue(db.DefaultContext) + pull.Issue.Created = timeutil.TimeStampNanoNow() + issues_model.UpdateIssueCols(db.DefaultContext, pull.Issue, "created") + + require.Equal(t, pull.HeadRepoID, pull.BaseRepoID) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: pull.HeadRepoID}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + for _, testCase := range []struct { + name string + timeNano int64 + expected bool + }{ + { + name: "AddTestPullRequestTask process PR", + timeNano: int64(pull.Issue.Created), + expected: true, + }, + { + name: "AddTestPullRequestTask skip PR", + timeNano: 0, + expected: false, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + logChecker, cleanup := test.NewLogChecker(log.DEFAULT, log.TRACE) + logChecker.Filter("Updating PR").StopMark("TestPullRequest ") + defer cleanup() + + opt := &repo_module.PushUpdateOptions{ + PusherID: owner.ID, + PusherName: owner.Name, + RepoUserName: owner.Name, + RepoName: repo.Name, + RefFullName: git.RefName("refs/heads/branch2"), + OldCommitID: pull.HeadCommitID, + NewCommitID: pull.HeadCommitID, + TimeNano: testCase.timeNano, + } + require.NoError(t, repo_service.PushUpdate(opt)) + logFiltered, logStopped := logChecker.Check(5 * time.Second) + assert.True(t, logStopped) + assert.Equal(t, testCase.expected, logFiltered[0]) + }) + } + + for _, testCase := range []struct { + name string + olderThan int64 + expected bool + }{ + { + name: "TestPullRequest process PR", + olderThan: int64(pull.Issue.Created), + expected: true, + }, + { + name: "TestPullRequest skip PR", + olderThan: int64(pull.Issue.Created) - 1, + expected: false, + }, + } { + t.Run(testCase.name, func(t *testing.T) { + logChecker, cleanup := test.NewLogChecker(log.DEFAULT, log.TRACE) + logChecker.Filter("Updating PR").StopMark("TestPullRequest ") + defer cleanup() + + pull_service.TestPullRequest(context.Background(), owner, repo.ID, testCase.olderThan, "branch2", true, pull.HeadCommitID, pull.HeadCommitID) + logFiltered, logStopped := logChecker.Check(5 * time.Second) + assert.True(t, logStopped) + assert.Equal(t, testCase.expected, logFiltered[0]) + }) + } +} diff --git a/tests/integration/pull_status_test.go b/tests/integration/pull_status_test.go index 26c99e6445..bb7098e424 100644 --- a/tests/integration/pull_status_test.go +++ b/tests/integration/pull_status_test.go @@ -12,6 +12,9 @@ import ( "testing" auth_model "code.gitea.io/gitea/models/auth" + git_model "code.gitea.io/gitea/models/git" + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/models/unittest" api "code.gitea.io/gitea/modules/structs" "github.com/stretchr/testify/assert" @@ -90,6 +93,10 @@ func TestPullCreate_CommitStatus(t *testing.T) { assert.True(t, ok) assert.Contains(t, cls, statesIcons[status]) } + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerName: "user1", Name: "repo1"}) + css := unittest.AssertExistsAndLoadBean(t, &git_model.CommitStatusSummary{RepoID: repo1.ID, SHA: commitID}) + assert.EqualValues(t, api.CommitStatusWarning, css.State) }) } diff --git a/tests/integration/release_test.go b/tests/integration/release_test.go index acc8cb68c2..ad3c7bf325 100644 --- a/tests/integration/release_test.go +++ b/tests/integration/release_test.go @@ -9,15 +9,19 @@ import ( "testing" "time" + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/setting" + api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func createNewRelease(t *testing.T, session *TestSession, repoURL, tag, title string, preRelease, draft bool) { @@ -307,3 +311,34 @@ func TestDownloadReleaseAttachment(t *testing.T) { session := loginUser(t, "user2") session.MakeRequest(t, req, http.StatusOK) } + +func TestReleaseHideArchiveLinksUI(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + release := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{TagName: "v2.0"}) + + require.NoError(t, release.LoadAttributes(db.DefaultContext)) + + session := loginUser(t, release.Repo.OwnerName) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + + zipURL := fmt.Sprintf("%s/archive/%s.zip", release.Repo.Link(), release.TagName) + tarGzURL := fmt.Sprintf("%s/archive/%s.tar.gz", release.Repo.Link(), release.TagName) + + resp := session.MakeRequest(t, NewRequest(t, "GET", release.HTMLURL()), http.StatusOK) + body := resp.Body.String() + assert.Contains(t, body, zipURL) + assert.Contains(t, body, tarGzURL) + + hideArchiveLinks := true + + req := NewRequestWithJSON(t, "PATCH", release.APIURL(), &api.EditReleaseOption{ + HideArchiveLinks: &hideArchiveLinks, + }).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + resp = session.MakeRequest(t, NewRequest(t, "GET", release.HTMLURL()), http.StatusOK) + body = resp.Body.String() + assert.NotContains(t, body, zipURL) + assert.NotContains(t, body, tarGzURL) +} diff --git a/tests/integration/remote_test.go b/tests/integration/remote_test.go new file mode 100644 index 0000000000..d905f88a81 --- /dev/null +++ b/tests/integration/remote_test.go @@ -0,0 +1,205 @@ +// Copyright Earl Warren +// SPDX-License-Identifier: MIT + +package integration + +import ( + "context" + "fmt" + "net/http" + "testing" + + auth_model "code.gitea.io/gitea/models/auth" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/test" + remote_service "code.gitea.io/gitea/services/remote" + "code.gitea.io/gitea/tests" + + "github.com/markbates/goth" + "github.com/stretchr/testify/assert" +) + +func TestRemote_MaybePromoteUserSuccess(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + // + // OAuth2 authentication source GitLab + // + gitlabName := "gitlab" + _ = addAuthSource(t, authSourcePayloadGitLabCustom(gitlabName)) + // + // Remote authentication source matching the GitLab authentication source + // + remoteName := "remote" + remote := createRemoteAuthSource(t, remoteName, "http://mygitlab.eu", gitlabName) + + // + // Create a user as if it had previously been created by the remote + // authentication source. + // + gitlabUserID := "5678" + gitlabEmail := "gitlabuser@example.com" + userBeforeSignIn := &user_model.User{ + Name: "gitlabuser", + Type: user_model.UserTypeRemoteUser, + LoginType: auth_model.Remote, + LoginSource: remote.ID, + LoginName: gitlabUserID, + } + defer createUser(context.Background(), t, userBeforeSignIn)() + + // + // A request for user information sent to Goth will return a + // goth.User exactly matching the user created above. + // + defer mockCompleteUserAuth(func(res http.ResponseWriter, req *http.Request) (goth.User, error) { + return goth.User{ + Provider: gitlabName, + UserID: gitlabUserID, + Email: gitlabEmail, + }, nil + })() + req := NewRequest(t, "GET", fmt.Sprintf("/user/oauth2/%s/callback?code=XYZ&state=XYZ", gitlabName)) + resp := MakeRequest(t, req, http.StatusSeeOther) + assert.Equal(t, "/", test.RedirectURL(resp)) + userAfterSignIn := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userBeforeSignIn.ID}) + + // both are about the same user + assert.Equal(t, userAfterSignIn.ID, userBeforeSignIn.ID) + // the login time was updated, proof the login succeeded + assert.Greater(t, userAfterSignIn.LastLoginUnix, userBeforeSignIn.LastLoginUnix) + // the login type was promoted from Remote to OAuth2 + assert.Equal(t, userBeforeSignIn.LoginType, auth_model.Remote) + assert.Equal(t, userAfterSignIn.LoginType, auth_model.OAuth2) + // the OAuth2 email was used to set the missing user email + assert.Equal(t, userBeforeSignIn.Email, "") + assert.Equal(t, userAfterSignIn.Email, gitlabEmail) +} + +func TestRemote_MaybePromoteUserFail(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + ctx := context.Background() + // + // OAuth2 authentication source GitLab + // + gitlabName := "gitlab" + gitlabSource := addAuthSource(t, authSourcePayloadGitLabCustom(gitlabName)) + // + // Remote authentication source matching the GitLab authentication source + // + remoteName := "remote" + remoteSource := createRemoteAuthSource(t, remoteName, "http://mygitlab.eu", gitlabName) + + { + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, &auth_model.Source{}, "", "") + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonNotAuth2, reason) + } + + { + remoteSource.Type = auth_model.OAuth2 + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, remoteSource, "", "") + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonBadAuth2, reason) + remoteSource.Type = auth_model.Remote + } + + { + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, "unknownloginname", "") + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonLoginNameNotExists, reason) + } + + { + remoteUserID := "844" + remoteUser := &user_model.User{ + Name: "withmailuser", + Type: user_model.UserTypeRemoteUser, + LoginType: auth_model.Remote, + LoginSource: remoteSource.ID, + LoginName: remoteUserID, + Email: "some@example.com", + } + defer createUser(context.Background(), t, remoteUser)() + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonEmailIsSet, reason) + } + + { + remoteUserID := "7464" + nonexistentloginsource := int64(4344) + remoteUser := &user_model.User{ + Name: "badsourceuser", + Type: user_model.UserTypeRemoteUser, + LoginType: auth_model.Remote, + LoginSource: nonexistentloginsource, + LoginName: remoteUserID, + } + defer createUser(context.Background(), t, remoteUser)() + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonNoSource, reason) + } + + { + remoteUserID := "33335678" + remoteUser := &user_model.User{ + Name: "badremoteuser", + Type: user_model.UserTypeRemoteUser, + LoginType: auth_model.Remote, + LoginSource: gitlabSource.ID, + LoginName: remoteUserID, + } + defer createUser(context.Background(), t, remoteUser)() + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, "") + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonSourceWrongType, reason) + } + + { + unrelatedName := "unrelated" + unrelatedSource := addAuthSource(t, authSourcePayloadGitHubCustom(unrelatedName)) + assert.NotNil(t, unrelatedSource) + + remoteUserID := "488484" + remoteEmail := "4848484@example.com" + remoteUser := &user_model.User{ + Name: "unrelateduser", + Type: user_model.UserTypeRemoteUser, + LoginType: auth_model.Remote, + LoginSource: remoteSource.ID, + LoginName: remoteUserID, + } + defer createUser(context.Background(), t, remoteUser)() + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, unrelatedSource, remoteUserID, remoteEmail) + assert.NoError(t, err) + assert.False(t, promoted) + assert.Equal(t, remote_service.ReasonNoMatch, reason) + } + + { + remoteUserID := "5678" + remoteEmail := "gitlabuser@example.com" + remoteUser := &user_model.User{ + Name: "remoteuser", + Type: user_model.UserTypeRemoteUser, + LoginType: auth_model.Remote, + LoginSource: remoteSource.ID, + LoginName: remoteUserID, + } + defer createUser(context.Background(), t, remoteUser)() + promoted, reason, err := remote_service.MaybePromoteRemoteUser(ctx, gitlabSource, remoteUserID, remoteEmail) + assert.NoError(t, err) + assert.True(t, promoted) + assert.Equal(t, remote_service.ReasonPromoted, reason) + } +} diff --git a/tests/integration/rename_branch_test.go b/tests/integration/rename_branch_test.go index b037178f90..27be77f71a 100644 --- a/tests/integration/rename_branch_test.go +++ b/tests/integration/rename_branch_test.go @@ -5,6 +5,7 @@ package integration import ( "net/http" + "net/url" "testing" git_model "code.gitea.io/gitea/models/git" @@ -17,6 +18,10 @@ import ( ) func TestRenameBranch(t *testing.T) { + onGiteaRun(t, testRenameBranch) +} + +func testRenameBranch(t *testing.T, u *url.URL) { defer tests.PrepareTestEnv(t)() repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) @@ -49,6 +54,96 @@ func TestRenameBranch(t *testing.T) { assert.Equal(t, "main", repo1.DefaultBranch) }) + t.Run("Database syncronization", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{ + "_csrf": GetCSRF(t, session, "/user2/repo1/settings/branches"), + "from": "master", + "to": "main", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + // check new branch link + req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/main/README.md", nil) + session.MakeRequest(t, req, http.StatusOK) + + // check old branch link + req = NewRequestWithValues(t, "GET", "/user2/repo1/src/branch/master/README.md", nil) + resp := session.MakeRequest(t, req, http.StatusSeeOther) + location := resp.Header().Get("Location") + assert.Equal(t, "/user2/repo1/src/branch/main/README.md", location) + + // check db + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + assert.Equal(t, "main", repo1.DefaultBranch) + + // create branch1 + csrf := GetCSRF(t, session, "/user2/repo1/src/branch/main") + + req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{ + "_csrf": csrf, + "new_branch_name": "branch1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + branch1 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.Equal(t, "branch1", branch1.Name) + + // create branch2 + req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/_new/branch/main", map[string]string{ + "_csrf": csrf, + "new_branch_name": "branch2", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + branch2 := unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + assert.Equal(t, "branch2", branch2.Name) + + // rename branch2 to branch1 + req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{ + "_csrf": GetCSRF(t, session, "/user2/repo1/settings/branches"), + "from": "branch2", + "to": "branch1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + flashCookie := session.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "error") + + branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + assert.Equal(t, "branch2", branch2.Name) + branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.Equal(t, "branch1", branch1.Name) + + // delete branch1 + req = NewRequestWithValues(t, "POST", "/user2/repo1/branches/delete", map[string]string{ + "_csrf": GetCSRF(t, session, "/user2/repo1/settings/branches"), + "name": "branch1", + }) + session.MakeRequest(t, req, http.StatusOK) + branch2 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + assert.Equal(t, "branch2", branch2.Name) + branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.True(t, branch1.IsDeleted) // virtual deletion + + // rename branch2 to branch1 again + req = NewRequestWithValues(t, "POST", "/user2/repo1/settings/rename_branch", map[string]string{ + "_csrf": GetCSRF(t, session, "/user2/repo1/settings/branches"), + "from": "branch2", + "to": "branch1", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + flashCookie = session.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.Contains(t, flashCookie.Value, "success") + + unittest.AssertNotExistsBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch2"}) + branch1 = unittest.AssertExistsAndLoadBean(t, &git_model.Branch{RepoID: repo1.ID, Name: "branch1"}) + assert.Equal(t, "branch1", branch1.Name) + }) + t.Run("Protected branch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() diff --git a/tests/integration/repo_mergecommit_revert_test.go b/tests/integration/repo_mergecommit_revert_test.go index 7041861f11..eb75d45c15 100644 --- a/tests/integration/repo_mergecommit_revert_test.go +++ b/tests/integration/repo_mergecommit_revert_test.go @@ -1,3 +1,6 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + package integration import ( diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go index b6d2e24df6..e058851071 100644 --- a/tests/integration/repo_search_test.go +++ b/tests/integration/repo_search_test.go @@ -19,7 +19,7 @@ import ( ) func resultFilenames(t testing.TB, doc *HTMLDoc) []string { - filenameSelections := doc.doc.Find(".repository.search").Find(".repo-search-result").Find(".header").Find("span.file") + filenameSelections := doc.Find(".repository.search").Find(".repo-search-result").Find(".header").Find("span.file") result := make([]string, filenameSelections.Length()) filenameSelections.Each(func(i int, selection *goquery.Selection) { result[i] = selection.Text() @@ -46,7 +46,7 @@ func testSearchRepo(t *testing.T, indexer bool) { code_indexer.UpdateRepoIndexer(repo) } - testSearch(t, "/user2/repo1/search?q=Description&page=1", []string{"README.md"}) + testSearch(t, "/user2/repo1/search?q=Description&page=1", []string{"README.md"}, indexer) defer test.MockVariableValue(&setting.Indexer.IncludePatterns, setting.IndexerGlobFromString("**.txt"))() defer test.MockVariableValue(&setting.Indexer.ExcludePatterns, setting.IndexerGlobFromString("**/y/**"))() @@ -58,32 +58,36 @@ func testSearchRepo(t *testing.T, indexer bool) { code_indexer.UpdateRepoIndexer(repo) } - testSearch(t, "/user2/glob/search?q=loren&page=1", []string{"a.txt"}) - testSearch(t, "/user2/glob/search?q=loren&page=1&fuzzy=false", []string{"a.txt"}) + testSearch(t, "/user2/glob/search?q=loren&page=1", []string{"a.txt"}, indexer) + testSearch(t, "/user2/glob/search?q=loren&page=1&fuzzy=false", []string{"a.txt"}, indexer) if indexer { // fuzzy search: matches both file3 (x/b.txt) and file1 (a.txt) // when indexer is enabled - testSearch(t, "/user2/glob/search?q=file3&page=1", []string{"x/b.txt", "a.txt"}) - testSearch(t, "/user2/glob/search?q=file4&page=1", []string{"x/b.txt", "a.txt"}) - testSearch(t, "/user2/glob/search?q=file5&page=1", []string{"x/b.txt", "a.txt"}) + testSearch(t, "/user2/glob/search?q=file3&page=1", []string{"x/b.txt", "a.txt"}, indexer) + testSearch(t, "/user2/glob/search?q=file4&page=1", []string{"x/b.txt", "a.txt"}, indexer) + testSearch(t, "/user2/glob/search?q=file5&page=1", []string{"x/b.txt", "a.txt"}, indexer) } else { // fuzzy search: OR of all the keywords // when indexer is disabled - testSearch(t, "/user2/glob/search?q=file3+file1&page=1", []string{"a.txt", "x/b.txt"}) - testSearch(t, "/user2/glob/search?q=file4&page=1", []string{}) - testSearch(t, "/user2/glob/search?q=file5&page=1", []string{}) + testSearch(t, "/user2/glob/search?q=file3+file1&page=1", []string{"a.txt", "x/b.txt"}, indexer) + testSearch(t, "/user2/glob/search?q=file4&page=1", []string{}, indexer) + testSearch(t, "/user2/glob/search?q=file5&page=1", []string{}, indexer) } - testSearch(t, "/user2/glob/search?q=file3&page=1&fuzzy=false", []string{"x/b.txt"}) - testSearch(t, "/user2/glob/search?q=file4&page=1&fuzzy=false", []string{}) - testSearch(t, "/user2/glob/search?q=file5&page=1&fuzzy=false", []string{}) + testSearch(t, "/user2/glob/search?q=file3&page=1&fuzzy=false", []string{"x/b.txt"}, indexer) + testSearch(t, "/user2/glob/search?q=file4&page=1&fuzzy=false", []string{}, indexer) + testSearch(t, "/user2/glob/search?q=file5&page=1&fuzzy=false", []string{}, indexer) } -func testSearch(t *testing.T, url string, expected []string) { +func testSearch(t *testing.T, url string, expected []string, indexer bool) { req := NewRequest(t, "GET", url) resp := MakeRequest(t, req, http.StatusOK) - filenames := resultFilenames(t, NewHTMLParser(t, resp.Body)) + doc := NewHTMLParser(t, resp.Body) + msg := doc.Find(".repository").Find(".ui.container").Find(".ui.message[data-test-tag=grep]") + assert.EqualValues(t, indexer, len(msg.Nodes) == 0) + + filenames := resultFilenames(t, doc) assert.EqualValues(t, expected, filenames) } diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index 771aa9ad8e..de86cba77a 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -14,9 +14,11 @@ import ( unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" gitea_context "code.gitea.io/gitea/services/context" repo_service "code.gitea.io/gitea/services/repository" + user_service "code.gitea.io/gitea/services/user" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -32,6 +34,97 @@ func TestRepoSettingsUnits(t *testing.T) { session.MakeRequest(t, req, http.StatusOK) } +func TestRepoAddMoreUnitsHighlighting(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + session := loginUser(t, user.Name) + + // Make sure there are no disabled repos in the settings! + setting.Repository.DisabledRepoUnits = []string{} + unit_model.LoadUnitConfig() + + // Create a known-good repo, with some units disabled. + repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{ + unit_model.TypeCode, + unit_model.TypePullRequests, + unit_model.TypeProjects, + unit_model.TypeActions, + unit_model.TypeIssues, + unit_model.TypeWiki, + }, []unit_model.Type{unit_model.TypePackages}, nil) + defer f() + + setUserHints := func(t *testing.T, hints bool) func() { + saved := user.EnableRepoUnitHints + + assert.NoError(t, user_service.UpdateUser(db.DefaultContext, user, &user_service.UpdateOptions{ + EnableRepoUnitHints: optional.Some(hints), + })) + + return func() { + assert.NoError(t, user_service.UpdateUser(db.DefaultContext, user, &user_service.UpdateOptions{ + EnableRepoUnitHints: optional.Some(saved), + })) + } + } + + assertHighlight := func(t *testing.T, page, uri string, highlighted bool) { + t.Helper() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/settings%s", repo.Link(), page)) + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + htmlDoc.AssertElement(t, fmt.Sprintf(".overflow-menu-items a[href='%s'].active", fmt.Sprintf("%s/settings%s", repo.Link(), uri)), highlighted) + } + + t.Run("hints enabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer setUserHints(t, true)() + + t.Run("settings", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Visiting the /settings page, "Settings" is highlighted + assertHighlight(t, "", "", true) + // ...but "Add more" isn't. + assertHighlight(t, "", "/units", false) + }) + + t.Run("units", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Visiting the /settings/units page, "Add more" is highlighted + assertHighlight(t, "/units", "/units", true) + // ...but "Settings" isn't. + assertHighlight(t, "/units", "", false) + }) + }) + + t.Run("hints disabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer setUserHints(t, false)() + + t.Run("settings", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Visiting the /settings page, "Settings" is highlighted + assertHighlight(t, "", "", true) + // ...but "Add more" isn't (it doesn't exist). + assertHighlight(t, "", "/units", false) + }) + + t.Run("units", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Visiting the /settings/units page, "Settings" is highlighted + assertHighlight(t, "/units", "", true) + // ...but "Add more" isn't (it doesn't exist) + assertHighlight(t, "/units", "/units", false) + }) + }) +} + func TestRepoAddMoreUnits(t *testing.T) { defer tests.PrepareTestEnv(t)() user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) diff --git a/tests/integration/repo_webhook_test.go b/tests/integration/repo_webhook_test.go index 3375c0f1ed..b98fce0f4a 100644 --- a/tests/integration/repo_webhook_test.go +++ b/tests/integration/repo_webhook_test.go @@ -7,7 +7,6 @@ import ( "net/http" "net/http/httptest" "net/url" - "strings" "testing" gitea_context "code.gitea.io/gitea/services/context" @@ -37,30 +36,58 @@ func TestNewWebHookLink(t *testing.T) { for _, url := range tests { resp := session.MakeRequest(t, NewRequest(t, "GET", url), http.StatusOK) htmlDoc := NewHTMLParser(t, resp.Body) - menus := htmlDoc.doc.Find(".ui.top.attached.header .ui.dropdown .menu a") - menus.Each(func(i int, menu *goquery.Selection) { - url, exist := menu.Attr("href") - assert.True(t, exist) - assert.True(t, strings.HasPrefix(url, baseurl)) - }) - assert.Equal(t, webhooksLen, htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), "not all webhooks are listed in the 'new' dropdown") + assert.Equal(t, + webhooksLen, + htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), + "not all webhooks are listed in the 'new' dropdown") + csrfToken = htmlDoc.GetCSRF() } // ensure that the "failure" pages has the full dropdown as well resp := session.MakeRequest(t, NewRequestWithValues(t, "POST", baseurl+"/gitea/new", map[string]string{"_csrf": csrfToken}), http.StatusUnprocessableEntity) htmlDoc := NewHTMLParser(t, resp.Body) - assert.Equal(t, webhooksLen, htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), "not all webhooks are listed in the 'new' dropdown on failure") + assert.Equal(t, + webhooksLen, + htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), + "not all webhooks are listed in the 'new' dropdown on failure") resp = session.MakeRequest(t, NewRequestWithValues(t, "POST", baseurl+"/1", map[string]string{"_csrf": csrfToken}), http.StatusUnprocessableEntity) htmlDoc = NewHTMLParser(t, resp.Body) - assert.Equal(t, webhooksLen, htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), "not all webhooks are listed in the 'new' dropdown on failure") + assert.Equal(t, + webhooksLen, + htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), + "not all webhooks are listed in the 'new' dropdown on failure") + + adminSession := loginUser(t, "user1") + t.Run("org3", func(t *testing.T) { + baseurl := "/org/org3/settings/hooks" + resp := adminSession.MakeRequest(t, NewRequest(t, "GET", baseurl), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Equal(t, + webhooksLen, + htmlDoc.Find(`a[href^="`+baseurl+`/"][href$="/new"]`).Length(), + "not all webhooks are listed in the 'new' dropdown") + }) + t.Run("admin", func(t *testing.T) { + baseurl := "/admin/hooks" + resp := adminSession.MakeRequest(t, NewRequest(t, "GET", baseurl), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Equal(t, + webhooksLen, + htmlDoc.Find(`a[href^="/admin/default-hooks/"][href$="/new"]`).Length(), + "not all webhooks are listed in the 'new' dropdown for default-hooks") + assert.Equal(t, + webhooksLen, + htmlDoc.Find(`a[href^="/admin/system-hooks/"][href$="/new"]`).Length(), + "not all webhooks are listed in the 'new' dropdown for system-hooks") + }) } func TestWebhookForms(t *testing.T) { defer tests.PrepareTestEnv(t)() - session := loginUser(t, "user2") + session := loginUser(t, "user1") t.Run("forgejo/required", testWebhookForms("forgejo", session, map[string]string{ "payload_url": "https://forgejo.example.com", @@ -200,19 +227,19 @@ func TestWebhookForms(t *testing.T) { })) t.Run("matrix/required", testWebhookForms("matrix", session, map[string]string{ - "homeserver_url": "https://matrix.example.com", - "room_id": "123", - "authorization_header": "Bearer 123456", + "homeserver_url": "https://matrix.example.com", + "access_token": "123456", + "room_id": "123", }, map[string]string{ - "authorization_header": "", + "access_token": "", })) t.Run("matrix/optional", testWebhookForms("matrix", session, map[string]string{ "homeserver_url": "https://matrix.example.com", + "access_token": "123456", "room_id": "123", "message_type": "1", // m.text - "branch_filter": "matrix/*", - "authorization_header": "Bearer 123456", + "branch_filter": "matrix/*", })) t.Run("wechatwork/required", testWebhookForms("wechatwork", session, map[string]string{ @@ -240,14 +267,12 @@ func TestWebhookForms(t *testing.T) { })) t.Run("sourcehut_builds/required", testWebhookForms("sourcehut_builds", session, map[string]string{ - "payload_url": "https://sourcehut_builds.example.com", - "manifest_path": ".build.yml", - "visibility": "PRIVATE", - "authorization_header": "Bearer 123456", + "payload_url": "https://sourcehut_builds.example.com", + "manifest_path": ".build.yml", + "visibility": "PRIVATE", + "access_token": "123456", }, map[string]string{ - "authorization_header": "", - }, map[string]string{ - "authorization_header": "token ", + "access_token": "", }, map[string]string{ "manifest_path": "", }, map[string]string{ @@ -262,9 +287,9 @@ func TestWebhookForms(t *testing.T) { "manifest_path": ".build.yml", "visibility": "PRIVATE", "secrets": "on", + "access_token": "123456", - "branch_filter": "srht/*", - "authorization_header": "Bearer 123456", + "branch_filter": "srht/*", })) } @@ -272,7 +297,9 @@ func assertInput(t testing.TB, form *goquery.Selection, name string) string { t.Helper() input := form.Find(`input[name="` + name + `"]`) if input.Length() != 1 { - t.Log(form.Html()) + form.Find("input").Each(func(i int, s *goquery.Selection) { + t.Logf("found ", s.AttrOr("name", "")) + }) t.Errorf("field found %d times, expected once", name, input.Length()) } switch input.AttrOr("type", "") { @@ -288,99 +315,126 @@ func assertInput(t testing.TB, form *goquery.Selection, name string) string { func testWebhookForms(name string, session *TestSession, validFields map[string]string, invalidPatches ...map[string]string) func(t *testing.T) { return func(t *testing.T) { - // new webhook form - resp := session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/settings/hooks/"+name+"/new"), http.StatusOK) - htmlForm := NewHTMLParser(t, resp.Body).Find(`form[action^="/user2/repo1/settings/hooks/"]`) + t.Run("repo1", func(t *testing.T) { + testWebhookFormsShared(t, "/user2/repo1/settings/hooks", name, session, validFields, invalidPatches...) + }) + t.Run("org3", func(t *testing.T) { + testWebhookFormsShared(t, "/org/org3/settings/hooks", name, session, validFields, invalidPatches...) + }) + t.Run("system", func(t *testing.T) { + testWebhookFormsShared(t, "/admin/system-hooks", name, session, validFields, invalidPatches...) + }) + t.Run("default", func(t *testing.T) { + testWebhookFormsShared(t, "/admin/default-hooks", name, session, validFields, invalidPatches...) + }) + } +} - // fill the form - payload := map[string]string{ - "_csrf": htmlForm.Find(`input[name="_csrf"]`).AttrOr("value", ""), - "events": "send_everything", - } - for k, v := range validFields { - assertInput(t, htmlForm, k) - payload[k] = v - } - if t.Failed() { - t.FailNow() // prevent further execution if the form could not be filled properly - } +func testWebhookFormsShared(t *testing.T, endpoint, name string, session *TestSession, validFields map[string]string, invalidPatches ...map[string]string) { + // new webhook form + resp := session.MakeRequest(t, NewRequest(t, "GET", endpoint+"/"+name+"/new"), http.StatusOK) + htmlForm := NewHTMLParser(t, resp.Body).Find(`form[action^="` + endpoint + `/"]`) - // create the webhook (this redirects back to the hook list) - resp = session.MakeRequest(t, NewRequestWithValues(t, "POST", "/user2/repo1/settings/hooks/"+name+"/new", payload), http.StatusSeeOther) - assertHasFlashMessages(t, resp, "success") + // fill the form + payload := map[string]string{ + "_csrf": htmlForm.Find(`input[name="_csrf"]`).AttrOr("value", ""), + "events": "send_everything", + } + for k, v := range validFields { + assertInput(t, htmlForm, k) + payload[k] = v + } + if t.Failed() { + t.FailNow() // prevent further execution if the form could not be filled properly + } - // find last created hook in the hook list - // (a bit hacky, but the list should be sorted) - resp = session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/settings/hooks"), http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - editFormURL := htmlDoc.Find(`a[href^="/user2/repo1/settings/hooks/"]`).Last().AttrOr("href", "") - assert.NotEmpty(t, editFormURL) + // create the webhook (this redirects back to the hook list) + resp = session.MakeRequest(t, NewRequestWithValues(t, "POST", endpoint+"/"+name+"/new", payload), http.StatusSeeOther) + assertHasFlashMessages(t, resp, "success") + listEndpoint := resp.Header().Get("Location") + updateEndpoint := endpoint + "/" + if endpoint == "/admin/system-hooks" || endpoint == "/admin/default-hooks" { + updateEndpoint = "/admin/hooks/" + } - // edit webhook form - resp = session.MakeRequest(t, NewRequest(t, "GET", editFormURL), http.StatusOK) - htmlForm = NewHTMLParser(t, resp.Body).Find(`form[action^="/user2/repo1/settings/hooks/"]`) - editPostURL := htmlForm.AttrOr("action", "") - assert.NotEmpty(t, editPostURL) + // find last created hook in the hook list + // (a bit hacky, but the list should be sorted) + resp = session.MakeRequest(t, NewRequest(t, "GET", listEndpoint), http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + selector := `a[href^="` + updateEndpoint + `"]` + if endpoint == "/admin/system-hooks" { + // system-hooks and default-hooks are listed on the same page + // add a specifier to select the latest system-hooks + // (the default-hooks are at the end, so no further specifier needed) + selector = `.admin-setting-content > div:first-of-type ` + selector + } + editFormURL := htmlDoc.Find(selector).Last().AttrOr("href", "") + assert.NotEmpty(t, editFormURL) - // fill the form - payload = map[string]string{ - "_csrf": htmlForm.Find(`input[name="_csrf"]`).AttrOr("value", ""), - "events": "push_only", - } - for k, v := range validFields { - assert.Equal(t, v, assertInput(t, htmlForm, k), "input %q did not contain value %q", k, v) - payload[k] = v - } + // edit webhook form + resp = session.MakeRequest(t, NewRequest(t, "GET", editFormURL), http.StatusOK) + htmlForm = NewHTMLParser(t, resp.Body).Find(`form[action^="` + updateEndpoint + `"]`) + editPostURL := htmlForm.AttrOr("action", "") + assert.NotEmpty(t, editPostURL) - // update the webhook - resp = session.MakeRequest(t, NewRequestWithValues(t, "POST", editPostURL, payload), http.StatusSeeOther) - assertHasFlashMessages(t, resp, "success") + // fill the form + payload = map[string]string{ + "_csrf": htmlForm.Find(`input[name="_csrf"]`).AttrOr("value", ""), + "events": "push_only", + } + for k, v := range validFields { + assert.Equal(t, v, assertInput(t, htmlForm, k), "input %q did not contain value %q", k, v) + payload[k] = v + } - // check the updated webhook - resp = session.MakeRequest(t, NewRequest(t, "GET", editFormURL), http.StatusOK) - htmlForm = NewHTMLParser(t, resp.Body).Find(`form[action^="/user2/repo1/settings/hooks/"]`) - for k, v := range validFields { - assert.Equal(t, v, assertInput(t, htmlForm, k), "input %q did not contain value %q", k, v) - } + // update the webhook + resp = session.MakeRequest(t, NewRequestWithValues(t, "POST", editPostURL, payload), http.StatusSeeOther) + assertHasFlashMessages(t, resp, "success") - if len(invalidPatches) > 0 { - // check that invalid fields are rejected - resp := session.MakeRequest(t, NewRequest(t, "GET", "/user2/repo1/settings/hooks/"+name+"/new"), http.StatusOK) - htmlForm := NewHTMLParser(t, resp.Body).Find(`form[action^="/user2/repo1/settings/hooks/"]`) + // check the updated webhook + resp = session.MakeRequest(t, NewRequest(t, "GET", editFormURL), http.StatusOK) + htmlForm = NewHTMLParser(t, resp.Body).Find(`form[action^="` + updateEndpoint + `"]`) + for k, v := range validFields { + assert.Equal(t, v, assertInput(t, htmlForm, k), "input %q did not contain value %q", k, v) + } - for _, invalidPatch := range invalidPatches { - t.Run("invalid", func(t *testing.T) { - // fill the form - payload := map[string]string{ - "_csrf": htmlForm.Find(`input[name="_csrf"]`).AttrOr("value", ""), - "events": "send_everything", - } - for k, v := range validFields { + if len(invalidPatches) > 0 { + // check that invalid fields are rejected + resp := session.MakeRequest(t, NewRequest(t, "GET", endpoint+"/"+name+"/new"), http.StatusOK) + htmlForm := NewHTMLParser(t, resp.Body).Find(`form[action^="` + endpoint + `/"]`) + + for _, invalidPatch := range invalidPatches { + t.Run("invalid", func(t *testing.T) { + // fill the form + payload := map[string]string{ + "_csrf": htmlForm.Find(`input[name="_csrf"]`).AttrOr("value", ""), + "events": "send_everything", + } + for k, v := range validFields { + payload[k] = v + } + for k, v := range invalidPatch { + if v == "" { + delete(payload, k) + } else { payload[k] = v } - for k, v := range invalidPatch { - if v == "" { - delete(payload, k) - } else { - payload[k] = v - } - } + } - resp := session.MakeRequest(t, NewRequestWithValues(t, "POST", "/user2/repo1/settings/hooks/"+name+"/new", payload), http.StatusUnprocessableEntity) - // check that the invalid form is pre-filled - htmlForm = NewHTMLParser(t, resp.Body).Find(`form[action^="/user2/repo1/settings/hooks/"]`) - for k, v := range payload { - if k == "_csrf" || k == "events" || v == "" { - // the 'events' is a radio input, which is buggy below - continue - } - assert.Equal(t, v, assertInput(t, htmlForm, k), "input %q did not contain value %q", k, v) + resp := session.MakeRequest(t, NewRequestWithValues(t, "POST", endpoint+"/"+name+"/new", payload), http.StatusUnprocessableEntity) + // check that the invalid form is pre-filled + htmlForm = NewHTMLParser(t, resp.Body).Find(`form[action^="` + endpoint + `/"]`) + for k, v := range payload { + if k == "_csrf" || k == "events" || v == "" { + // the 'events' is a radio input, which is buggy below + continue } - if t.Failed() { - t.Log(invalidPatch) - } - }) - } + assert.Equal(t, v, assertInput(t, htmlForm, k), "input %q did not contain value %q", k, v) + } + if t.Failed() { + t.Log(invalidPatch) + } + }) } } } diff --git a/tests/integration/size_translations_test.go b/tests/integration/size_translations_test.go index 78cd16795d..1ee5f7b36f 100644 --- a/tests/integration/size_translations_test.go +++ b/tests/integration/size_translations_test.go @@ -89,7 +89,7 @@ func TestDataSizeTranslation(t *testing.T) { fullSize = noDigits.ReplaceAllString(fullSize, "") assert.Equal(t, "git: КиБ; lfs: Б", fullSize) - // Check if file sizes are correclty translated + // Check if file sizes are correctly translated testFileSizeTranslated(t, session, path.Join(testUser, testRepoName, "src/branch/main/137byteFile.txt"), "137 Б") testFileSizeTranslated(t, session, path.Join(testUser, testRepoName, "src/branch/main/1.5kibFile.txt"), "1,5 КиБ") testFileSizeTranslated(t, session, path.Join(testUser, testRepoName, "src/branch/main/1.25mibFile.txt"), "1,3 МиБ") diff --git a/tests/integration/user_test.go b/tests/integration/user_test.go index 3a3932bdc2..02cc9b51cc 100644 --- a/tests/integration/user_test.go +++ b/tests/integration/user_test.go @@ -495,9 +495,7 @@ func TestUserPronouns(t *testing.T) { // Set the pronouns for user2 pronouns := "she/her" req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{ - LoginName: "user2", - SourceID: 0, - Pronouns: &pronouns, + Pronouns: &pronouns, }).AddTokenAuth(adminToken) resp := MakeRequest(t, req, http.StatusOK) @@ -596,9 +594,7 @@ func TestUserPronouns(t *testing.T) { // Set the pronouns to Unspecified (an empty string) via the API pronouns := "" req := NewRequestWithJSON(t, "PATCH", "/api/v1/admin/users/user2", &api.EditUserOption{ - LoginName: "user2", - SourceID: 0, - Pronouns: &pronouns, + Pronouns: &pronouns, }).AddTokenAuth(adminToken) MakeRequest(t, req, http.StatusOK) diff --git a/tests/integration/webhook_test.go b/tests/integration/webhook_test.go index e5ecddab32..ec85d12b07 100644 --- a/tests/integration/webhook_test.go +++ b/tests/integration/webhook_test.go @@ -9,10 +9,16 @@ import ( "testing" "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" webhook_model "code.gitea.io/gitea/models/webhook" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/json" webhook_module "code.gitea.io/gitea/modules/webhook" + "code.gitea.io/gitea/services/release" + "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) @@ -70,3 +76,113 @@ func TestWebhookPayloadRef(t *testing.T) { assert.Empty(t, expected) }) } + +func TestWebhookReleaseEvents(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + w := unittest.AssertExistsAndLoadBean(t, &webhook_model.Webhook{ + ID: 1, + RepoID: repo.ID, + }) + w.HookEvent = &webhook_module.HookEvent{ + SendEverything: true, + } + assert.NoError(t, w.UpdateEvent()) + assert.NoError(t, webhook_model.UpdateWebhook(db.DefaultContext, w)) + + hookTasks := retrieveHookTasks(t, w.ID, true) + + gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) + assert.NoError(t, err) + defer gitRepo.Close() + + t.Run("CreateRelease", func(t *testing.T) { + assert.NoError(t, release.CreateRelease(gitRepo, &repo_model.Release{ + RepoID: repo.ID, + Repo: repo, + PublisherID: user.ID, + Publisher: user, + TagName: "v1.1.1", + Target: "master", + Title: "v1.1.1 is released", + Note: "v1.1.1 is released", + IsDraft: false, + IsPrerelease: false, + IsTag: false, + }, nil, "")) + + // check the newly created hooktasks + hookTasksLenBefore := len(hookTasks) + hookTasks = retrieveHookTasks(t, w.ID, false) + + checkHookTasks(t, map[webhook_module.HookEventType]string{ + webhook_module.HookEventRelease: "published", + webhook_module.HookEventCreate: "", // a tag was created as well + webhook_module.HookEventPush: "", // the tag creation also means a push event + }, hookTasks[:len(hookTasks)-hookTasksLenBefore]) + + t.Run("UpdateRelease", func(t *testing.T) { + rel := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{RepoID: repo.ID, TagName: "v1.1.1"}) + assert.NoError(t, release.UpdateRelease(db.DefaultContext, user, gitRepo, rel, nil, nil, nil, false)) + + // check the newly created hooktasks + hookTasksLenBefore := len(hookTasks) + hookTasks = retrieveHookTasks(t, w.ID, false) + + checkHookTasks(t, map[webhook_module.HookEventType]string{ + webhook_module.HookEventRelease: "updated", + }, hookTasks[:len(hookTasks)-hookTasksLenBefore]) + }) + }) + + t.Run("CreateNewTag", func(t *testing.T) { + assert.NoError(t, release.CreateNewTag(db.DefaultContext, + user, + repo, + "master", + "v1.1.2", + "v1.1.2 is tagged", + )) + + // check the newly created hooktasks + hookTasksLenBefore := len(hookTasks) + hookTasks = retrieveHookTasks(t, w.ID, false) + + checkHookTasks(t, map[webhook_module.HookEventType]string{ + webhook_module.HookEventCreate: "", // tag was created as well + webhook_module.HookEventPush: "", // the tag creation also means a push event + }, hookTasks[:len(hookTasks)-hookTasksLenBefore]) + + t.Run("UpdateRelease", func(t *testing.T) { + rel := unittest.AssertExistsAndLoadBean(t, &repo_model.Release{RepoID: repo.ID, TagName: "v1.1.2"}) + assert.NoError(t, release.UpdateRelease(db.DefaultContext, user, gitRepo, rel, nil, nil, nil, true)) + + // check the newly created hooktasks + hookTasksLenBefore := len(hookTasks) + hookTasks = retrieveHookTasks(t, w.ID, false) + + checkHookTasks(t, map[webhook_module.HookEventType]string{ + webhook_module.HookEventRelease: "published", + }, hookTasks[:len(hookTasks)-hookTasksLenBefore]) + }) + }) +} + +func checkHookTasks(t *testing.T, expectedActions map[webhook_module.HookEventType]string, hookTasks []*webhook_model.HookTask) { + t.Helper() + for _, hookTask := range hookTasks { + expectedAction, ok := expectedActions[hookTask.EventType] + if !ok { + t.Errorf("unexpected (or duplicated) event %q", hookTask.EventType) + } + var payload struct { + Action string `json:"action"` + } + assert.NoError(t, json.Unmarshal([]byte(hookTask.PayloadContent), &payload)) + assert.Equal(t, expectedAction, payload.Action, "unexpected action for %q event", hookTask.EventType) + delete(expectedActions, hookTask.EventType) + } + assert.Empty(t, expectedActions) +} diff --git a/tests/test_utils.go b/tests/test_utils.go index af83f9027d..a607194bef 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -11,7 +11,9 @@ import ( "os" "path" "path/filepath" + "runtime" "testing" + "time" "code.gitea.io/gitea/models/db" packages_model "code.gitea.io/gitea/models/packages" @@ -20,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/process" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" @@ -181,6 +184,36 @@ func PrepareAttachmentsStorage(t testing.TB) { })) } +// cancelProcesses cancels all processes of the [process.Manager]. +// Returns immediately if delay is 0, otherwise wait until all processes are done +// and fails the test if it takes longer that the given delay. +func cancelProcesses(t testing.TB, delay time.Duration) { + processManager := process.GetManager() + processes, _ := processManager.Processes(true, true) + for _, p := range processes { + processManager.Cancel(p.PID) + t.Logf("PrepareTestEnv:Process %q cancelled", p.Description) + } + if delay == 0 || len(processes) == 0 { + return + } + + start := time.Now() + processes, _ = processManager.Processes(true, true) + for len(processes) > 0 { + if time.Since(start) > delay { + t.Errorf("ERROR PrepareTestEnv: could not cancel all processes within %s", delay) + for _, p := range processes { + t.Logf("PrepareTestEnv:Remaining Process: %q", p.Description) + } + return + } + runtime.Gosched() // let the context cancellation propagate + processes, _ = processManager.Processes(true, true) + } + t.Logf("PrepareTestEnv: all processes cancelled within %s", time.Since(start)) +} + func PrepareTestEnv(t testing.TB, skip ...int) func() { t.Helper() ourSkip := 1 @@ -189,6 +222,11 @@ func PrepareTestEnv(t testing.TB, skip ...int) func() { } deferFn := PrintCurrentTest(t, ourSkip) + // kill all background processes to prevent them from interfering with the fixture loading + // see https://codeberg.org/forgejo/forgejo/issues/2962 + cancelProcesses(t, 30*time.Second) + t.Cleanup(func() { cancelProcesses(t, 0) }) // cancel remaining processes in a non-blocking way + // load database fixtures assert.NoError(t, unittest.LoadFixtures()) diff --git a/web_src/css/actions.css b/web_src/css/actions.css index e7b9a3855a..0ab09f537a 100644 --- a/web_src/css/actions.css +++ b/web_src/css/actions.css @@ -44,9 +44,10 @@ } .run-list-item-right { - flex: 0 0 15%; + width: 130px; display: flex; flex-direction: column; + flex-shrink: 0; gap: 3px; color: var(--color-text-light); } @@ -57,3 +58,26 @@ gap: .25rem; align-items: center; } + +.run-list .flex-item-trailing { + flex-wrap: nowrap; + width: 280px; + flex: 0 0 280px; +} + +.run-list-ref { + display: inline-block !important; +} + +@media (max-width: 767.98px) { + .run-list .flex-item-trailing { + flex-direction: column; + align-items: flex-end; + width: auto; + flex-basis: auto; + } + .run-list-item-right, + .run-list-ref { + max-width: 110px; + } +} diff --git a/web_src/css/base.css b/web_src/css/base.css index 17c3f2ee68..c571280ee0 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -24,7 +24,21 @@ --repo-header-issue-min-height: 41px; --min-height-textarea: 132px; /* padding + 6 lines + border = calc(1.57142em + 6lh + 2px), but lh is not fully supported */ --tab-size: 4; - --checkbox-size: 16px; /* height and width of checkbox and radio inputs */ + --checkbox-size: 15px; /* height and width of checkbox and radio inputs */ + --page-spacing: 16px; /* space between page elements */ + --page-margin-x: 32px; /* minimum space on left and right side of page */ +} + +@media (min-width: 768px) and (max-width: 1200px) { + :root { + --page-margin-x: 16px; + } +} + +@media (max-width: 767.98px) { + :root { + --page-margin-x: 8px; + } } :root * { @@ -232,6 +246,18 @@ h1.error-code { user-select: none; } +.top-right-buttons { + gap: 0.5rem; +} + +.top-right-buttons .ui.button { + margin-right: 0; +} + +.ui.partial.secondary.menu { + margin-bottom: 0; +} + a { color: var(--color-primary); cursor: pointer; @@ -427,6 +453,7 @@ a.label, .ui.selection.dropdown .menu > .item { border-color: var(--color-secondary); + white-space: nowrap; } .ui.selection.visible.dropdown > .text:not(.default) { @@ -594,11 +621,14 @@ img.ui.avatar, margin-bottom: 14px; } -/* add padding to all content when there is no .secondary.nav. this uses padding instead of - margin because with the negative margin on .ui.grid we would have to set margin-top: 0, - but that does not work universally for all pages */ +/* add margin to all pages when there is no .secondary.nav */ .page-content > :first-child:not(.secondary-nav) { - padding-top: 14px; + margin-top: var(--page-spacing); +} +/* if .ui.grid is the first child the first grid-column has 'padding-top: 1rem' which we need + to compensate here */ +.page-content > :first-child.ui.grid { + margin-top: calc(var(--page-spacing) - 1rem); } .ui.pagination.menu .active.item { @@ -1256,6 +1286,7 @@ overflow-menu .ui.label { white-space: pre-wrap; word-break: break-all; overflow-wrap: anywhere; + line-height: inherit; /* needed for inline code preview in markup */ } .blame .code-inner { @@ -1532,6 +1563,7 @@ table th[data-sortt-desc] .svg { align-items: center; gap: .25rem; vertical-align: middle; + min-width: 0; } .ui.ui.button { @@ -1552,4 +1584,5 @@ table th[data-sortt-desc] .svg { display: flex; align-items: center; gap: .25rem; + min-width: 0; } diff --git a/web_src/css/dashboard.css b/web_src/css/dashboard.css index 4bb9fa38bf..2ee2399d73 100644 --- a/web_src/css/dashboard.css +++ b/web_src/css/dashboard.css @@ -7,7 +7,6 @@ .dashboard.feeds .context.user.menu .ui.header, .dashboard.issues .context.user.menu .ui.header { font-size: 1rem; - text-transform: none; } .dashboard.feeds .filter.menu, diff --git a/web_src/css/features/projects.css b/web_src/css/features/projects.css index cec5e6fc64..e23c146748 100644 --- a/web_src/css/features/projects.css +++ b/web_src/css/features/projects.css @@ -22,34 +22,27 @@ cursor: default; } +.project-column .issue-card { + color: var(--color-text); +} + .project-column-header { display: flex; align-items: center; justify-content: space-between; } -.project-column-header.dark-label { - color: var(--color-project-board-dark-label) !important; -} - -.project-column-header.dark-label .project-column-title { - color: var(--color-project-board-dark-label) !important; -} - -.project-column-header.light-label { - color: var(--color-project-board-light-label) !important; -} - -.project-column-header.light-label .project-column-title { - color: var(--color-project-board-light-label) !important; -} - .project-column-title { background: none !important; line-height: 1.25 !important; cursor: inherit; } +.project-column-title, +.project-column-issue-count { + color: inherit !important; +} + .project-column > .cards { flex: 1; display: flex; @@ -64,6 +57,8 @@ .project-column > .divider { margin: 5px 0; + border-color: currentcolor; + opacity: .5; } .project-column:first-child { diff --git a/web_src/css/form.css b/web_src/css/form.css index 2a976c056d..a8f73b6b66 100644 --- a/web_src/css/form.css +++ b/web_src/css/form.css @@ -249,21 +249,6 @@ textarea:focus, .user.signup form .optional .title { margin-left: 250px !important; } - .user.activate form .inline.field > input, - .user.forgot.password form .inline.field > input, - .user.reset.password form .inline.field > input, - .user.link-account form .inline.field > input, - .user.signin form .inline.field > input, - .user.signup form .inline.field > input, - .user.activate form .inline.field > textarea, - .user.forgot.password form .inline.field > textarea, - .user.reset.password form .inline.field > textarea, - .user.link-account form .inline.field > textarea, - .user.signin form .inline.field > textarea, - .user.signup form .inline.field > textarea, - .oauth-login-link { - width: 50%; - } } @media (max-width: 767.98px) { @@ -310,14 +295,7 @@ textarea:focus, .user.reset.password form .inline.field > label, .user.link-account form .inline.field > label, .user.signin form .inline.field > label, - .user.signup form .inline.field > label, - .user.activate form input, - .user.forgot.password form input, - .user.reset.password form input, - .user.link-account form input, - .user.signin form input, - .user.signup form input, - .oauth-login-link { + .user.signup form .inline.field > label { width: 100% !important; } } @@ -435,9 +413,9 @@ textarea:focus, .repository.new.repo form label, .repository.new.migrate form label, .repository.new.fork form label, - .repository.new.repo form input, - .repository.new.migrate form input, - .repository.new.fork form input, + .repository.new.repo form .inline.field > input, + .repository.new.migrate form .inline.field > input, + .repository.new.fork form .inline.field > input, .repository.new.fork form .field a, .repository.new.repo form .selection.dropdown, .repository.new.migrate form .selection.dropdown, diff --git a/web_src/css/install.css b/web_src/css/install.css index 4ac294e902..ee2395e6c5 100644 --- a/web_src/css/install.css +++ b/web_src/css/install.css @@ -18,7 +18,8 @@ width: auto; } -.page-content.install form.ui.form input { +.page-content.install form.ui.form input:not([type="checkbox"],[type="radio"]), +.page-content.install form.ui.form .ui.selection.dropdown { width: 60%; } diff --git a/web_src/css/modules/checkbox.css b/web_src/css/modules/checkbox.css index 9238e0b3f3..8d73573bfa 100644 --- a/web_src/css/modules/checkbox.css +++ b/web_src/css/modules/checkbox.css @@ -20,7 +20,7 @@ input[type="radio"] { .ui.checkbox input[type="checkbox"], .ui.checkbox input[type="radio"] { position: absolute; - top: 0; + top: 1px; left: 0; width: var(--checkbox-size); height: var(--checkbox-size); @@ -66,7 +66,7 @@ input[type="radio"] { } .ui.toggle.checkbox input { width: 3.5rem; - height: 1.5rem; + height: 21px; opacity: 0; z-index: 3; } @@ -81,29 +81,30 @@ input[type="radio"] { content: ""; z-index: 1; top: 0; - width: 3.5rem; - height: 1.5rem; + width: 49px; + height: 21px; border-radius: 500rem; left: 0; } .ui.toggle.checkbox label::after { background: var(--color-white); + box-shadow: 1px 1px 4px 1px var(--color-shadow); position: absolute; content: ""; opacity: 1; z-index: 2; - width: 1.5rem; - height: 1.5rem; - top: 0; - left: 0; + width: 18px; + height: 18px; + top: 1.5px; + left: 1.5px; border-radius: 500rem; transition: background 0.3s ease, left 0.3s ease; } .ui.toggle.checkbox input ~ label::after { - left: -0.05rem; + left: 1.5px; } .ui.toggle.checkbox input:checked ~ label::after { - left: 2.15rem; + left: 29px; } .ui.toggle.checkbox input:focus ~ label::before, .ui.toggle.checkbox label::before { diff --git a/web_src/css/modules/container.css b/web_src/css/modules/container.css index dc854f89d0..f394d6c06d 100644 --- a/web_src/css/modules/container.css +++ b/web_src/css/modules/container.css @@ -49,30 +49,11 @@ /* overwrite width of containers inside the main page content div (div with class "page-content") */ .page-content .ui.ui.ui.container:not(.fluid) { width: 1280px; - max-width: calc(100% - 64px); + max-width: calc(100% - calc(2 * var(--page-margin-x))); margin-left: auto; margin-right: auto; } .ui.container.fluid.padded { - padding: 0 32px; -} - -/* enable fluid page widths for medium size viewports */ -@media (min-width: 768px) and (max-width: 1200px) { - .page-content .ui.ui.ui.container:not(.fluid) { - max-width: calc(100% - 32px); - } - .ui.container.fluid.padded { - padding: 0 16px; - } -} - -@media (max-width: 767.98px) { - .page-content .ui.ui.ui.container:not(.fluid) { - max-width: calc(100% - 16px); - } - .ui.container.fluid.padded { - padding: 0 8px; - } + padding: 0 var(--page-margin-x); } diff --git a/web_src/css/modules/divider.css b/web_src/css/modules/divider.css index 48560bd3d9..acc8408f37 100644 --- a/web_src/css/modules/divider.css +++ b/web_src/css/modules/divider.css @@ -2,12 +2,16 @@ margin: 10px 0; height: 0; font-weight: var(--font-weight-medium); - text-transform: uppercase; color: var(--color-text); font-size: 1rem; width: 100%; } +h4.divider { + margin-top: 1.25rem; + margin-bottom: 1.25rem; +} + .divider:not(.divider-text) { border-top: 1px solid var(--color-secondary); } diff --git a/web_src/css/modules/flexcontainer.css b/web_src/css/modules/flexcontainer.css index 0b559f1e7d..5d4e12cc12 100644 --- a/web_src/css/modules/flexcontainer.css +++ b/web_src/css/modules/flexcontainer.css @@ -2,13 +2,20 @@ .flex-container { display: flex !important; - gap: 16px; + gap: var(--page-spacing); + margin-top: var(--page-spacing); } +/* small options menu on the left, used in settings/admin pages */ .flex-container-nav { width: 240px; } +/* wide sidebar on the right, used in frontpage */ +.flex-container-sidebar { + width: 35%; +} + .flex-container-main { flex: 1; min-width: 0; /* make the "text truncate" work, otherwise the flex axis is not limited and the text just overflows */ @@ -18,7 +25,9 @@ .flex-container { flex-direction: column; } - .flex-container-nav { + .flex-container-nav, + .flex-container-sidebar { + order: -1; width: auto; } } diff --git a/web_src/css/modules/header.css b/web_src/css/modules/header.css index 05381e1185..9cec5fcbe6 100644 --- a/web_src/css/modules/header.css +++ b/web_src/css/modules/header.css @@ -9,7 +9,6 @@ font-family: var(--fonts-regular); font-weight: var(--font-weight-medium); line-height: 1.28571429; - text-transform: none; } .ui.header:first-child { diff --git a/web_src/css/modules/label.css b/web_src/css/modules/label.css index 0512c5fddb..2032b2c84b 100644 --- a/web_src/css/modules/label.css +++ b/web_src/css/modules/label.css @@ -5,12 +5,12 @@ display: inline-flex; align-items: center; gap: .25rem; + min-width: 0; vertical-align: middle; line-height: 1; background: var(--color-label-bg); color: var(--color-label-text); padding: 0.3em 0.5em; - text-transform: none; font-size: 0.85714286rem; font-weight: var(--font-weight-medium); border: 0 solid transparent; diff --git a/web_src/css/modules/list.css b/web_src/css/modules/list.css index 73760390de..32c71e802b 100644 --- a/web_src/css/modules/list.css +++ b/web_src/css/modules/list.css @@ -126,6 +126,12 @@ cursor: pointer; } +.ui.list .list > .item [class*="right floated"], +.ui.list > .item [class*="right floated"] { + float: right; + margin: 0 0 0 1em; +} + .ui.menu .ui.list > .item, .ui.menu .ui.list .list > .item { display: list-item; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 7ea7dad41e..87dbeb5bba 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -177,12 +177,44 @@ } } -.repository.file.list .repo-path { - word-break: break-word; +.commit-summary { + flex: 1; + overflow-wrap: anywhere; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } -.repository.file.list #repo-files-table { - table-layout: fixed; +.commit-header .commit-summary, +td .commit-summary { + white-space: normal; +} + +.latest-commit { + display: flex; + flex: 1; + align-items: center; + overflow: hidden; + text-overflow: ellipsis; +} + +@media (max-width: 767.98px) { + .latest-commit .sha { + display: none; + } + .latest-commit .commit-summary { + margin-left: 8px; + } +} + +.repo-path { + display: flex; + overflow-wrap: anywhere; +} + +/* this is what limits the commit table width to a value that works on all viewport sizes */ +#repo-files-table th:first-of-type { + max-width: calc(calc(min(100vw, 1280px)) - 145px - calc(2 * var(--page-margin-x))); } .repository.file.list #repo-files-table thead th { @@ -262,7 +294,6 @@ } .repository.file.list #repo-files-table td.age { - width: 120px; color: var(--color-text-light-1); } @@ -404,7 +435,6 @@ padding: 0 !important; } -.non-diff-file-content .attached.segment, .non-diff-file-content .pdfobject { border-radius: 0 0 var(--border-radius) var(--border-radius); } @@ -564,26 +594,18 @@ align-items: center; } -.repository.view.issue .issue-title-buttons, -.repository.view.issue .edit-buttons { +.repository.view.issue .top-right-buttons { display: flex; } -.issue-title-buttons { - gap: 0.5rem; -} - @media (max-width: 767.98px) { .repository.view.issue .issue-title { flex-direction: column; } - .repository.view.issue .issue-title-buttons, - .repository.view.issue .edit-buttons { + .repository.view.issue .top-right-buttons { width: 100%; - justify-content: space-between; - } - .repository.view.issue .edit-buttons { margin-top: .5rem; + justify-content: space-between; } .comment.form .issue-content-left .avatar { display: none; @@ -597,6 +619,10 @@ .comment.form .content .form::after { display: none; } + + .repository.view.issue .issue-title.edit-active h1 { + padding-right: 0; + } } .repository.view.issue .issue-title { @@ -614,7 +640,7 @@ font-size: 32px; line-height: 40px; margin: 0; - padding-right: 0.25rem; + padding-right: 0.5rem; min-height: var(--repo-header-issue-min-height); } @@ -809,55 +835,53 @@ margin-right: 0.25em; } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit { - line-height: 34px; /* this must be same as .badge height, to avoid overflow */ - clear: both; /* reset the "float right shabox", in the future, use flexbox instead */ +.singular-commit { + display: flex; + align-items: center; } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit > img.avatar, -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit > .avatar img { - position: relative; - top: -2px; +.singular-commit .badge { + height: 30px !important; } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label { +.singular-commit .shabox .sha.label { margin: 0; border: 1px solid var(--color-light-border); } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isWarning { +.singular-commit .shabox .sha.label.isSigned.isWarning { border: 1px solid var(--color-red-badge); background: var(--color-red-badge-bg); } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isWarning:hover { +.singular-commit .shabox .sha.label.isSigned.isWarning:hover { background: var(--color-red-badge-hover-bg) !important; } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerified { +.singular-commit .shabox .sha.label.isSigned.isVerified { border: 1px solid var(--color-green-badge); background: var(--color-green-badge-bg); } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerified:hover { +.singular-commit .shabox .sha.label.isSigned.isVerified:hover { background: var(--color-green-badge-hover-bg) !important; } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted { +.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted { border: 1px solid var(--color-yellow-badge); background: var(--color-yellow-badge-bg); } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover { +.singular-commit .shabox .sha.label.isSigned.isVerifiedUntrusted:hover { background: var(--color-yellow-badge-hover-bg) !important; } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched { +.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched { border: 1px solid var(--color-orange-badge); background: var(--color-orange-badge-bg); } -.repository.view.issue .comment-list .timeline-item.commits-list .singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover { +.singular-commit .shabox .sha.label.isSigned.isVerifiedUnmatched:hover { background: var(--color-orange-badge-hover-bg) !important; } @@ -1052,6 +1076,12 @@ margin-left: 15px; } +.repository.view.issue .comment-list .event .detail .text { + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} + .repository.view.issue .comment-list .event .segments { box-shadow: none; } @@ -1223,10 +1253,6 @@ margin: 0; } -.repository #commits-table td.message { - text-overflow: unset; -} - .repository #commits-table.ui.basic.striped.table tbody tr:nth-child(2n) { background-color: var(--color-light) !important; } @@ -2153,6 +2179,20 @@ display: inline-block !important; } +.commit-header-buttons { + display: flex; + gap: 4px; + align-items: flex-start; + white-space: nowrap; +} + +@media (max-width: 767.98px) { + .commit-header-buttons { + flex-direction: column; + align-items: stretch; + } +} + .settings.webhooks .list > .item:not(:first-child), .settings.githooks .list > .item:not(:first-child), .settings.actions .list > .item:not(:first-child) { @@ -2302,6 +2342,8 @@ .stats-table { display: table; width: 100%; + margin: 6px 0; + border-spacing: 2px; } .stats-table .table-cell { @@ -2309,11 +2351,34 @@ } .stats-table .table-cell.tiny { - height: 0.5em; + height: 8px; +} + +.stats-table .table-cell:first-child { + border-top-left-radius: 4px; + border-bottom-left-radius: 4px; +} + +.stats-table .table-cell:last-child { + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; +} + +.labels-list { + display: inline-flex; + flex-wrap: wrap; + gap: 2.5px; +} + +.labels-list a { + display: flex; + text-decoration: none; } .labels-list .label { - margin: 2px 0; + padding: 0 6px; + margin: 0 !important; + min-height: 20px; display: inline-flex !important; line-height: 1.3; /* there is a `font-size: 1.25em` for inside emoji, so here the line-height needs to be larger slightly */ } @@ -2371,7 +2436,7 @@ tbody.commit-list { .author-wrapper { overflow: hidden; text-overflow: ellipsis; - max-width: calc(100% - 50px); + max-width: 100%; display: inline-block; vertical-align: middle; } @@ -2379,6 +2444,7 @@ tbody.commit-list { .author-wrapper { max-width: 180px; align-self: center; + white-space: nowrap; } /* in the commit list, messages can wrap so we can use inline */ @@ -2396,10 +2462,6 @@ tbody.commit-list { tr.commit-list { width: 100%; } - th .message-wrapper { - display: block; - max-width: calc(100vw - 70px); - } .author-wrapper { max-width: 80px; } @@ -2409,27 +2471,18 @@ tbody.commit-list { tr.commit-list { width: 723px; } - th .message-wrapper { - max-width: 120px; - } } @media (min-width: 992px) and (max-width: 1200px) { tr.commit-list { width: 933px; } - th .message-wrapper { - max-width: 350px; - } } @media (min-width: 1201px) { tr.commit-list { width: 1127px; } - th .message-wrapper { - max-width: 525px; - } } .commit-list .commit-status-link { @@ -2757,7 +2810,7 @@ tbody.commit-list { .repository.file.list #repo-files-table .entry td.message, .repository.file.list #repo-files-table .commit-list td.message, .repository.file.list #repo-files-table .entry span.commit-summary, - .repository.file.list #repo-files-table .commit-list span.commit-summary { + .repository.file.list #repo-files-table .commit-list tr span.commit-summary { display: none !important; } .repository.view.issue .comment-list .timeline, diff --git a/web_src/css/repo/issue-list.css b/web_src/css/repo/issue-list.css index d19421fcbc..37090f71b4 100644 --- a/web_src/css/repo/issue-list.css +++ b/web_src/css/repo/issue-list.css @@ -69,23 +69,6 @@ } } -#issue-list .flex-item-title .labels-list { - display: flex; - flex-wrap: wrap; - gap: 0.25em; -} - -#issue-list .flex-item-title .labels-list a { - display: flex; - text-decoration: none; -} - -#issue-list .flex-item-title .labels-list .label { - padding: 0 6px; - margin: 0; - min-height: 20px; -} - #issue-list .flex-item-body .branches { display: inline-flex; } diff --git a/web_src/css/themes/theme-forgejo-dark.css b/web_src/css/themes/theme-forgejo-dark.css index 862aaf4ba4..7ff071bbb0 100644 --- a/web_src/css/themes/theme-forgejo-dark.css +++ b/web_src/css/themes/theme-forgejo-dark.css @@ -271,9 +271,6 @@ i.grey.icon.icon.icon.icon { border-radius: 0.28571429rem !important; overflow: hidden; } -.ui.secondary.vertical.menu > .item { - border-radius: 0 !important; -} .ui.basic.primary.button.item { background-color: var(--color-active) !important; color: var(--color-text) !important; @@ -318,3 +315,16 @@ strong.attention-note, svg.attention-note { strong.attention-caution, svg.attention-caution { color: var(--color-red-light); } +.ui.basic.red.button { + background-color: var(--color-red); + color: var(--color-white); +} +.ui.basic.red.button:hover, +.ui.basic.red.button:focus { + background-color: var(--color-red-dark-1); + color: var(--color-white); +} +.ui.basic.red.button:active { + background-color: var(--color-red-dark-2); + color: var(--color-white); +} diff --git a/web_src/css/themes/theme-forgejo-light.css b/web_src/css/themes/theme-forgejo-light.css index 7e3aa69344..0ee7020a5e 100644 --- a/web_src/css/themes/theme-forgejo-light.css +++ b/web_src/css/themes/theme-forgejo-light.css @@ -262,9 +262,6 @@ border-radius: 0.28571429rem !important; overflow: hidden; } -.ui.secondary.vertical.menu > .item { - border-radius: 0 !important; -} .ui.basic.primary.button.item { background-color: var(--color-active) !important; color: var(--color-text) !important; diff --git a/web_src/css/themes/theme-gitea-dark.css b/web_src/css/themes/theme-gitea-dark.css index 07e217742d..c74f334c2d 100644 --- a/web_src/css/themes/theme-gitea-dark.css +++ b/web_src/css/themes/theme-gitea-dark.css @@ -65,7 +65,7 @@ --color-console-fg-subtle: #bec4c8; --color-console-bg: #171b1e; --color-console-border: #2e353b; - --color-console-hover-bg: #e8e8ff16; + --color-console-hover-bg: #292d31; --color-console-active-bg: #2e353b; --color-console-menu-bg: #252b30; --color-console-menu-border: #424b51; @@ -215,8 +215,6 @@ --color-placeholder-text: var(--color-text-light-3); --color-editor-line-highlight: var(--color-primary-light-5); --color-project-board-bg: var(--color-secondary-light-2); - --color-project-board-dark-label: #0e1011; - --color-project-board-light-label: #dde0e2; --color-caret: var(--color-text); /* should ideally be --color-text-dark, see #15651 */ --color-reaction-bg: #e8e8ff12; --color-reaction-hover-bg: var(--color-primary-light-4); diff --git a/web_src/css/themes/theme-gitea-light.css b/web_src/css/themes/theme-gitea-light.css index 2741e0e0bd..01dd8ba4f7 100644 --- a/web_src/css/themes/theme-gitea-light.css +++ b/web_src/css/themes/theme-gitea-light.css @@ -63,12 +63,12 @@ /* console colors - used for actions console and console files */ --color-console-fg: #f8f8f9; --color-console-fg-subtle: #bec4c8; - --color-console-bg: #181b1d; - --color-console-border: #313538; - --color-console-hover-bg: #ffffff16; - --color-console-active-bg: #313538; - --color-console-menu-bg: #272b2e; - --color-console-menu-border: #464a4d; + --color-console-bg: #171b1e; + --color-console-border: #2e353b; + --color-console-hover-bg: #292d31; + --color-console-active-bg: #2e353b; + --color-console-menu-bg: #252b30; + --color-console-menu-border: #424b51; /* named colors */ --color-red: #db2828; --color-orange: #f2711c; @@ -215,8 +215,6 @@ --color-placeholder-text: var(--color-text-light-3); --color-editor-line-highlight: var(--color-primary-light-6); --color-project-board-bg: var(--color-secondary-light-4); - --color-project-board-dark-label: #0e1114; - --color-project-board-light-label: #eaeef2; --color-caret: var(--color-text-dark); --color-reaction-bg: #0000170a; --color-reaction-hover-bg: var(--color-primary-light-5); diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index ac6a8f3bb6..70b12dcb28 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -1,7 +1,6 @@
    - {{template "repo/latest_commit" .}} + +
    +
    + {{template "repo/latest_commit" .}} +
    +
    {{if .LatestCommit}}{{if .LatestCommit.Committer}}{{TimeSince .LatestCommit.Committer.When ctx.Locale}}{{end}}{{end}}
    - {{.Num}} + {{.Num}} {{.FormattedContent}}