diff --git a/.dockerignore b/.dockerignore index 6898dda39ab..5008cca0314 100644 --- a/.dockerignore +++ b/.dockerignore @@ -41,5 +41,7 @@ frontend/node_modules node_modules # travis vendor/bundle +# Local checkout; all-in-one copies hocuspocus from its dedicated image. +vendor/hocuspocus /public/assets /config/frontend_assets.manifest.json diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b83f67934d8..cd74bbbae83 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -5,6 +5,11 @@ updates: schedule: interval: "daily" target-branch: "dev" + cooldown: + default-days: 5 + semver-major-days: 30 + semver-minor-days: 14 + semver-patch-days: 5 groups: angular: patterns: @@ -40,6 +45,11 @@ updates: schedule: interval: "daily" target-branch: "dev" + cooldown: + default-days: 5 + semver-major-days: 30 + semver-minor-days: 14 + semver-patch-days: 5 groups: aws-gems: patterns: diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index 3fe1d57a6f6..708cd66b65f 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -72,6 +72,7 @@ jobs: samachon, shiroginne, toy, + tiroessler, ulferts, vonTronje, vspielau, diff --git a/.github/workflows/continuous-delivery.yml b/.github/workflows/continuous-delivery.yml index f59458f4394..24d6f427c1b 100644 --- a/.github/workflows/continuous-delivery.yml +++ b/.github/workflows/continuous-delivery.yml @@ -21,8 +21,10 @@ jobs: REPOSITORY: opf/openproject-flavours WORKFLOW_ID: ci.yml REF_NAME: ${{ github.ref_name }} + THIS_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: | - PAYLOAD=$(jq -n --arg ref "$REF_NAME" '{"ref": "dev", "inputs": {"ref": $ref}}') + PAYLOAD=$(jq -n --arg ref "$REF_NAME" --arg triggered_by_url "$THIS_RUN_URL" \ + '{"ref": "dev", "inputs": {"ref": $ref, "triggered_by_url": $triggered_by_url}}') curl -i --fail-with-body -H"authorization: Bearer $TOKEN" \ -XPOST -H"Accept: application/vnd.github.v3+json" \ https://api.github.com/repos/$REPOSITORY/actions/workflows/$WORKFLOW_ID/dispatches \ diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1b568f315a4..338b227b03e 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -255,23 +255,12 @@ jobs: if [ -d vendor/bundle.bak ]; then mv vendor/bundle.bak vendor/bundle fi - - name: Test - # We only test the native container. If that fails the builds for the others - # will be cancelled as well. - if: matrix.platform == 'linux/amd64' && matrix.target == 'all-in-one' + - name: Validate image run: | - docker run \ - --name openproject \ - -d -p 8080:80 --platform ${{ matrix.platform }} \ - -e SUPERVISORD_LOG_LEVEL=debug \ - -e OPENPROJECT_LOGIN__REQUIRED=false \ - -e OPENPROJECT_HTTPS=false \ - ${{ steps.build.outputs.imageid }} - - sleep 60 - - docker logs openproject --tail 100 - wget -O- --retry-on-http-error=503,502 --retry-connrefused http://localhost:8080/api/v3 + ./script/ci/docker_validate_image.sh \ + --image "${{ steps.build.outputs.imageid }}" \ + --target "${{ matrix.target }}" \ + --platform "${{ matrix.platform }}" - name: Push image id: push uses: docker/build-push-action@v6 @@ -355,7 +344,7 @@ jobs: - name: Inspect image run: | docker buildx imagetools inspect ${{ needs.setup.outputs.registry_image }}:${{ steps.meta.outputs.version }} - notify: + notify-failure: needs: [setup, build, merge] if: ${{ always() && contains(needs.*.result, 'failure') }} uses: ./.github/workflows/email-notification.yml diff --git a/.github/workflows/downstream-ci.yml b/.github/workflows/downstream-ci.yml index 1495e2fddd1..318ebe64e58 100644 --- a/.github/workflows/downstream-ci.yml +++ b/.github/workflows/downstream-ci.yml @@ -38,6 +38,7 @@ jobs: BASE_REF: ${{ github.base_ref }} HEAD_REF: ${{ github.event.pull_request.head.ref }} REF_NAME: ${{ github.ref_name }} + THIS_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} # ref: # * on push this will be `dev` or a specific release branch (e.g. release/16.1) - there is always a matching branch for that downstream # * on pull_request we use the PR branch's base (e.g. dev or a release branch) to match the above @@ -64,7 +65,8 @@ jobs: exit 0 fi - PAYLOAD=$(jq -n --arg ref "$REF" --arg core_ref "$CORE_REF" '{"ref": $ref, "inputs": {"core_ref": $core_ref}}') + PAYLOAD=$(jq -n --arg ref "$REF" --arg core_ref "$CORE_REF" --arg triggered_by_url "$THIS_RUN_URL" \ + '{"ref": $ref, "inputs": {"core_ref": $core_ref, "triggered_by_url": $triggered_by_url}}') OUTPUT_FILE=/tmp/request-output echo "Triggering $WORKFLOW_ID workflow on $REPOSITORY branch '$REF', which will check out core branch '$CORE_REF'" diff --git a/.github/workflows/pullpreview.yml b/.github/workflows/pullpreview.yml index 6e909c68c1e..718e8448e90 100644 --- a/.github/workflows/pullpreview.yml +++ b/.github/workflows/pullpreview.yml @@ -44,8 +44,9 @@ jobs: - name: Prepare docker-compose files run: | cp ./docker/pullpreview/docker-compose.yml ./docker-compose.pullpreview.yml + cp ./docker/pullpreview/Caddyfile ./Caddyfile cp ./docker/prod/Dockerfile ./Dockerfile - - uses: pullpreview/action@v5 + - uses: pullpreview/action@v6 with: # allows to ssh to the instance using our GitHub ssh key # command like: ssh ec2-user@pr-19375-trial-ip-3-127-219-135.my.preview.run @@ -54,7 +55,8 @@ jobs: instance_type: xlarge ports: 80,443,8080 default_port: 443 - ttl: 10d + ttl: 8d + dns: my.opf.run env: AWS_ACCESS_KEY_ID: "${{ secrets.AWS_ACCESS_KEY_ID }}" AWS_SECRET_ACCESS_KEY: "${{ secrets.AWS_SECRET_ACCESS_KEY }}" diff --git a/.github/workflows/seed-all-locales.yml b/.github/workflows/seed-all-locales.yml new file mode 100644 index 00000000000..0e88f27f4ef --- /dev/null +++ b/.github/workflows/seed-all-locales.yml @@ -0,0 +1,131 @@ +name: Test seeding in all locales + +on: + schedule: + - cron: '0 2 * * 0' # Weekly on Sunday at 2 AM UTC + workflow_dispatch: + inputs: + ref: + description: 'Git ref to test (branch, tag, or SHA). Defaults to latest release branch.' + required: false + type: string + +permissions: + contents: read + +jobs: + prepare: + if: github.repository == 'opf/openproject' + name: Prepare + runs-on: ubuntu-latest + outputs: + locales: ${{ steps.list.outputs.locales }} + ref: ${{ steps.use_input_or_find_latest_release.outputs.ref }} + steps: + - name: Determine git ref to test seeding in + id: use_input_or_find_latest_release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + INPUT_REF: ${{ inputs.ref }} + run: | + if [ -n "$INPUT_REF" ]; then + echo "ref=$INPUT_REF" >> "$GITHUB_OUTPUT" + else + BRANCH=$(gh api repos/opf/openproject/branches --paginate --jq '.[].name' | grep '^release/' | sort --version-sort | tail -1) + if [ -z "$BRANCH" ]; then + echo "Error: no release branch found" + exit 1 + fi + echo "Found latest release branch: $BRANCH" + echo "ref=$BRANCH" >> "$GITHUB_OUTPUT" + fi + + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ steps.use_input_or_find_latest_release.outputs.ref }} + + - name: Print ref to summary + run: | + SHA=$(git rev-parse HEAD) + SHORT_SHA=$(git rev-parse --short HEAD) + echo "Testing seeding on **${{ steps.use_input_or_find_latest_release.outputs.ref }}** ([$SHORT_SHA](https://github.com/opf/openproject/commit/$SHA))" >> "$GITHUB_STEP_SUMMARY" + + - name: List available locales + id: list + run: | + locales=$(ruby script/i18n/test_seed_all_locales --list) + echo "locales=$locales" >> "$GITHUB_OUTPUT" + + seed: + needs: prepare + if: github.repository == 'opf/openproject' + name: Seed in ${{ matrix.locale }} for ${{ matrix.edition }} edition + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + locale: ${{ fromJson(needs.prepare.outputs.locales) }} + edition: [standard, bim] + services: + postgres: + image: postgres:16 + env: + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - 5432:5432 + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + env: + DATABASE_URL: postgres://postgres:postgres@localhost:5432/openproject_seed_test + RAILS_ENV: development + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + ref: ${{ needs.prepare.outputs.ref }} + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libpq-dev + + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true + + - name: Configure database + run: | + cat > config/database.yml <<'EOF' + development: + url: <%= ENV["DATABASE_URL"] %> + EOF + + - name: Seed in locale ${{ matrix.locale }} for ${{ matrix.edition }} edition + env: + SILENCE_SQL_LOGS: 1 + OPENPROJECT_EDITION: ${{ matrix.edition }} + run: ruby script/i18n/test_seed_all_locales ${{ matrix.locale }} + + notify-failure: + needs: [prepare, seed] + if: ${{ always() && contains(needs.*.result, 'failure') }} + uses: ./.github/workflows/email-notification.yml + secrets: inherit + with: + to: operations@openproject.com + subject: "Seeding with some locales failed on ${{ needs.prepare.outputs.ref }}" + body: | + The seed-all-locales workflow has failed. + + Branch: ${{ needs.prepare.outputs.ref }} + Editions tested: standard, bim + Trigger: ${{ github.event_name }} + + Some locale/edition combinations failed to seed correctly. + Check the workflow run for details on which locales failed: + ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/.rubocop.yml b/.rubocop.yml index c4067e4f6a0..47896294dfe 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -198,6 +198,11 @@ Rails/I18nLocaleAssignment: Exclude: - "spec/**/*.rb" +Rails/I18nLocaleTexts: + Enabled: true + Exclude: + - "spec/**/*.rb" + # We have config.active_record.belongs_to_required_by_default = false , # which means, we do have to declare presence validators on belongs_to relations. Rails/RedundantPresenceValidationOnBelongsTo: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 15e055791b5..b0484f3ae6f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,7 +4,7 @@ We are pleased that you are thinking about contributing to OpenProject! This gui ## Get in touch -Please get in touch with us using our [development forum](https://community.openproject.org/projects/openproject/boards/7) or send us an email to [info@openproject.com](mailto:info@openproject.com). +Please get in touch with us using our [OpenProject community platform](https://community.openproject.org/) or send us an email to [info@openproject.com](mailto:info@openproject.com). ## Issue tracking and coordination @@ -12,7 +12,6 @@ We eat our own ice cream so we use OpenProject for roadmap planning and team col - [Product roadmap](https://community.openproject.org/projects/openproject/roadmap) - [Wish list](https://community.openproject.org/projects/openproject/work_packages?query_id=180) -- [Bug backlog board](https://community.openproject.org/projects/openproject/boards/2905) - [Report a bug](https://www.openproject.org/docs/development/report-a-bug/) - [Submit a feature idea](https://www.openproject.org/docs/development/submit-feature-idea/) diff --git a/COPYRIGHT b/COPYRIGHT index 6e4e0e1b519..08f7c7b939c 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -1,6 +1,6 @@ OpenProject is an open source project management software. -Copyright (C) 2012-2025 the OpenProject GmbH +Copyright (C) 2012-2026 the OpenProject GmbH This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License diff --git a/Gemfile b/Gemfile index e152dab8776..132b7db2786 100644 --- a/Gemfile +++ b/Gemfile @@ -41,7 +41,7 @@ gem "activemodel-serializers-xml", "~> 1.0.1" gem "activerecord-import", "~> 2.2.0" gem "activerecord-session_store", "~> 2.2.0" gem "ox" -gem "rails", "~> 8.0.4" +gem "rails", "~> 8.1.2" gem "responders", "~> 3.2" gem "ffi", "~> 1.15" @@ -163,7 +163,7 @@ gem "matrix", "~> 0.4.3" gem "mcp", "~> 0.4.0" -gem "meta-tags", "~> 2.22.2" +gem "meta-tags", "~> 2.22.3" gem "paper_trail", "~> 17.0.0" @@ -200,7 +200,7 @@ gem "fog-aws" gem "aws-sdk-core", "~> 3.241" # File upload via fog + screenshots on travis -gem "aws-sdk-s3", "~> 1.211" +gem "aws-sdk-s3", "~> 1.213" gem "openproject-token", "~> 8.6.0" @@ -428,4 +428,4 @@ end gem "openproject-octicons", "~>19.32.0" gem "openproject-octicons_helper", "~>19.32.0" -gem "openproject-primer_view_components", "~>0.80.2" +gem "openproject-primer_view_components", "~>0.81.1" diff --git a/Gemfile.lock b/Gemfile.lock index c7cbde0ebfb..2ae54d7887e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,7 +203,7 @@ PATH remote: modules/two_factor_authentication specs: openproject-two_factor_authentication (1.0.0) - aws-sdk-sns (>= 1.101, < 1.112) + aws-sdk-sns (>= 1.101, < 1.113) messagebird-rest (>= 1.4.2, < 5.1.0) rotp (~> 6.1) webauthn (~> 3.0) @@ -223,29 +223,31 @@ GEM remote: https://rubygems.org/ specs: Ascii85 (2.0.1) - actioncable (8.0.4) - actionpack (= 8.0.4) - activesupport (= 8.0.4) + action_text-trix (2.1.16) + railties + actioncable (8.1.2) + actionpack (= 8.1.2) + activesupport (= 8.1.2) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.0.4) - actionpack (= 8.0.4) - activejob (= 8.0.4) - activerecord (= 8.0.4) - activestorage (= 8.0.4) - activesupport (= 8.0.4) + actionmailbox (8.1.2) + actionpack (= 8.1.2) + activejob (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) mail (>= 2.8.0) - actionmailer (8.0.4) - actionpack (= 8.0.4) - actionview (= 8.0.4) - activejob (= 8.0.4) - activesupport (= 8.0.4) + actionmailer (8.1.2) + actionpack (= 8.1.2) + actionview (= 8.1.2) + activejob (= 8.1.2) + activesupport (= 8.1.2) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.0.4) - actionview (= 8.0.4) - activesupport (= 8.0.4) + actionpack (8.1.2) + actionview (= 8.1.2) + activesupport (= 8.1.2) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -256,33 +258,34 @@ GEM actionpack-xml_parser (2.0.1) actionpack (>= 5.0) railties (>= 5.0) - actiontext (8.0.4) - actionpack (= 8.0.4) - activerecord (= 8.0.4) - activestorage (= 8.0.4) - activesupport (= 8.0.4) + actiontext (8.1.2) + action_text-trix (~> 2.1.15) + actionpack (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.0.4) - activesupport (= 8.0.4) + actionview (8.1.2) + activesupport (= 8.1.2) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) active_record_doctor (2.0.1) activerecord (>= 7.0.0) - activejob (8.0.4) - activesupport (= 8.0.4) + activejob (8.1.2) + activesupport (= 8.1.2) globalid (>= 0.3.6) - activemodel (8.0.4) - activesupport (= 8.0.4) + activemodel (8.1.2) + activesupport (= 8.1.2) activemodel-serializers-xml (1.0.3) activemodel (>= 5.0.0.a) activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (8.0.4) - activemodel (= 8.0.4) - activesupport (= 8.0.4) + activerecord (8.1.2) + activemodel (= 8.1.2) + activesupport (= 8.1.2) timeout (>= 0.4.0) activerecord-import (2.2.0) activerecord (>= 4.2) @@ -294,20 +297,20 @@ GEM cgi (>= 0.3.6) rack (>= 2.0.8, < 4) railties (>= 7.0) - activestorage (8.0.4) - actionpack (= 8.0.4) - activejob (= 8.0.4) - activerecord (= 8.0.4) - activesupport (= 8.0.4) + activestorage (8.1.2) + actionpack (= 8.1.2) + activejob (= 8.1.2) + activerecord (= 8.1.2) + activesupport (= 8.1.2) marcel (~> 1.0) - activesupport (8.0.4) + activesupport (8.1.2) base64 - benchmark (>= 0.3) bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) + json logger (>= 1.4.2) minitest (>= 5.1) securerandom (>= 0.3) @@ -339,8 +342,8 @@ GEM awesome_nested_set (3.9.0) activerecord (>= 4.0.0, < 8.2) aws-eventstream (1.4.0) - aws-partitions (1.1202.0) - aws-sdk-core (3.241.3) + aws-partitions (1.1210.0) + aws-sdk-core (3.241.4) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -348,15 +351,15 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.120.0) - aws-sdk-core (~> 3, >= 3.241.3) + aws-sdk-kms (1.121.0) + aws-sdk-core (~> 3, >= 3.241.4) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.211.0) - aws-sdk-core (~> 3, >= 3.241.3) + aws-sdk-s3 (1.213.0) + aws-sdk-core (~> 3, >= 3.241.4) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sdk-sns (1.111.0) - aws-sdk-core (~> 3, >= 3.241.3) + aws-sdk-sns (1.112.0) + aws-sdk-core (~> 3, >= 3.241.4) aws-sigv4 (~> 1.5) aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) @@ -556,7 +559,7 @@ GEM factory_bot_rails (6.5.1) factory_bot (~> 6.5) railties (>= 6.1.0) - faraday (2.14.0) + faraday (2.14.1) faraday-net_http (>= 2.0, < 3.5) json logger @@ -724,7 +727,7 @@ GEM reline (>= 0.4.2) iso8601 (0.13.0) jmespath (1.6.2) - json (2.18.0) + json (2.18.1) json-jwt (1.17.0) activesupport (>= 4.2) aes_key_wrap @@ -803,8 +806,8 @@ GEM json_rpc_handler (~> 0.1) messagebird-rest (5.0.0) jwt (< 4) - meta-tags (2.22.2) - actionpack (>= 6.0.0, < 8.2) + meta-tags (2.22.3) + actionpack (>= 6.0.0) method_source (1.1.0) mime-types (3.7.0) logger @@ -879,7 +882,7 @@ GEM actionview openproject-octicons (= 19.32.0) railties - openproject-primer_view_components (0.80.2) + openproject-primer_view_components (0.81.1) actionview (>= 7.2.0) activesupport (>= 7.2.0) openproject-octicons (>= 19.30.1) @@ -1106,7 +1109,7 @@ GEM prawn-table (0.2.2) prawn (>= 1.3.0, < 3.0.0) prettyprint (0.2.0) - prism (1.8.0) + prism (1.9.0) prometheus-client-mmap (1.5.0) base64 bigdecimal @@ -1199,20 +1202,20 @@ GEM rackup (1.0.1) rack (< 3) webrick - rails (8.0.4) - actioncable (= 8.0.4) - actionmailbox (= 8.0.4) - actionmailer (= 8.0.4) - actionpack (= 8.0.4) - actiontext (= 8.0.4) - actionview (= 8.0.4) - activejob (= 8.0.4) - activemodel (= 8.0.4) - activerecord (= 8.0.4) - activestorage (= 8.0.4) - activesupport (= 8.0.4) + rails (8.1.2) + actioncable (= 8.1.2) + actionmailbox (= 8.1.2) + actionmailer (= 8.1.2) + actionpack (= 8.1.2) + actiontext (= 8.1.2) + actionview (= 8.1.2) + activejob (= 8.1.2) + activemodel (= 8.1.2) + activerecord (= 8.1.2) + activestorage (= 8.1.2) + activesupport (= 8.1.2) bundler (>= 1.15.0) - railties (= 8.0.4) + railties (= 8.1.2) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -1227,9 +1230,9 @@ GEM rails-i18n (8.1.0) i18n (>= 0.7, < 2) railties (>= 8.0.0, < 9) - railties (8.0.4) - actionpack (= 8.0.4) - activesupport (= 8.0.4) + railties (8.1.2) + actionpack (= 8.1.2) + activesupport (= 8.1.2) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -1423,7 +1426,7 @@ GEM unicode-display_width (>= 1.1.1, < 4) test-prof (1.4.4) text-hyphen (1.5.0) - thor (1.4.0) + thor (1.5.0) thread_safe (0.3.6) timecop (0.9.10) timeout (0.6.0) @@ -1548,7 +1551,7 @@ DEPENDENCIES auto_strip_attributes (~> 2.5) awesome_nested_set (~> 3.9.0) aws-sdk-core (~> 3.241) - aws-sdk-s3 (~> 1.211) + aws-sdk-s3 (~> 1.213) axe-core-rspec bcrypt (~> 3.1.6) bootsnap (~> 1.20.0) @@ -1623,7 +1626,7 @@ DEPENDENCIES matrix (~> 0.4.3) mcp (~> 0.4.0) md_to_pdf! - meta-tags (~> 2.22.2) + meta-tags (~> 2.22.3) mini_magick (~> 5.3.0) multi_json (~> 1.19.0) my_page! @@ -1652,7 +1655,7 @@ DEPENDENCIES openproject-octicons (~> 19.32.0) openproject-octicons_helper (~> 19.32.0) openproject-openid_connect! - openproject-primer_view_components (~> 0.80.2) + openproject-primer_view_components (~> 0.81.1) openproject-recaptcha! openproject-reporting! openproject-storages! @@ -1686,7 +1689,7 @@ DEPENDENCIES rack-test (~> 2.2.0) rack-timeout (~> 0.7.0) rack_session_access - rails (~> 8.0.4) + rails (~> 8.1.2) rails-controller-testing (~> 1.0.2) rails-i18n (~> 8.1.0) rbtrace @@ -1756,23 +1759,24 @@ DEPENDENCIES CHECKSUMS Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a - actioncable (8.0.4) sha256=aadb2bf2977b666cfeaa7dee66fd50e147559f78a8d55f6169e913502475e09f - actionmailbox (8.0.4) sha256=ed0b634a502fb63d1ba01ae025772e9d0261b7ba12e66389c736fcf4635cd80f - actionmailer (8.0.4) sha256=3b9270d8e19f0afb534b11c52f439937dc30028adcbbae2b244f3383ce75de4b - actionpack (8.0.4) sha256=0364c7582f32c8f404725fa30d3f6853f834c5f4964afd4a072b848c8a23cddb + action_text-trix (2.1.16) sha256=f645a2c21821b8449fd1d6770708f4031c91a2eedf9ef476e9be93c64e703a8a + actioncable (8.1.2) sha256=dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969 + actionmailbox (8.1.2) sha256=058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b + actionmailer (8.1.2) sha256=f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9 + actionpack (8.1.2) sha256=ced74147a1f0daafaa4bab7f677513fd4d3add574c7839958f7b4f1de44f8423 actionpack-xml_parser (2.0.1) sha256=40cb461ee99445314ab580a783fb7413580deb8b28113c9e70ecd7c1b334d5e6 - actiontext (8.0.4) sha256=40b3970268ac29b865685456b2586df5052d068fd0cb04acb2291e737cea2340 - actionview (8.0.4) sha256=5bd3c41ee7a59e14cf062bb5e4ee53c9a253d12fc13c8754cae368012e1a1648 + actiontext (8.1.2) sha256=0bf57da22a9c19d970779c3ce24a56be31b51c7640f2763ec64aa72e358d2d2d + actionview (8.1.2) sha256=80455b2588911c9b72cec22d240edacb7c150e800ef2234821269b2b2c3e2e5b active_record_doctor (2.0.1) sha256=7af0ac02195385c8f2f67d0e4ebe72b1fc79d65eaaf329e0db07f4d12a84069a - activejob (8.0.4) sha256=cbc8a85d0e168cb90a5629c8a36fe2d08ba840103d3aed3eee0c7beb784fccce - activemodel (8.0.4) sha256=8f4e4fac3cd104b1bf30419c3745206f6f724c0e2902a939b4113f4c90730dfd + activejob (8.1.2) sha256=908dab3713b101859536375819f4156b07bdf4c232cc645e7538adb9e302f825 + activemodel (8.1.2) sha256=e21358c11ce68aed3f9838b7e464977bc007b4446c6e4059781e1d5c03bcf33e activemodel-serializers-xml (1.0.3) sha256=fa1b16305e7254cc58a59c68833e3c0a593a59c8ab95d3be5aaea7cd9416c397 - activerecord (8.0.4) sha256=bda32c171799e5ca5460447d3b7272ed14447244e2497abf2107f87fc44cbf32 + activerecord (8.1.2) sha256=acfbe0cadfcc50fa208011fe6f4eb01cae682ebae0ef57145ba45380c74bcc44 activerecord-import (2.2.0) sha256=f8ca99b196e50775723d1f1d192c379f656378dc9f5628240992a0d78807fa4b activerecord-nulldb-adapter (1.2.2) sha256=01e0b2e49af11ad56a92e274a3d8c9fb3c50a12a5460218c4c4b45355d9ef968 activerecord-session_store (2.2.0) sha256=65918054573683bf4f87af89e765e1fece14c9d71cfac1f11abe4687c96e2743 - activestorage (8.0.4) sha256=47f312962fc898c1669f20cf7448d19668a5547f4a5f64e59a837d9d3f64a043 - activesupport (8.0.4) sha256=894a3a6c7733b5fae5a7df3acd76c4b563f38687df8a04fa3cbd25360f3fe95a + activestorage (8.1.2) sha256=8a63a48c3999caeee26a59441f813f94681fc35cc41aba7ce1f836add04fba76 + activesupport (8.1.2) sha256=88842578ccd0d40f658289b0e8c842acfe9af751afee2e0744a7873f50b6fdae acts_as_list (1.2.6) sha256=8345380900b7bee620c07ad00991ccee59af3d8c9e8574f426e321da2865fdc8 acts_as_tree (2.9.1) sha256=b869eb10a8de38616b64ffcf9e882d3d99c8e06909c4057078a76c3b89a9a2f3 addressable (2.8.8) sha256=7c13b8f9536cf6364c03b9d417c19986019e28f7c00ac8132da4eb0fe393b057 @@ -1788,11 +1792,11 @@ CHECKSUMS auto_strip_attributes (2.6.0) sha256=a7e2e0cf744de2bcd947fd68014220702bcc88c81274c1cd9ce6f7316aae39b0 awesome_nested_set (3.9.0) sha256=3ce99e816550f97f4de118e621630070aacf24928b920fe4a68846578a8daaed aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b - aws-partitions (1.1202.0) sha256=c8aa0f134a23464c61cfd00edfb4b6d968b01847a8b591d4dcc0c63a4897c301 - aws-sdk-core (3.241.3) sha256=c7c445ecf1c601c860fd537458b2eb8df0c5df01e63c371849e6594e6b1d4f47 - aws-sdk-kms (1.120.0) sha256=a206ac6f62efbe971f802e8399d2702496a5c5bc800abcf94ead87bdddfdfd80 - aws-sdk-s3 (1.211.0) sha256=2ae5feb09ff4862462824f267b76601ed16922a15de56cf51e4fa99bc5b3f519 - aws-sdk-sns (1.111.0) sha256=195edbd6953d4caa2748bd4861e0fe8df54d90aadf07a0ac268987b946c7e5be + aws-partitions (1.1210.0) sha256=04143b868f8b3fc481f68552df6a430f1083a56e2afec5a6bc5c89532ab423fe + aws-sdk-core (3.241.4) sha256=a42ccba8c24ea9800e7b6c40aa201c967458f7c460044a6eebf64fbf1226e4fd + aws-sdk-kms (1.121.0) sha256=d563c1cfb4b5754efbc671216c8eca875338748adad0f42518c28dfa0a2d01e0 + aws-sdk-s3 (1.213.0) sha256=af596ccf544582406db610e95cc9099276eaf03142f57a2f30f76940e598e50d + aws-sdk-sns (1.112.0) sha256=aff1b1b5bbcb4229599221c558a41790c1cd1a1fed47ac3d27d27512ad24b254 aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 axe-core-api (4.11.0) sha256=3d9c94e3c8f8f9b8f154a3ce036b3dec2dabf7bb7de5e51d663b18bd8a0d691b axe-core-rspec (4.11.0) sha256=3c3e3ef3863d9f5243e056b7da328932c0b6682dda299bb4bd74d760641486d7 @@ -1881,7 +1885,7 @@ CHECKSUMS excon (1.3.2) sha256=a089babe98638e58042a7d542b2bbd183304527e33d612b6dde22fa491a544a5 factory_bot (6.5.6) sha256=12beb373214dccc086a7a63763d6718c49769d5606f0501e0a4442676917e077 factory_bot_rails (6.5.1) sha256=d3cc4851eae4dea8a665ec4a4516895045e710554d2b5ac9e68b94d351bc6d68 - faraday (2.14.0) sha256=8699cfe5d97e55268f2596f9a9d5a43736808a943714e3d9a53e6110593941cd + faraday (2.14.1) sha256=a43cceedc1e39d188f4d2cdd360a8aaa6a11da0c407052e426ba8d3fb42ef61c faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c fastimage (2.4.0) sha256=5fce375e27d3bdbb46c18dbca6ba9af29d3304801ae1eb995771c4796c5ac7e8 @@ -1948,7 +1952,7 @@ CHECKSUMS irb (1.16.0) sha256=2abe56c9ac947cdcb2f150572904ba798c1e93c890c256f8429981a7675b0806 iso8601 (0.13.0) sha256=298c2b15b7be5fa95a1372813d36a2257656cd8e906dfbc1f5cb409851425aa2 jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1 - json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505 + json (2.18.1) sha256=fe112755501b8d0466b5ada6cf50c8c3f41e897fa128ac5d263ec09eedc9f986 json-jwt (1.17.0) sha256=6ff99026b4c54281a9431179f76ceb81faa14772d710ef6169785199caadc4cc json-schema (4.3.1) sha256=d5e68dc32b94408d0b06ad04f9382ccbb6fe5a44910e066f8547f56c471a7825 json_rpc_handler (0.1.1) sha256=ea248c8cb4d5490dde320db316ac5e3caf8137a20b5ff9035a4bfc1d19438d90 @@ -1975,7 +1979,7 @@ CHECKSUMS mcp (0.4.0) sha256=4d1dd2b99fbd81a5fdc808d258c38a4f57dd69751ee1e5f62b3ab40e31625a36 md_to_pdf (0.2.5) messagebird-rest (5.0.0) sha256=da4cc1efba3d5e4aa021fad07426c2cb6b326ce5670da5104bb8f6056a39d59c - meta-tags (2.22.2) sha256=7fe78af4a92be12091f473cb84a21f6bddbd37f24c4413172df76cd14fff9e83 + meta-tags (2.22.3) sha256=41ead5437140869717cbdd659cc6f1caa3e498b3e74b03ed63503b5b38ed504f method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5 mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56 mime-types-data (3.2025.0924) sha256=f276bca15e59f35767cbcf2bc10e023e9200b30bd6a572c1daf7f4cc24994728 @@ -2028,7 +2032,7 @@ CHECKSUMS openproject-octicons (19.32.0) sha256=e9c908e7c4310d57e1dece8fc506339862f18b67b3b67d549e8f56a7b763d48b openproject-octicons_helper (19.32.0) sha256=687a8b173c6436634397477c1f05b0a575e52745a5cc1aef03351272e73e3832 openproject-openid_connect (1.0.0) - openproject-primer_view_components (0.80.2) sha256=d36d9fd48857f3dbcdd0e7a408ef9ce01211a9b09e6186ad2413b300f2e50a8e + openproject-primer_view_components (0.81.1) sha256=ebda313d71f4c7e82b9a31e0d54b1e2b5ac3776816161fb61517ced1d945587d openproject-recaptcha (1.0.0) openproject-reporting (1.0.0) openproject-storages (1.0.0) @@ -2119,7 +2123,7 @@ CHECKSUMS prawn (2.4.0) sha256=82062744f7126c2d77501da253a154271790254dfa8c309b8e52e79bc5de2abd prawn-table (0.2.2) sha256=336d46e39e003f77bf973337a958af6a68300b941c85cb22288872dc2b36addb prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193 - prism (1.8.0) sha256=84453a16ef5530ea62c5f03ec16b52a459575ad4e7b9c2b360fd8ce2c39c1254 + prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85 prometheus-client-mmap (1.5.0) sha256=361eb98d6c19ae0f44ae5e02f9e6750436fd92d1c501d1c69843609c1daf0117 prometheus-client-mmap (1.5.0-aarch64-linux-gnu) sha256=e7fe1a630dda83a237efb0eb4a29ee3da37922722fc89ecac6057a1187372c5d prometheus-client-mmap (1.5.0-aarch64-linux-musl) sha256=897fa5d82150ddcb3bc30dfa7af0deb85930655500e71bd8879daa86b5e690ff @@ -2149,12 +2153,12 @@ CHECKSUMS rack-timeout (0.7.0) sha256=757337e9793cca999bb73a61fe2a7d4280aa9eefbaf787ce3b98d860749c87d9 rack_session_access (0.2.0) sha256=03eb98f2027429ccbbeb18556006dfb6d928b0557ad3770783b8e2f368198d6b rackup (1.0.1) sha256=ba86604a28989fe1043bff20d819b360944ca08156406812dca6742b24b3c249 - rails (8.0.4) sha256=364494a32d2dc3f9d5c135d036ce47e7776684bc6add73f1037ac2b1007962db + rails (8.1.2) sha256=5069061b23dfa8706b9f0159ae8b9d35727359103178a26962b868a680ba7d95 rails-controller-testing (1.0.5) sha256=741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94 rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d rails-html-sanitizer (1.6.2) sha256=35fce2ca8242da8775c83b6ba9c1bcaad6751d9eb73c1abaa8403475ab89a560 rails-i18n (8.1.0) sha256=52d5fd6c0abef28d84223cc05647f6ae0fd552637a1ede92deee9545755b6cf3 - railties (8.0.4) sha256=8203d853dcffab4abcdd05c193f101676a92068075464694790f6d8f72d5cb47 + railties (8.1.2) sha256=1289ece76b4f7668fc46d07e55cc992b5b8751f2ad85548b7da351b8c59f8055 rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c rake-compiler-dock (1.11.0) sha256=eab51f2cd533eb35cea6b624a75281f047123e70a64c58b607471bb49428f8c2 @@ -2237,7 +2241,7 @@ CHECKSUMS terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2 test-prof (1.4.4) sha256=1a59513ed9d33a1f5ca17c0b89da4e70f60a91c83ec62e9a873dbb99141353ef text-hyphen (1.5.0) sha256=c44a4533b8a554e7ff7c955e131bcccc78a0b4c56ce1d73f2c8c11f43b075a06 - thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d + thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 thread_safe (0.3.6) sha256=9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a timecop (0.9.10) sha256=12ba45ce57cdcf6b1043cb6cdffa6381fd89ce10d369c28a7f6f04dc1b0cd8eb timeout (0.6.0) sha256=6d722ad619f96ee383a0c557ec6eb8c4ecb08af3af62098a0be5057bf00de1af diff --git a/app/components/_index.sass b/app/components/_index.sass index 9265c6cbe8c..589d8517fb8 100644 --- a/app/components/_index.sass +++ b/app/components/_index.sass @@ -7,6 +7,7 @@ @import "open_project/common/attribute_help_text_component" @import "open_project/common/attribute_help_text_caption_component" @import "open_project/common/attribute_label_component" +@import "open_project/common/inplace_edit_fields/index" @import "open_project/common/submenu_component" @import "open_project/common/main_menu_toggle_component" @import "portfolios/details_component" diff --git a/app/components/admin/custom_fields/edit_form_header_component.html.erb b/app/components/admin/custom_fields/edit_form_header_component.html.erb index 66808da18b3..477904b211d 100644 --- a/app/components/admin/custom_fields/edit_form_header_component.html.erb +++ b/app/components/admin/custom_fields/edit_form_header_component.html.erb @@ -29,9 +29,9 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Primer::OpenProject::PageHeader.new(test_selector: "custom-fields--page-header")) do |header| - header.with_title { @custom_field.attribute_in_database("name") } + header.with_title { page_title } - header.with_breadcrumbs(breadcrumbs_items) + header.with_breadcrumbs(breadcrumbs_items, selected_item_font_weight: :normal) helpers.render_tab_header_nav(header, tabs, test_selector: :custom_field_detail_header) end diff --git a/app/components/admin/custom_fields/edit_form_header_component.rb b/app/components/admin/custom_fields/edit_form_header_component.rb index 15c3d514a86..00987bf65c2 100644 --- a/app/components/admin/custom_fields/edit_form_header_component.rb +++ b/app/components/admin/custom_fields/edit_form_header_component.rb @@ -76,11 +76,19 @@ module Admin private + def page_title + concat @custom_field.attribute_in_database("name") + concat render(Primer::Beta::Text.new(color: :muted)) { " (#{helpers.label_for_custom_field_format(@custom_field.field_format)})" } + end + def breadcrumbs_items - [{ href: admin_index_path, text: t(:label_administration) }, - { href: custom_fields_path, text: t(:label_custom_field_plural) }, - { href: custom_fields_path(tab: @custom_field.type), text: I18n.t(@custom_field.type_name) }, - @custom_field.attribute_in_database("name")] + [ + { href: admin_index_path, text: t(:label_administration) }, + { href: custom_fields_path, text: t(:label_custom_field_plural) }, + { href: custom_fields_path(tab: @custom_field.type), text: I18n.t(@custom_field.type_name) }, + helpers.nested_breadcrumb_element(helpers.label_for_custom_field_format(model.field_format), + @custom_field.attribute_in_database("name")) + ] end end end diff --git a/app/components/members/role_form_component.html.erb b/app/components/members/role_form_component.html.erb index 01567d6f650..dd6a73754f7 100644 --- a/app/components/members/role_form_component.html.erb +++ b/app/components/members/role_form_component.html.erb @@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= form_with model: member.new_record? ? [member.project, member] : member, +<%= form_with model: [member.project, member], builder: TabularFormBuilder, html: form_html_options do |f| %> <%= hidden_field_tag :page, params[:page].to_i %> diff --git a/app/components/messages/show_page_header_component.html.erb b/app/components/messages/show_page_header_component.html.erb index 9181b253ee9..57396fac845 100644 --- a/app/components/messages/show_page_header_component.html.erb +++ b/app/components/messages/show_page_header_component.html.erb @@ -29,7 +29,7 @@ mobile_icon: :pencil, mobile_label: t(:button_edit), size: :medium, - href: edit_topic_path(@topic), + href: edit_project_forum_topic_path(@topic.forum.project, @topic.forum, @topic), aria: { label: t(:button_edit) }, data: { test_selector: "message-edit-button" }, title: t(:button_edit) @@ -46,7 +46,7 @@ mobile_icon: :trash, mobile_label: t(:button_delete), size: :medium, - href: topic_path(@topic), + href: project_forum_topic_path(@topic.forum.project, @topic.forum, @topic), aria: { label: I18n.t(:button_delete) }, data: { turbo_confirm: I18n.t(:text_are_you_sure), diff --git a/app/components/my/access_token/api_tokens_section_component.rb b/app/components/my/access_token/api_tokens_section_component.rb index 8c2625a9415..932141cf02b 100644 --- a/app/components/my/access_token/api_tokens_section_component.rb +++ b/app/components/my/access_token/api_tokens_section_component.rb @@ -56,7 +56,7 @@ module My def token_available? case token_type.to_s - when "Token::API" then Setting.rest_api_enabled? + when "Token::API" then Setting.api_tokens_enabled? when "Token::ICalMeeting" then Setting.ical_enabled? when "Token::RSS" then Setting.feeds_enabled? else raise ArgumentError, "Unknown token type: #{token_type}" diff --git a/app/components/open_project/common/attribute_component.rb b/app/components/open_project/common/attribute_component.rb index 4bb31884475..5ebbdb8a56d 100644 --- a/app/components/open_project/common/attribute_component.rb +++ b/app/components/open_project/common/attribute_component.rb @@ -37,11 +37,11 @@ module OpenProject :description, :lines, :background_reference_id, - :formatted + :format PARAGRAPH_CSS_CLASS = "op-uc-p" - def initialize(id, name, description, lines: 1, background_reference_id: "content", formatted: false, **args) + def initialize(id, name, description, lines: 1, background_reference_id: "content", format: true, **args) super() @id = id @name = name @@ -49,7 +49,7 @@ module OpenProject @system_arguments = args @lines = lines @background_reference_id = background_reference_id - @formatted = formatted + @format = format end def short_text @@ -61,7 +61,7 @@ module OpenProject end def full_text - @full_text ||= formatted ? description : helpers.format_text(description) + @full_text ||= format ? helpers.format_text(description) : description end def display_expand_button_value diff --git a/app/components/open_project/common/inplace_edit_field_component.html.erb b/app/components/open_project/common/inplace_edit_field_component.html.erb new file mode 100644 index 00000000000..01251f341c5 --- /dev/null +++ b/app/components/open_project/common/inplace_edit_field_component.html.erb @@ -0,0 +1,20 @@ +<%= component_wrapper(tag: :div, class: "op-inplace-edit", data: { test_selector: wrapper_test_selector }) do %> + <% if display_field_component.present? && !enforce_edit_mode %> + <%= render display_field_component %> + <% else %> + <%= primer_form_with( + model:, + url: inplace_edit_field_update_path(model: model.class.name, id: model.id, attribute:), + method: :patch, + data: { turbo_stream: true } + ) do |form| + render_field_component = ->(f) { render edit_field_component(f) } # The render_inline_form method looses context and thus does not know about the `field_component` method + system_arguments = @system_arguments + + render_inline_form(form) do |f| + f.hidden name: "system_arguments_json", value: system_arguments.to_json + render_field_component.call(f) + end + end %> + <% end %> +<% end %> diff --git a/app/components/open_project/common/inplace_edit_field_component.rb b/app/components/open_project/common/inplace_edit_field_component.rb new file mode 100644 index 00000000000..64fe1e2e50f --- /dev/null +++ b/app/components/open_project/common/inplace_edit_field_component.rb @@ -0,0 +1,98 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject + module Common + class InplaceEditFieldComponent < ViewComponent::Base + include OpTurbo::Streamable + + attr_reader :model, :attribute, :enforce_edit_mode + + def initialize(model:, attribute:, enforce_edit_mode: false, **system_arguments) + super() + @model = model + @attribute = attribute + @enforce_edit_mode = enforce_edit_mode + @system_arguments = system_arguments + @system_arguments[:id] = system_arguments[:id] || SecureRandom.uuid + end + + def field_class + OpenProject::InplaceEdit::FieldRegistry.fetch(attribute) + end + + def edit_field_component(form) + field_class.new( + form:, + attribute:, + model:, + **@system_arguments + ) + end + + def display_field_class + if field_class.respond_to?(:display_class) + field_class.display_class + else + InplaceEditFields::DisplayFields::DisplayFieldComponent + end + end + + def display_field_component + return nil if display_field_class.nil? + + display_field_class.new(model:, attribute:, writable: writable?, **@system_arguments) + end + + def wrapper_key + model_class = @model.class.name.parameterize(separator: "_") + "op-inplace-edit-field--#{model_class}-#{model.id}--#{attribute.name}--#{@system_arguments[:id]}" + end + + def wrapper_test_selector + "op-inplace-edit-field" + end + + private + + def writable? + return @writable if defined?(@writable) + + contract_class = OpenProject::InplaceEdit::UpdateRegistry.fetch_contract(model) + @writable = + if contract_class.present? + contract_class.new(model, User.current).writable?(attribute) + else + false + end + end + end + end +end diff --git a/app/components/open_project/common/inplace_edit_fields/display_fields/display_field_component.rb b/app/components/open_project/common/inplace_edit_fields/display_fields/display_field_component.rb new file mode 100644 index 00000000000..7be8c96cfac --- /dev/null +++ b/app/components/open_project/common/inplace_edit_fields/display_fields/display_field_component.rb @@ -0,0 +1,89 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject + module Common + module InplaceEditFields + module DisplayFields + class DisplayFieldComponent < ViewComponent::Base + include OpenProject::TextFormatting + + attr_reader :model, :attribute, :writable + + def initialize(model:, attribute:, writable:, **system_arguments) + super() + @model = model + @attribute = attribute + @writable = writable + @system_arguments = system_arguments + end + + def render_display_value + value = model.public_send(attribute) + + if value.present? + format_text(value) + else + "–" + end + end + + def display_field_arguments + @display_field_arguments ||= { + classes: "op-inplace-edit--display-field #{'op-inplace-edit--display-field_editable' if writable}", + data: { + controller: "inplace-edit", + inplace_edit_url_value: edit_url, + action: writable ? "click->inplace-edit#request" : "" + } + } + end + + def call + render(Primer::BaseComponent.new(tag: :div, **display_field_arguments)) do + render_display_value + end + end + + private + + def edit_url + inplace_edit_field_edit_path( + model: model.class.name, + id: model.id, + attribute:, + system_arguments_json: @system_arguments.to_json + ) + end + end + end + end + end +end diff --git a/app/components/open_project/common/inplace_edit_fields/display_fields/display_fields_component.sass b/app/components/open_project/common/inplace_edit_fields/display_fields/display_fields_component.sass new file mode 100644 index 00000000000..e38a6a57830 --- /dev/null +++ b/app/components/open_project/common/inplace_edit_fields/display_fields/display_fields_component.sass @@ -0,0 +1,16 @@ +.op-inplace-edit + &--display-field + &_editable + margin-left: -9px !important // cancel out 8px padding + 1px border + margin-right: -9px !important // cancel out 8px padding + 1px border + padding: var(--base-size-8) + width: calc(100% + 18px) !important + border: 1px solid transparent + border-radius: var(--borderRadius-medium) + + &:hover, &:focus + border-color: var(--borderColor-default) + box-shadow: var(--shadow-inset) + + &:not(&_editable) + cursor: not-allowed diff --git a/app/components/open_project/common/inplace_edit_fields/display_fields/rich_text_area_component.rb b/app/components/open_project/common/inplace_edit_fields/display_fields/rich_text_area_component.rb new file mode 100644 index 00000000000..f582d282d9c --- /dev/null +++ b/app/components/open_project/common/inplace_edit_fields/display_fields/rich_text_area_component.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject + module Common + module InplaceEditFields + module DisplayFields + class RichTextAreaComponent < DisplayFieldComponent + attr_reader :model, :attribute, :writable + + def call + render(Primer::BaseComponent.new(tag: :div, **display_field_arguments)) do + render(Primer::BaseComponent.new(tag: :div, + classes: "op-uc-container op-uc-container_reduced-headings -multiline")) do + render_display_value + end + end + end + end + end + end + end +end diff --git a/app/components/open_project/common/inplace_edit_fields/index.sass b/app/components/open_project/common/inplace_edit_fields/index.sass new file mode 100644 index 00000000000..a45f5bcaf86 --- /dev/null +++ b/app/components/open_project/common/inplace_edit_fields/index.sass @@ -0,0 +1 @@ +@import "display_fields/display_fields_component" diff --git a/app/components/open_project/common/inplace_edit_fields/rich_text_area_component.rb b/app/components/open_project/common/inplace_edit_fields/rich_text_area_component.rb new file mode 100644 index 00000000000..a3bb16fbf58 --- /dev/null +++ b/app/components/open_project/common/inplace_edit_fields/rich_text_area_component.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject + module Common + module InplaceEditFields + class RichTextAreaComponent < ViewComponent::Base + attr_reader :form, :attribute, :model + + def self.display_class + DisplayFields::RichTextAreaComponent + end + + def initialize(form:, attribute:, model:, **system_arguments) + super() + @form = form + @attribute = attribute + @model = model + @system_arguments = system_arguments + @system_arguments[:classes] = class_names( + @system_arguments[:classes], + "op-inplace-edit-field--text-area" + ) + @system_arguments[:label] ||= model.class.human_attribute_name(attribute) + + @system_arguments[:rich_text_options] ||= {} + @system_arguments[:rich_text_options][:primerized] = true + end + + def call + form.rich_text_area(name: attribute, **@system_arguments) + + form.group(layout: :horizontal, justify_content: :flex_end) do |button_group| + button_group.submit(name: :reset, + type: :submit, + label: I18n.t(:button_cancel), + scheme: :default, + formaction: inplace_edit_field_reset_path(model: model.class.name, id: model.id, attribute:), + formmethod: :get, + test_selector: "op-inplace-edit-field--textarea-cancel") + button_group.submit(name: :submit, + label: I18n.t(:button_save), + scheme: :primary, + test_selector: "op-inplace-edit-field--textarea-save") + end + end + end + end + end +end diff --git a/app/components/open_project/common/inplace_edit_fields/text_input_component.rb b/app/components/open_project/common/inplace_edit_fields/text_input_component.rb new file mode 100644 index 00000000000..ae71deeb9b0 --- /dev/null +++ b/app/components/open_project/common/inplace_edit_fields/text_input_component.rb @@ -0,0 +1,71 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module OpenProject + module Common + module InplaceEditFields + class TextInputComponent < ViewComponent::Base + attr_reader :form, :attribute, :model + + def self.display_class + DisplayFields::DisplayFieldComponent + end + + def initialize(form:, attribute:, model:, **system_arguments) + super() + @form = form + @attribute = attribute + @model = model + @system_arguments = system_arguments + @system_arguments[:label] ||= model.class.human_attribute_name(attribute) + end + + def call + form.text_field name: attribute, + data: { controller: "inplace-edit", + inplace_edit_url_value: reset_url, + action: "keydown.esc->inplace-edit#request" }, + **@system_arguments + end + + private + + def reset_url + inplace_edit_field_reset_path( + model: model.class.name, + id: model.id, + attribute:, + system_arguments_json: @system_arguments.to_json + ) + end + end + end + end +end diff --git a/app/components/projects/row_component.rb b/app/components/projects/row_component.rb index 555b6152a09..6c783553b23 100644 --- a/app/components/projects/row_component.rb +++ b/app/components/projects/row_component.rb @@ -83,7 +83,7 @@ module Projects end def custom_field_column(column) # rubocop:disable Metrics/AbcSize - return nil unless user_can_view_project? + return nil unless user_can_view_project_attributes? cf = column.custom_field custom_value = project.formatted_custom_value_for(cf) @@ -93,7 +93,7 @@ module Projects "dialog-#{project.id}-cf-#{cf.id}", cf.name, custom_value, - formatted: true + format: false # already formatted ) elsif custom_value.is_a?(Array) safe_join(Array(custom_value).compact_blank, ", ") @@ -196,7 +196,7 @@ module Projects end def project_status - return nil unless user_can_view_project? + return nil unless user_can_view_project_attributes? content = "".html_safe @@ -212,7 +212,7 @@ module Projects end def status_explanation - return nil unless user_can_view_project? + return nil unless user_can_view_project_attributes? if project.status_explanation.present? && project.status_explanation render OpenProject::Common::AttributeComponent.new("dialog-#{project.id}-status-explanation", @@ -222,7 +222,7 @@ module Projects end def description - return nil unless user_can_view_project? + return nil unless user_can_view_project_attributes? if project.description.present? render OpenProject::Common::AttributeComponent.new("dialog-#{project.id}-description", @@ -436,7 +436,7 @@ module Projects end end - def user_can_view_project? + def user_can_view_project_attributes? User.current.allowed_in_project?(:view_project_attributes, project) end diff --git a/app/components/settings/project_custom_fields/edit_form_header_component.html.erb b/app/components/settings/project_custom_fields/edit_form_header_component.html.erb index a9eeafa8c8c..ac46b0e0433 100644 --- a/app/components/settings/project_custom_fields/edit_form_header_component.html.erb +++ b/app/components/settings/project_custom_fields/edit_form_header_component.html.erb @@ -28,9 +28,9 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <%= render(Primer::OpenProject::PageHeader.new) do |header| - header.with_title { @custom_field.attribute_in_database("name") } + header.with_title { page_title } header.with_description { t("settings.project_attributes.edit.description") } unless hide_description? - header.with_breadcrumbs(breadcrumbs_items) + header.with_breadcrumbs(breadcrumbs_items, selected_item_font_weight: :normal) helpers.render_tab_header_nav(header, tabs, test_selector: :project_attribute_detail_header) end diff --git a/app/components/settings/project_custom_fields/edit_form_header_component.rb b/app/components/settings/project_custom_fields/edit_form_header_component.rb index 331fa5f2a1f..4f52c6e58d1 100644 --- a/app/components/settings/project_custom_fields/edit_form_header_component.rb +++ b/app/components/settings/project_custom_fields/edit_form_header_component.rb @@ -78,11 +78,19 @@ module Settings tabs end + def page_title + concat @custom_field.attribute_in_database("name") + concat render(Primer::Beta::Text.new(color: :muted)) { " (#{helpers.label_for_custom_field_format(@custom_field.field_format)})" } + end + def breadcrumbs_items - [{ href: admin_index_path, text: t("label_administration") }, - { href: admin_settings_project_custom_fields_path, text: t("label_project_plural") }, - { href: admin_settings_project_custom_fields_path, text: t("settings.project_attributes.heading") }, - @custom_field.attribute_in_database("name")] + [ + { href: admin_index_path, text: t("label_administration") }, + { href: admin_settings_project_custom_fields_path, text: t("label_project_plural") }, + { href: admin_settings_project_custom_fields_path, text: t("settings.project_attributes.heading") }, + helpers.nested_breadcrumb_element(helpers.label_for_custom_field_format(@custom_field.field_format), + @custom_field.attribute_in_database("name")) + ] end def hide_description? diff --git a/app/components/settings/project_custom_fields/new_form_header_component.html.erb b/app/components/settings/project_custom_fields/new_form_header_component.html.erb index 01e7fc54296..962383901ef 100644 --- a/app/components/settings/project_custom_fields/new_form_header_component.html.erb +++ b/app/components/settings/project_custom_fields/new_form_header_component.html.erb @@ -29,7 +29,7 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Primer::OpenProject::PageHeader.new) do |header| - header.with_title { t("settings.project_attributes.new.heading") } + header.with_title { page_title } header.with_description { t("settings.project_attributes.new.description") } unless hide_description? header.with_breadcrumbs(breadcrumb_items, selected_item_font_weight: :normal) end diff --git a/app/components/settings/project_custom_fields/new_form_header_component.rb b/app/components/settings/project_custom_fields/new_form_header_component.rb index ef41034fb11..5595bcc5549 100644 --- a/app/components/settings/project_custom_fields/new_form_header_component.rb +++ b/app/components/settings/project_custom_fields/new_form_header_component.rb @@ -31,6 +31,11 @@ module Settings module ProjectCustomFields class NewFormHeaderComponent < ApplicationComponent + def page_title + concat t("settings.project_attributes.new.heading") + concat render(Primer::Beta::Text.new(color: :muted)) { " (#{helpers.label_for_custom_field_format(model.field_format)})" } + end + def breadcrumb_items [ { href: admin_index_path, text: t("label_administration") }, diff --git a/app/components/shares/permission_button_component.rb b/app/components/shares/permission_button_component.rb index 83f0c8162f7..a356df40a57 100644 --- a/app/components/shares/permission_button_component.rb +++ b/app/components/shares/permission_button_component.rb @@ -29,7 +29,7 @@ #++ module Shares - class PermissionButtonComponent < ApplicationComponent # rubocop:disable OpenProject/AddPreviewForViewComponent + class PermissionButtonComponent < ApplicationComponent include ApplicationHelper include OpPrimer::ComponentHelpers include OpTurbo::Streamable diff --git a/app/components/users/hover_card_component.rb b/app/components/users/hover_card_component.rb index 52da4ee0ced..7b04ba87313 100644 --- a/app/components/users/hover_card_component.rb +++ b/app/components/users/hover_card_component.rb @@ -34,7 +34,7 @@ class Users::HoverCardComponent < ApplicationComponent def initialize(id:) super - @user = User.find_by(id:) + @user = User.visible.find_by(id:) end def render? diff --git a/app/contracts/projects/update_contract.rb b/app/contracts/projects/update_contract.rb index d3397ed1d94..d2d3f4a37f9 100644 --- a/app/contracts/projects/update_contract.rb +++ b/app/contracts/projects/update_contract.rb @@ -31,14 +31,14 @@ module Projects class UpdateContract < BaseContract def writable_attributes - if allow_project_attributes_only + if allow_project_attributes_only? with_available_custom_fields_only(super) - elsif allow_edit_attributes_only + elsif allow_edit_attributes_only? without_custom_fields(super) - elsif allow_all_attributes + elsif allow_all_attributes? # When all attributes are updated (API-only case), allow writing to all available custom # fields (including disabled ones) to maintain backward compatibility with the API. - with_all_available_custom_fields_only(super) + with_all_available_custom_fields(super) else [] end @@ -46,28 +46,31 @@ module Projects private - def project_attributes_only = options[:project_attributes_only].present? + def project_attributes_only? = options[:project_attributes_only].present? - def edit_project = user.allowed_in_project?(:edit_project, model) + def allow_edit_project? = user.allowed_in_project?(:edit_project, model) - def edit_project_attributes = user.allowed_in_project?(:edit_project_attributes, model) + def allow_edit_project_attributes? = user.allowed_in_project?(:edit_project_attributes, model) - def allow_edit_attributes_only = edit_project && !project_attributes_only && !edit_project_attributes - - def allow_project_attributes_only - edit_project_attributes && (project_attributes_only || !edit_project) + def allow_edit_attributes_only? + allow_edit_project? && !project_attributes_only? && !allow_edit_project_attributes? end - def allow_all_attributes - (edit_project && edit_project_attributes && !project_attributes_only) || - (changed_by_user == ["active"]) # Allow archiving, permission checked in manage_permission + def allow_project_attributes_only? + allow_edit_project_attributes? && (project_attributes_only? || !allow_edit_project?) + end + + def allow_all_attributes? + return true if allow_edit_project? && allow_edit_project_attributes? && !project_attributes_only? + + changed_by_user == ["active"] # Allow archiving, permission checked in manage_permission end def without_custom_fields(changes) = changes.grep_v(/^custom_field_/) def with_available_custom_fields_only(changes) = changes & available_custom_fields.map(&:attribute_name) - def with_all_available_custom_fields_only(changes) + def with_all_available_custom_fields(changes) allowed_attributes = changes.grep_v(/^custom_field_/) allowed_attributes += changes & all_available_custom_fields.map(&:attribute_name) allowed_attributes @@ -76,7 +79,7 @@ module Projects def manage_permission if changed_by_user == ["active"] :archive_project - elsif project_attributes_only + elsif project_attributes_only? :edit_project_attributes else # if "active" is changed, :archive_project permission will also be diff --git a/app/contracts/work_packages/base_contract.rb b/app/contracts/work_packages/base_contract.rb index 9e176e846b0..b26b1d1c309 100644 --- a/app/contracts/work_packages/base_contract.rb +++ b/app/contracts/work_packages/base_contract.rb @@ -131,6 +131,14 @@ module WorkPackages attribute :budget + validates :subject, + presence: true, + unless: -> { model.type&.replacement_pattern_defined_for?(:subject) } + validates :subject, length: { maximum: 255 } + + # TODO: add validation, check permission (#71253) + attribute :sprint_id + validates :due_date, date: { after_or_equal_to: :start_date, message: :greater_than_or_equal_to_start_date, @@ -239,6 +247,16 @@ module WorkPackages def valid?(context = :saving_custom_fields) = super + def writable_attributes + attributes = super + + unless auto_generated_attributes_writable? + attributes -= auto_generated_attribute_names + end + + attributes + end + private def validate_after_soonest_start(date_attribute) @@ -680,5 +698,11 @@ module WorkPackages def leaf_or_manually_scheduled? model.leaf? || model.schedule_manually? end + + def auto_generated_attributes_writable? = false + + def auto_generated_attribute_names + (model.type && model.type.enabled_patterns&.keys&.map(&:to_s)) || [] + end end end diff --git a/app/contracts/work_packages/copy_contract.rb b/app/contracts/work_packages/copy_contract.rb index da6de498dd7..054c1985a34 100644 --- a/app/contracts/work_packages/copy_contract.rb +++ b/app/contracts/work_packages/copy_contract.rb @@ -51,5 +51,12 @@ module WorkPackages # might not be active in the project yet. But when it is activated later, # the value should then be present. def validate_phase_active_in_project; end + + private + + # Auto-generated attributes are ok to be writable. The input does not come from the + # user so there is no need to run into a "read only error". + # The actual values will be regenerated after saving. + def auto_generated_attributes_writable? = true end end diff --git a/app/contracts/work_packages/create_note_contract.rb b/app/contracts/work_packages/create_note_contract.rb index 937cc1030c0..a99e6b74854 100644 --- a/app/contracts/work_packages/create_note_contract.rb +++ b/app/contracts/work_packages/create_note_contract.rb @@ -32,6 +32,8 @@ module WorkPackages class CreateNoteContract < ::ModelContract def self.model = WorkPackage + def validate_model? = false + attribute :journal_notes do errors.add(:journal_notes, :error_unauthorized) unless adding_notes_allowed? errors.add(:journal_notes, :blank) if model.journal_notes.blank? diff --git a/app/controllers/admin/custom_fields/custom_field_projects_controller.rb b/app/controllers/admin/custom_fields/custom_field_projects_controller.rb index 933b5b8a35d..f78f78674bf 100644 --- a/app/controllers/admin/custom_fields/custom_field_projects_controller.rb +++ b/app/controllers/admin/custom_fields/custom_field_projects_controller.rb @@ -34,10 +34,8 @@ class Admin::CustomFields::CustomFieldProjectsController < ApplicationController layout "admin" - model_object CustomField - before_action :require_admin - before_action :find_model_object + before_action :find_custom_field before_action :available_custom_fields_projects_query, only: %i[index destroy] before_action :initialize_custom_field_project, only: :new @@ -99,14 +97,13 @@ class Admin::CustomFields::CustomFieldProjectsController < ApplicationController ) end - def find_model_object(object_id = :custom_field_id) - super - @custom_field = @object + def find_custom_field + @custom_field = CustomField.find(params[:custom_field_id]) end def find_projects_to_activate_for_custom_field if (project_ids = params.to_unsafe_h[:custom_fields_project][:project_ids]).present? - @projects = Project.find(project_ids) + @projects = Project.visible.find(project_ids) else initialize_custom_field_project @custom_field_project.errors.add(:project_ids, :blank) diff --git a/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb b/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb index 56a84fa6246..87323c15a7b 100644 --- a/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb +++ b/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb @@ -36,9 +36,10 @@ module Admin include Dry::Monads[:result] layout :admin_or_frame_layout - model_object CustomField - before_action :require_admin, :find_model_object, :find_active_item + before_action :require_admin + before_action :find_custom_field + before_action :find_active_item # See https://github.com/hotwired/turbo-rails?tab=readme-ov-file#a-note-on-custom-layouts def admin_or_frame_layout @@ -225,11 +226,6 @@ module Admin end end - def find_model_object - @object = find_custom_field - @custom_field = @object - end - def find_custom_field raise NotImplementedError, "SubclassResponsibility" end @@ -238,7 +234,7 @@ module Admin @active_item = if params[:id].present? CustomField::Hierarchy::Item.including_children.find(params[:id]) else - @object.hierarchy_root + @custom_field.hierarchy_root end end end diff --git a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb index a4816eac0c5..0cc80efdf09 100644 --- a/app/controllers/admin/custom_fields/hierarchy/items_controller.rb +++ b/app/controllers/admin/custom_fields/hierarchy/items_controller.rb @@ -37,7 +37,7 @@ module Admin private def find_custom_field - CustomField.hierarchy_root_and_children.find(params[:custom_field_id]) + @custom_field = CustomField.hierarchy_root_and_children.find(params[:custom_field_id]) end end end diff --git a/app/controllers/admin/settings/project_custom_fields/hierarchy/items_controller.rb b/app/controllers/admin/settings/project_custom_fields/hierarchy/items_controller.rb index f9d74794073..bd17713dc81 100644 --- a/app/controllers/admin/settings/project_custom_fields/hierarchy/items_controller.rb +++ b/app/controllers/admin/settings/project_custom_fields/hierarchy/items_controller.rb @@ -38,7 +38,7 @@ module Admin private def find_custom_field - CustomField.hierarchy_root_and_children.find(params[:project_custom_field_id]) + @custom_field = CustomField.hierarchy_root_and_children.find(params[:project_custom_field_id]) end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index cf4918694a5..e77963a5411 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -34,7 +34,6 @@ require "cgi" require "doorkeeper/dashboard_helper" class ApplicationController < ActionController::Base - class_attribute :_model_object class_attribute :_model_scope class_attribute :accept_key_auth_actions @@ -246,18 +245,18 @@ class ApplicationController < ActionController::Base # Find project of id params[:id] # Note: find() is Project.friendly.find() def find_project - @project = Project.find(params[:id]) + @project = Project.visible.find(params[:id]) end # Find project of id params[:project_id] # Note: find() is Project.friendly.find() def find_project_by_project_id - @project = Project.find(params[:project_id]) + @project = Project.visible.find(params[:project_id]) end # Find project by project_id if given def find_optional_project - @project = Project.find(params[:project_id]) if params[:project_id].present? + @project = Project.visible.find(params[:project_id]) if params[:project_id].present? rescue ActiveRecord::RecordNotFound render_404 end @@ -269,67 +268,6 @@ class ApplicationController < ActionController::Base @project = @object.project end - def find_model_object(object_id = :id) - model = self.class._model_object - if model - @object = model.find(params[object_id]) - instance_variable_set(:"@#{controller_name.singularize}", @object) if @object - end - end - - def find_model_object_and_project(object_id = :id) - if params[object_id] - model_object = self.class._model_object - instance = model_object.find(params[object_id]) - @project = instance.project - instance_variable_set(:"@#{model_object.to_s.underscore}", instance) - else - @project = Project.find(params[:project_id]) - end - end - - # TODO: this method is right now only suited for controllers of objects that somehow have an association to Project - def find_object_and_scope - model_object = self.class._model_object.find(params[:id]) if params[:id].present? - - associations = self.class._model_scope + [Project] - - associated = find_belongs_to_chained_objects(associations, model_object) - - associated.each do |a| - instance_variable_set("@" + a.class.to_s.downcase, a) - end - end - - # this method finds all records that are specified in the associations param - # after the first object is found it traverses the belongs_to chain of that first object - # if a start_object is provided it is taken as the starting point of the traversal - # e.g associations [Message, Board, Project] finds Message by find(:message_id) - # then message.forum and board.project - def find_belongs_to_chained_objects(associations, start_object = nil) - associations.inject([start_object].compact) do |instances, association| - scope_name, scope_association = if association.is_a?(Hash) - [association.keys.first.to_s.downcase, association.values.first] - else - [association.to_s.downcase, association.to_s.downcase] - end - - # TODO: Remove this hidden dependency on params - instances << ( - if instances.last.nil? - scope_name.camelize.constantize.find(params[:"#{scope_name}_id"]) - else - instances.last.send(scope_association.to_sym) - end) - instances - end - end - - def self.model_object(model, options = {}) - self._model_object = model - self._model_scope = Array(options[:scope]) if options[:scope] - end - # Filter for bulk work package operations def find_work_packages @work_packages = WorkPackage.includes(:project) diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index c5f5b4f6c4f..c26c6b87230 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -30,9 +30,8 @@ class CategoriesController < ApplicationController menu_item :settings_categories - model_object Category - before_action :find_model_object, except: %i[new create] - before_action :find_project_from_association, except: %i[new create] + + before_action :find_category_and_project, except: %i[new create] before_action :find_project, only: %i[new create] before_action :authorize @@ -81,14 +80,12 @@ class CategoriesController < ApplicationController private - # Wrap ApplicationController's find_model_object method to set - # @category instead of just @category - def find_model_object - super - @category = @object + def find_category_and_project + @category = Category.find(params[:id]) + @project = @category.project end def find_project - @project = Project.find(params[:project_id]) + @project = Project.visible.find(params[:project_id]) end end diff --git a/app/controllers/concerns/accounts/current_user.rb b/app/controllers/concerns/accounts/current_user.rb index bc089498e8c..dee91364a6d 100644 --- a/app/controllers/concerns/accounts/current_user.rb +++ b/app/controllers/concerns/accounts/current_user.rb @@ -101,7 +101,7 @@ module Accounts::CurrentUser end def current_api_key_user - return unless Setting.rest_api_enabled? && api_request? + return unless Setting.api_tokens_enabled? && api_request? key = api_key_from_request @@ -168,7 +168,7 @@ module Accounts::CurrentUser redirect_to main_app.signin_path(back_url: login_back_url) end - auth_header = OpenProject::Authentication::WWWAuthenticate.response_header(request_headers: request.headers) + auth_header = OpenProject::Authentication::WWWAuthenticate.response_header format.any(:xml, :js, :json, :turbo_stream) do head :unauthorized, diff --git a/app/controllers/custom_actions_controller.rb b/app/controllers/custom_actions_controller.rb index bfeb80dd4b2..62c766a5896 100644 --- a/app/controllers/custom_actions_controller.rb +++ b/app/controllers/custom_actions_controller.rb @@ -35,8 +35,7 @@ class CustomActionsController < ApplicationController redirect_to action: :index end - self._model_object = CustomAction - before_action :find_model_object, only: %i(edit update destroy) + before_action :find_custom_action, only: %i(edit update destroy) before_action :pad_params, only: %i(create update) layout "admin" @@ -73,6 +72,10 @@ class CustomActionsController < ApplicationController private + def find_custom_action + @custom_action = CustomAction.find(params[:id]) + end + def index_or_render(render_action) ->(call) { call.on_success do diff --git a/app/controllers/external_link_warning_controller.rb b/app/controllers/external_link_warning_controller.rb index d16f9cdae0f..4153cc949fb 100644 --- a/app/controllers/external_link_warning_controller.rb +++ b/app/controllers/external_link_warning_controller.rb @@ -34,19 +34,31 @@ class ExternalLinkWarningController < ApplicationController skip_before_action :check_if_login_required no_authorization_required! :show - before_action :parse_external_url, only: [:show] - before_action :verify_capture_enabled, only: [:show] + before_action :parse_external_url + before_action :verify_capture_enabled + before_action :optional_require_login def show; end private + def login_back_url_params + params.permit(:url) + end + def verify_capture_enabled unless capture_enabled? redirect_to @external_url, allow_other_host: true, status: :see_other end end + def optional_require_login + return unless Setting.capture_external_links? + return unless Setting.capture_external_links_require_login? + + require_login + end + def capture_enabled? Setting.capture_external_links? && EnterpriseToken.allows_to?(:capture_external_links) end diff --git a/app/controllers/forums_controller.rb b/app/controllers/forums_controller.rb index 3d70f09fccf..9ff04248152 100644 --- a/app/controllers/forums_controller.rb +++ b/app/controllers/forums_controller.rb @@ -30,10 +30,12 @@ class ForumsController < ApplicationController default_search_scope :messages - before_action :find_project_by_project_id, - :authorize + before_action :find_project_by_project_id before_action :new_forum, only: %i[new create] before_action :find_forum, only: %i[show edit update move destroy] + + before_action :authorize + accept_key_auth :show include SortHelper @@ -47,7 +49,7 @@ class ForumsController < ApplicationController :forums end - def show + def show # rubocop:disable Metrics/AbcSize sort_init "updated_at", "desc" sort_update "created_at" => "#{Message.table_name}.created_at", "replies" => "#{Message.table_name}.replies_count", @@ -59,11 +61,11 @@ class ForumsController < ApplicationController @message = Message.new render action: "show", layout: !request.xhr? end - format.json do - set_topics - - render template: "messages/index" - end + # The JSON template does not exist anymore, this never rendered + # format.json do + # set_topics + # render template: "messages/index" + # end format.atom do @messages = @forum .messages @@ -92,7 +94,7 @@ class ForumsController < ApplicationController def create if @forum.save flash[:notice] = I18n.t(:notice_successful_create) - redirect_to action: "index" + redirect_to project_forums_path(@project) else render :new end @@ -101,26 +103,24 @@ class ForumsController < ApplicationController def update if @forum.update(permitted_params.forum) flash[:notice] = I18n.t(:notice_successful_update) - redirect_to action: "index" + redirect_to project_forums_path(@project) else - render :edit + render :edit, status: :unprocessable_entity end end def move - if @forum.update(permitted_params.forum_move) - flash[:notice] = t(:notice_successful_update) - else - flash.now[:error] = t("forum_could_not_be_saved") - render action: :edit, status: :unprocessable_entity - end - redirect_to action: "index" + @forum.update!(permitted_params.forum_move) + + flash[:notice] = t(:notice_successful_update) + redirect_to project_forums_path(@project) end def destroy - @forum.destroy + @forum.destroy! + flash[:notice] = I18n.t(:notice_successful_delete) - redirect_to action: "index", status: :see_other + redirect_to project_forums_path(@project) end private diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 09616c8103d..8ba62ac7f71 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -150,7 +150,7 @@ class GroupsController < ApplicationController protected def find_group - @group = Group.find(params[:id]) + @group = Group.visible.find(params[:id]) end def group_members diff --git a/app/controllers/inplace_edit_fields_controller.rb b/app/controllers/inplace_edit_fields_controller.rb new file mode 100644 index 00000000000..2b4bb22baae --- /dev/null +++ b/app/controllers/inplace_edit_fields_controller.rb @@ -0,0 +1,131 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class InplaceEditFieldsController < ApplicationController + include OpTurbo::ComponentStream + + before_action :find_model + before_action :set_attribute + no_authorization_required! :edit, :update, :reset + + def edit + replace_via_turbo_stream( + component: component(enforce_edit_mode: true), + status: :ok + ) + + respond_with_turbo_streams + end + + def update + handler = OpenProject::InplaceEdit::UpdateRegistry.fetch_handler(@model) + + if handler.present? + success = handler.call( + model: @model, + params: permitted_params, + user: current_user + ) + else + raise ArgumentError, "Missing update handler for #{@model}" + end + + if success + render_success_flash_message_via_turbo_stream( + message: I18n.t(:notice_successful_update) + ) + end + + replace_via_turbo_stream( + component: component(enforce_edit_mode: !success), + status: success ? :ok : :unprocessable_entity + ) + + respond_with_turbo_streams + rescue ArgumentError + head :not_found + end + + def reset + replace_via_turbo_stream(component:) + respond_with_turbo_streams + end + + private + + def find_model + model_class = resolve_model_class(params[:model]) + @model = model_class.visible.find(params[:id]) + rescue ActiveRecord::RecordNotFound, ArgumentError + head :not_found + end + + def resolve_model_class(model_param) + return nil if model_param.blank? + + model_class = + OpenProject::InplaceEdit::UpdateRegistry.resolve_model_class(model_param) + + unless model_class && + model_class < ApplicationRecord && + model_class.respond_to?(:visible) + raise ArgumentError, "Unsupported model for inplace edit" + end + + model_class + end + + def set_attribute + @attribute = params[:attribute].to_sym + end + + def permitted_params + params + .expect(@model.model_name.param_key => [@attribute]) + end + + def component(enforce_edit_mode: false) + OpenProject::Common::InplaceEditFieldComponent.new( + model: @model, + attribute: @attribute, + enforce_edit_mode:, + **system_arguments.to_h.symbolize_keys + ) + end + + def system_arguments + arguments = params[:system_arguments_json].presence || params.to_unsafe_h + .values + .filter_map { |v| v["system_arguments_json"] } + .first + + arguments.nil? ? {} : JSON.parse(arguments) + end +end diff --git a/app/controllers/ldap_auth_sources_controller.rb b/app/controllers/ldap_auth_sources_controller.rb index f0e8a072c79..12ac4573eab 100644 --- a/app/controllers/ldap_auth_sources_controller.rb +++ b/app/controllers/ldap_auth_sources_controller.rb @@ -31,13 +31,13 @@ class LdapAuthSourcesController < ApplicationController menu_item :ldap_authentication include PaginationHelper + layout "admin" before_action :require_admin before_action :block_if_password_login_disabled - self._model_object = LdapAuthSource - before_action :find_model_object, only: %i(edit update destroy) + before_action :find_ldap_auth_source, only: %i(edit update destroy) before_action :prevent_editing_when_seeded, only: %i(update) def index @@ -101,6 +101,10 @@ class LdapAuthSourcesController < ApplicationController protected + def find_ldap_auth_source + @ldap_auth_source = LdapAuthSource.find(params[:id]) + end + def prevent_editing_when_seeded if @ldap_auth_source.seeded_from_env? flash[:warning] = I18n.t(:label_seeded_from_env_warning) diff --git a/app/controllers/members_controller.rb b/app/controllers/members_controller.rb index b6c58613770..689d0548dd5 100644 --- a/app/controllers/members_controller.rb +++ b/app/controllers/members_controller.rb @@ -31,16 +31,15 @@ class MembersController < ApplicationController include MemberHelper - model_object Member - before_action :find_model_object_and_project, except: %i[autocomplete_for_member destroy_by_principal] - before_action :find_project_by_project_id, only: %i[autocomplete_for_member destroy_by_principal] + before_action :find_project_by_project_id + before_action :find_member, except: %i[index create autocomplete_for_member destroy_by_principal] before_action :authorize def index set_index_data! end - def create + def create # rubocop:disable Metrics/AbcSize overall_result = [] find_or_create_users(send_notification: true) do |member_params| @@ -85,8 +84,8 @@ class MembersController < ApplicationController per_page: params[:per_page]) end - def destroy_by_principal - principal = Principal.find(params[:principal_id]) + def destroy_by_principal # rubocop:disable Metrics/AbcSize + principal = Principal.visible.find(params[:principal_id]) service_call = Members::DeleteByPrincipalService .new(user: current_user, project: @project, principal:) @@ -118,7 +117,11 @@ class MembersController < ApplicationController private - def authorize_for(controller, action) + def find_member + @member = @project.members.visible.find(params[:id]) + end + + def authorize_for?(controller, action) current_user.allowed_in_project?({ controller:, action: }, @project) end @@ -152,8 +155,8 @@ class MembersController < ApplicationController { project: @project, available_roles: roles, - authorize_update: authorize_for("members", :update), - authorize_delete: authorize_for("members", :destroy), + authorize_update: authorize_for?("members", :update), + authorize_delete: authorize_for?("members", :destroy), authorize_work_package_shares_view: current_user.allowed_in_project?(:view_shared_work_packages, @project), authorize_work_package_shares_delete: current_user.allowed_in_project?(:share_work_packages, @project), authorize_manage_user: current_user.allowed_globally?(:manage_user), @@ -205,6 +208,7 @@ class MembersController < ApplicationController def possible_members(criteria, limit, type: nil) Principal + .visible .possible_member(@project, type:) .like(criteria, email: user_allowed_to_view_emails?) .limit(limit) diff --git a/app/controllers/messages_controller.rb b/app/controllers/messages_controller.rb index c016d914e06..20fddb1be6b 100644 --- a/app/controllers/messages_controller.rb +++ b/app/controllers/messages_controller.rb @@ -31,8 +31,8 @@ class MessagesController < ApplicationController menu_item :forums default_search_scope :messages - model_object Message, scope: Forum - before_action :find_object_and_scope + before_action :find_project_and_forum + before_action :find_message, only: %i[show edit update destroy reply quote] before_action :authorize, except: %i[edit update destroy] # Checked inside the method. no_authorization_required! :edit, :update, :destroy @@ -89,7 +89,7 @@ class MessagesController < ApplicationController if call.success? call_hook(:controller_messages_new_after_save, params:, message: @message) - redirect_to topic_path(@message) + redirect_to project_forum_topic_path(@project, @forum, @message) else render action: :new, status: :unprocessable_entity end @@ -105,7 +105,7 @@ class MessagesController < ApplicationController if call.success? call_hook(:controller_messages_reply_after_save, params:, message: @reply) end - redirect_to topic_path(@topic, r: @reply) + redirect_to project_forum_topic_path(@project, @forum, @topic, r: @reply) end # Edit a message @@ -118,7 +118,7 @@ class MessagesController < ApplicationController if call.success? flash[:notice] = t(:notice_successful_update) @message.reload - redirect_to topic_path(@message.root, r: @message.parent_id && @message.id) + redirect_to project_forum_topic_path(@project, @forum, @message.root, r: @message.parent_id && @message.id) else render action: :edit, status: :unprocessable_entity end @@ -132,9 +132,9 @@ class MessagesController < ApplicationController @message.destroy flash[:notice] = t(:notice_successful_delete) redirect_target = if @message.parent.nil? - { controller: "/forums", action: "show", project_id: @project, id: @forum } + project_forum_path(@project, @forum) else - { action: "show", id: @message.parent, r: @message } + project_forum_topic_path(@project, @forum, @message.parent, r: @message) end redirect_to redirect_target, status: :see_other @@ -157,6 +157,15 @@ class MessagesController < ApplicationController private + def find_project_and_forum + @project = Project.visible.find(params[:project_id]) + @forum = @project.forums.find(params[:forum_id]) + end + + def find_message + @message = @forum.messages.find(params[:id]) + end + def update_message(message) Messages::UpdateService .new(user: current_user, diff --git a/app/controllers/my/access_tokens_controller.rb b/app/controllers/my/access_tokens_controller.rb index 65a32670f4f..250eabb8f72 100644 --- a/app/controllers/my/access_tokens_controller.rb +++ b/app/controllers/my/access_tokens_controller.rb @@ -172,7 +172,7 @@ module My helper_method :has_tokens? def has_tokens? - Setting.feeds_enabled? || Setting.rest_api_enabled? || current_user.ical_tokens.any? + Setting.feeds_enabled? || Setting.api_tokens_enabled? || current_user.ical_tokens.any? end def set_api_token diff --git a/app/controllers/news/comments_controller.rb b/app/controllers/news/comments_controller.rb index ca5cf324793..03d5def8a41 100644 --- a/app/controllers/news/comments_controller.rb +++ b/app/controllers/news/comments_controller.rb @@ -30,8 +30,9 @@ class News::CommentsController < ApplicationController default_search_scope :news - model_object Comment, scope: [News => :commented] - before_action :find_object_and_scope + + before_action :find_news_and_project + before_action :find_comment, only: [:destroy] before_action :authorize def create @@ -41,11 +42,22 @@ class News::CommentsController < ApplicationController flash[:notice] = I18n.t(:label_comment_added) end - redirect_to news_path(@news), status: :see_other + redirect_to project_news_path(@project, @news), status: :see_other end def destroy - @comment.destroy - redirect_to news_path(@news), status: :see_other + @comment.destroy! + redirect_to project_news_path(@project, @news), status: :see_other + end + + private + + def find_comment + @comment = @news.comments.find(params[:id]) + end + + def find_news_and_project + @project = Project.visible.find(params[:project_id]) + @news = @project.news.visible.find(params[:news_id]) end end diff --git a/app/controllers/news_controller.rb b/app/controllers/news_controller.rb index f88309d064b..dec12d27241 100644 --- a/app/controllers/news_controller.rb +++ b/app/controllers/news_controller.rb @@ -34,17 +34,16 @@ class NewsController < ApplicationController default_search_scope :news + before_action :load_and_authorize_in_optional_project before_action :find_news_object, except: %i[new create index] - before_action :find_project_from_association, except: %i[new create index] - before_action :find_project, only: %i[new create] - before_action :authorize, except: [:index] - before_action :load_and_authorize_in_optional_project, only: [:index] + before_action :authorize + accept_key_auth :index - def index - scope = @project ? @project.news : News.all + def index # rubocop:disable Metrics/AbcSize + scope = @project ? @project.news : News.visible - @newss = scope.merge(News.latest_for(current_user, count: 0)) + @news = scope.merge(News.latest_for(current_user, count: 0)) .page(page_param) .per_page(per_page_param) @@ -53,7 +52,7 @@ class NewsController < ApplicationController render locals: { menu_name: project_or_global_menu } end format.atom do - render_feed(@newss, + render_feed(@news, title: (@project ? @project.name : Setting.app_title) + ": #{I18n.t(:label_news_plural)}") end end @@ -95,7 +94,7 @@ class NewsController < ApplicationController if call.success? flash[:notice] = I18n.t(:notice_successful_update) - redirect_to action: "show", id: @news + redirect_to project_news_path(@project, @news) else @news = call.result render action: :edit, status: :unprocessable_entity @@ -119,10 +118,11 @@ class NewsController < ApplicationController private def find_news_object - @news = @object = News.find(params[:id].to_i) - end - - def find_project - @project = Project.find(params[:project_id]) + if @project + @news = @project.news.visible.find(params[:id].to_i) + else + @news = News.visible.find(params[:id].to_i) + @project = @news.project + end end end diff --git a/app/controllers/placeholder_users/memberships_controller.rb b/app/controllers/placeholder_users/memberships_controller.rb index a979f2bce23..1ebce719388 100644 --- a/app/controllers/placeholder_users/memberships_controller.rb +++ b/app/controllers/placeholder_users/memberships_controller.rb @@ -30,13 +30,14 @@ class PlaceholderUsers::MembershipsController < ApplicationController include IndividualPrincipals::MembershipControllerMethods + layout "admin" before_action :authorize_global before_action :find_individual_principal def find_individual_principal - @individual_principal = PlaceholderUser.find(params[:placeholder_user_id]) + @individual_principal = PlaceholderUser.visible.find(params[:placeholder_user_id]) end def redirected_to_tab(_membership) diff --git a/app/controllers/placeholder_users_controller.rb b/app/controllers/placeholder_users_controller.rb index 77ba46d8b12..1b2caefff71 100644 --- a/app/controllers/placeholder_users_controller.rb +++ b/app/controllers/placeholder_users_controller.rb @@ -111,7 +111,7 @@ class PlaceholderUsersController < ApplicationController respond_to do |format| format.html do flash[:notice] = I18n.t(:notice_successful_update) - redirect_back(fallback_location: edit_placeholder_user_path(@placeholder_user)) + redirect_back_or_to(edit_placeholder_user_path(@placeholder_user)) end end else @@ -146,7 +146,7 @@ class PlaceholderUsersController < ApplicationController private def find_placeholder_user - @placeholder_user = PlaceholderUser.find(params[:id]) + @placeholder_user = PlaceholderUser.visible.find(params[:id]) end protected diff --git a/app/controllers/projects/archive_controller.rb b/app/controllers/projects/archive_controller.rb index 4f5fd317a3e..37b3e9dd191 100644 --- a/app/controllers/projects/archive_controller.rb +++ b/app/controllers/projects/archive_controller.rb @@ -31,7 +31,7 @@ class Projects::ArchiveController < ApplicationController include OpTurbo::ComponentStream - before_action :find_project_by_project_id + before_action :find_project_including_archived before_action :authorize, only: %i[create dialog] before_action :require_admin, only: [:destroy] @@ -49,17 +49,21 @@ class Projects::ArchiveController < ApplicationController private + def find_project_including_archived + # The visible scope filters out archived projects, but here we want to explicitly unarchive them. + # The contracts do proper permission checks, so we can skip the visible scope here. + @project = Project.find(params[:project_id]) + end + def change_status_action(status) service_call = change_status(status) - if service_call.success? - redirect_to(projects_path, status: :see_other) - else + if !service_call.success? flash[:error] = t(:"error_can_not_#{status}_project", errors: service_call.errors.full_messages.join(", ")) - redirect_back fallback_location: projects_path, - status: :see_other end + + redirect_to(projects_path, status: :see_other) end def change_status(status) diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index bb62d32a288..374ff30d74a 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -34,7 +34,8 @@ class ProjectsController < ApplicationController menu_item :overview menu_item :roadmap, only: :roadmap - before_action :find_project, except: %i[index new create] + before_action :find_project, except: %i[index new create destroy destroy_info] + before_action :find_project_including_archived, only: %i[destroy destroy_info] before_action :load_query_or_deny_access, only: %i[index] before_action :authorize, only: %i[copy_form copy deactivate_work_package_attachments export_project_initiation_pdf] @@ -181,6 +182,12 @@ class ProjectsController < ApplicationController private + def find_project_including_archived + # The actions that use this method are only accessible to admins, so we can show them archived projects as well and + # can skip the visible scope here. + @project = Project.find(params[:id]) + end + def from_template? = @template.present? def new_blank diff --git a/app/controllers/shares_controller.rb b/app/controllers/shares_controller.rb index 850dd72b3f8..51997db62b9 100644 --- a/app/controllers/shares_controller.rb +++ b/app/controllers/shares_controller.rb @@ -57,7 +57,7 @@ class SharesController < ApplicationController visible_shares_before_adding = sharing_strategy.shares.present? find_or_create_users(send_notification: send_notification?) do |member_params| - user = User.find_by(id: member_params[:user_id]) + user = User.visible.find_by(id: member_params[:user_id]) if user.present? && (user.locked? || user.deleted?) @errors.add(:base, I18n.t("sharing.warning_locked_user", user: user.name)) else diff --git a/app/controllers/users/memberships_controller.rb b/app/controllers/users/memberships_controller.rb index 5e012762f29..044717f84ed 100644 --- a/app/controllers/users/memberships_controller.rb +++ b/app/controllers/users/memberships_controller.rb @@ -30,13 +30,14 @@ class Users::MembershipsController < ApplicationController include IndividualPrincipals::MembershipControllerMethods + layout "admin" before_action :authorize_global before_action :find_individual_principal def find_individual_principal - @individual_principal = User.find(params[:user_id]) + @individual_principal = User.visible.find(params[:user_id]) end def redirected_to_tab(membership) diff --git a/app/controllers/versions_controller.rb b/app/controllers/versions_controller.rb index d6b0d31be0b..e2d1ff3d116 100644 --- a/app/controllers/versions_controller.rb +++ b/app/controllers/versions_controller.rb @@ -32,9 +32,7 @@ class VersionsController < ApplicationController menu_item :roadmap, only: %i(index show) menu_item :settings_versions - model_object Version - before_action :find_model_object, except: %i[index new create close_completed] - before_action :find_project_from_association, except: %i[index new create close_completed] + before_action :find_version, except: %i[index new create close_completed] before_action :find_project, only: %i[index new create close_completed] before_action :authorize @@ -114,6 +112,11 @@ class VersionsController < ApplicationController private + def find_version + @version = Version.visible.find(params[:id]) + @project = @version.project + end + def archived_project_mesage if current_user.admin? ApplicationController.helpers.sanitize( @@ -135,7 +138,7 @@ class VersionsController < ApplicationController end def find_project - @project = Project.find(params[:project_id]) + @project = Project.visible.find(params[:project_id]) end def retrieve_selected_type_ids(selectable_types, default_types = nil) diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index bd199274fef..72896441f84 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -370,7 +370,7 @@ class WikiController < ApplicationController end def find_wiki - @project = Project.find(params[:project_id]) + @project = Project.visible.find(params[:project_id]) @wiki = @project.wiki render_404 unless @wiki end diff --git a/app/controllers/wiki_menu_items_controller.rb b/app/controllers/wiki_menu_items_controller.rb index 5bd15ca8eac..bb7351753e8 100644 --- a/app/controllers/wiki_menu_items_controller.rb +++ b/app/controllers/wiki_menu_items_controller.rb @@ -37,7 +37,7 @@ class WikiMenuItemsController < ApplicationController next controller.wiki_menu_item.menu_identifier if controller.wiki_menu_item.try(:persisted?) project = controller.instance_variable_get(:@project) - if (page = WikiPage.find_by(wiki_id: project.wiki.id, slug: controller.params[:id])) + if (page = project.wiki.pages.find_by(id: controller.params[:id])) default_menu_item(controller, page) end end @@ -45,7 +45,8 @@ class WikiMenuItemsController < ApplicationController current_menu_item :select_main_menu_item do |controller| next controller.wiki_menu_item.menu_identifier if controller.wiki_menu_item.try(:persisted?) - if (page = WikiPage.find_by(id: controller.params[:id])) + project = controller.instance_variable_get(:@project) + if (page = project.wiki.pages.find_by(id: controller.params[:id])) default_menu_item(controller, page) end end @@ -114,7 +115,7 @@ class WikiMenuItemsController < ApplicationController end def select_main_menu_item - @page = WikiPage.find params[:id] + @page = @project.wiki.pages.find params[:id] @possible_wiki_pages = @project .wiki .pages @@ -126,12 +127,16 @@ class WikiMenuItemsController < ApplicationController end end - def replace_main_menu_item - current_page = WikiPage.find params[:id] + def replace_main_menu_item # rubocop:disable Metrics/AbcSize + current_page = @project.wiki.pages.find(params[:id]) - if (current_menu_item = current_page.menu_item) && (page = WikiPage.find_by(id: params[:wiki_page][:id])) && current_menu_item != page.menu_item - create_main_menu_item_for_wiki_page(page, current_menu_item.options) - current_menu_item.destroy + if current_menu_item = current_page.menu_item + page = @project.wiki.pages.find(params[:wiki_page][:id]) + + if page && current_menu_item != page.menu_item + create_main_menu_item_for_wiki_page(page, current_menu_item.options) + current_menu_item.destroy! + end end redirect_to action: :edit, id: current_page @@ -140,11 +145,11 @@ class WikiMenuItemsController < ApplicationController private def wiki_menu_item_params - @wiki_menu_item_params ||= params.require(:menu_items_wiki_menu_item).permit(:name, :title, :navigatable_id, :parent_id, - :setting, :new_wiki_page, :index_page) + @wiki_menu_item_params ||= params.expect(menu_items_wiki_menu_item: %i[name title navigatable_id parent_id + setting new_wiki_page index_page]) end - def get_data_from_params(params) + def get_data_from_params(params) # rubocop:disable Metrics/AbcSize wiki = @project.wiki @page = wiki.find_page(params[:id]) @@ -188,6 +193,6 @@ class WikiMenuItemsController < ApplicationController end menu_item.options = options - menu_item.save + menu_item.save! end end diff --git a/app/controllers/work_package_hierarchy_relations_controller.rb b/app/controllers/work_package_hierarchy_relations_controller.rb index 66cf444a2b5..ac48c957ec0 100644 --- a/app/controllers/work_package_hierarchy_relations_controller.rb +++ b/app/controllers/work_package_hierarchy_relations_controller.rb @@ -65,7 +65,7 @@ class WorkPackageHierarchyRelationsController < ApplicationController end def destroy - related = WorkPackage.find(params[:id]) + related = WorkPackage.visible.find(params[:id]) service_result = if related.parent_id == @work_package.id set_relation(child: related, parent: nil) @@ -101,7 +101,7 @@ class WorkPackageHierarchyRelationsController < ApplicationController def related_work_package @related_work_package ||= if params[:work_package][:id].present? - WorkPackage.find(params[:work_package][:id]) + WorkPackage.visible.find(params[:work_package][:id]) else WorkPackage.new end @@ -139,7 +139,7 @@ class WorkPackageHierarchyRelationsController < ApplicationController end def set_work_package - @work_package = WorkPackage.find(params[:work_package_id]) + @work_package = WorkPackage.visible.find(params[:work_package_id]) @project = @work_package.project end diff --git a/app/controllers/work_package_relations_controller.rb b/app/controllers/work_package_relations_controller.rb index 181bd932cce..50c51722093 100644 --- a/app/controllers/work_package_relations_controller.rb +++ b/app/controllers/work_package_relations_controller.rb @@ -127,11 +127,11 @@ class WorkPackageRelationsController < ApplicationController end def set_work_package - @work_package = WorkPackage.find(params[:work_package_id]) + @work_package = WorkPackage.visible.find(params[:work_package_id]) end def set_relation - @relation = @work_package.relations.find(params[:id]) + @relation = @work_package.relations.visible.find(params[:id]) end def create_relation_params diff --git a/app/controllers/work_package_relations_tab_controller.rb b/app/controllers/work_package_relations_tab_controller.rb index 1c8fda279ef..6fb17c3fbfa 100644 --- a/app/controllers/work_package_relations_tab_controller.rb +++ b/app/controllers/work_package_relations_tab_controller.rb @@ -51,7 +51,7 @@ class WorkPackageRelationsTabController < ApplicationController private def set_work_package - @work_package = WorkPackage.find(params[:work_package_id]) + @work_package = WorkPackage.visible.find(params[:work_package_id]) @project = @work_package.project # required for authorization via before_action end end diff --git a/app/controllers/work_packages/activities_tab_controller.rb b/app/controllers/work_packages/activities_tab_controller.rb index c93c1c22f38..5d92a77a695 100644 --- a/app/controllers/work_packages/activities_tab_controller.rb +++ b/app/controllers/work_packages/activities_tab_controller.rb @@ -35,7 +35,6 @@ class WorkPackages::ActivitiesTabController < ApplicationController include WorkPackages::ActivitiesTab::StimulusControllers before_action :find_work_package - before_action :find_project before_action :find_journal, only: %i[emoji_actions item_actions edit cancel_edit update toggle_reaction] before_action :set_filter before_action :authorize @@ -201,43 +200,33 @@ class WorkPackages::ActivitiesTabController < ApplicationController private + def find_work_package + @work_package = WorkPackage.visible.find(params[:work_package_id]) + @project = @work_package.project + rescue ActiveRecord::RecordNotFound + respond_with_error(I18n.t("label_not_found")) + end + def initialize_pagination @paginator, @paginated_journals = WorkPackages::ActivitiesTab::Paginator .paginate(@work_package, params.merge(filter: @filter, limit: 20)) end def respond_with_error(error_message) - respond_to do |format| - # turbo_frame requests (tab is initially rendered and an error occured) are handled below + @turbo_status = :not_found + render_error_flash_message_via_turbo_stream(message: error_message) + + respond_to_with_turbo_streams do |format| format.html do render( - WorkPackages::ActivitiesTab::ErrorFrameComponent.new( - error_message: - ), + WorkPackages::ActivitiesTab::ErrorFrameComponent.new(error_message: error_message), layout: false, status: :not_found ) end - # turbo_stream requests (tab is already rendered and an error occured in subsequent requests) are handled below - format.turbo_stream do - @turbo_status = :not_found - render_error_flash_message_via_turbo_stream(message: error_message) - end end end - def find_work_package - @work_package = WorkPackage.find(params[:work_package_id]) - rescue ActiveRecord::RecordNotFound - respond_with_error(I18n.t("label_not_found")) - end - - def find_project - @project = @work_package.project - rescue ActiveRecord::RecordNotFound - respond_with_error(I18n.t("label_not_found")) - end - def find_journal @journal = Journal .with_sequence_version diff --git a/app/forms/admin/settings/api_settings_form.rb b/app/forms/admin/settings/api_settings_form.rb index dbecf781b0b..d90e409fa55 100644 --- a/app/forms/admin/settings/api_settings_form.rb +++ b/app/forms/admin/settings/api_settings_form.rb @@ -48,7 +48,7 @@ module Admin end settings_form do |sf| - sf.check_box(name: :rest_api_enabled) + sf.check_box(name: :api_tokens_enabled, caption: I18n.t(:setting_api_tokens_enabled_caption)) sf.text_field( name: :apiv3_max_page_size, diff --git a/app/forms/admin/settings/external_links_settings_form.rb b/app/forms/admin/settings/external_links_settings_form.rb index 946f67c328c..5aa54f233f0 100644 --- a/app/forms/admin/settings/external_links_settings_form.rb +++ b/app/forms/admin/settings/external_links_settings_form.rb @@ -36,6 +36,10 @@ module Admin name: :capture_external_links, caption: I18n.t(:setting_capture_external_links_text) ) + sf.check_box( + name: :capture_external_links_require_login, + caption: I18n.t(:setting_capture_external_links_require_login_text) + ) end end end diff --git a/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb b/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb index 13c863c0c54..d1695fcb939 100644 --- a/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb +++ b/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb @@ -53,7 +53,7 @@ class CustomFields::Inputs::Base::Autocomplete::MultiValueInput < CustomFields:: end def custom_values - @custom_values ||= @object.custom_values_for_custom_field(id: @custom_field.id) + @custom_values ||= @object.custom_values_for_custom_field(@custom_field) end def invalid? diff --git a/app/helpers/custom_fields_helper.rb b/app/helpers/custom_fields_helper.rb index 3b83bf35ab4..b5f68de591d 100644 --- a/app/helpers/custom_fields_helper.rb +++ b/app/helpers/custom_fields_helper.rb @@ -72,7 +72,7 @@ module CustomFieldsHelper end def custom_field_tag_for_bulk_edit(name, custom_field, project = nil) # rubocop:disable Metrics/AbcSize - field_name = "#{name}[custom_field_values][#{custom_field.id}]" + field_name = name.present? ? "#{name}[custom_field_values][#{custom_field.id}]" : "custom_field_values[#{custom_field.id}]" field_id = "#{name}_custom_field_values_#{custom_field.id}" field_format = OpenProject::CustomFieldFormat.find_by(name: custom_field.field_format) diff --git a/app/helpers/messages_helper.rb b/app/helpers/messages_helper.rb index e344f6367b5..354c370f76e 100644 --- a/app/helpers/messages_helper.rb +++ b/app/helpers/messages_helper.rb @@ -36,6 +36,12 @@ module MessagesHelper end def message_url(message) - topic_url(message.root, r: message.id, anchor: "message-#{message.id}") + project_forum_topic_url( + message.forum.project, + message.forum, + message.root, + r: message.id, + anchor: "message-#{message.id}" + ) end end diff --git a/app/models/activities/message_activity_provider.rb b/app/models/activities/message_activity_provider.rb index 4c581ca0f1e..60bd4a6afd3 100644 --- a/app/models/activities/message_activity_provider.rb +++ b/app/models/activities/message_activity_provider.rb @@ -66,11 +66,11 @@ class Activities::MessageActivityProvider < Activities::BaseActivityProvider end def event_path(event) - url_helpers.topic_path(*url_helper_parameter(event)) + url_helpers.project_forum_topic_path(*url_helper_parameter(event)) end def event_url(event) - url_helpers.topic_url(*url_helper_parameter(event)) + url_helpers.project_forum_topic_url(*url_helper_parameter(event)) end private @@ -83,9 +83,19 @@ class Activities::MessageActivityProvider < Activities::BaseActivityProvider is_reply = event["parent_id"].present? if is_reply - { id: event["parent_id"], r: event["journable_id"], anchor: "message-#{event['journable_id']}" } + { + project_id: event["project_id"], + forum: event["forum_id"], + id: event["parent_id"], + r: event["journable_id"], + anchor: "message-#{event['journable_id']}" + } else - [event["journable_id"]] + { + project_id: event["project_id"], + forum_id: event["forum_id"], + id: event["journable_id"] + } end end end diff --git a/app/models/activities/news_activity_provider.rb b/app/models/activities/news_activity_provider.rb index b3a532bdb98..baee70fcf86 100644 --- a/app/models/activities/news_activity_provider.rb +++ b/app/models/activities/news_activity_provider.rb @@ -50,16 +50,16 @@ class Activities::NewsActivityProvider < Activities::BaseActivityProvider end def event_path(event) - url_helpers.news_path(url_helper_parameter(event)) + url_helpers.project_news_path(url_helper_parameter(event)) end def event_url(event) - url_helpers.news_url(url_helper_parameter(event)) + url_helpers.project_news_url(url_helper_parameter(event)) end private def url_helper_parameter(event) - event["journable_id"] + { project_id: event["project_id"], id: event["journable_id"] } end end diff --git a/app/models/custom_field.rb b/app/models/custom_field.rb index 37c66278308..55b4d5b62e7 100644 --- a/app/models/custom_field.rb +++ b/app/models/custom_field.rb @@ -258,7 +258,7 @@ class CustomField < ApplicationRecord name =~ /\A(.+)CustomField\z/ begin $1.constantize - rescue StandardError + rescue NameError nil end end @@ -369,7 +369,7 @@ class CustomField < ApplicationRecord # Use a ruby finder to avoid hitting the database with N+1 queries on the project list page, # the errors are eager loaded via the Queries::Projects::CustomFieldContext. - calculated_value_errors.find { it.customized_id == customized.id } + calculated_value_errors.find { it.customized == customized } end private diff --git a/app/models/exports/formatters/custom_field.rb b/app/models/exports/formatters/custom_field.rb index 22a166c89c1..a4e236e93e3 100644 --- a/app/models/exports/formatters/custom_field.rb +++ b/app/models/exports/formatters/custom_field.rb @@ -39,7 +39,7 @@ module Exports # Takes a WorkPackage or Project and an attribute and returns the value to be exported. def retrieve_value(object) custom_field = find_custom_field(object) - return "" if custom_field.nil? + return nil if custom_field.nil? format_for_export(object, custom_field) end @@ -68,8 +68,8 @@ module Exports ## # Finds a custom field from the attribute identifier def find_custom_field(object) - id = attribute.to_s.sub("cf_", "").to_i - object.available_custom_fields.detect { |cf| cf.id == id } + id = attribute.to_s.delete_prefix("cf_").to_i + object.available_custom_fields.find { it.id == id } end end end diff --git a/app/models/journable/historic_active_record_relation.rb b/app/models/journable/historic_active_record_relation.rb index 97ea5cbe8ef..a28902cb9a1 100644 --- a/app/models/journable/historic_active_record_relation.rb +++ b/app/models/journable/historic_active_record_relation.rb @@ -101,7 +101,7 @@ class Journable::HistoricActiveRecordRelation < ActiveRecord::Relation # # SELECT * from work_packages - def build_arel(connection, aliases = nil) + def build_arel(aliases = nil) substitute_join_tables_in_where_clause(self) # Based on the previous modifications, build the algebra object and prepend diff --git a/app/models/members/scopes/visible.rb b/app/models/members/scopes/visible.rb index 1c6e71f7ed1..8fe3adafe2e 100644 --- a/app/models/members/scopes/visible.rb +++ b/app/models/members/scopes/visible.rb @@ -34,7 +34,7 @@ module Members::Scopes class_methods do # Find all members visible to the inquiring user - def visible(user) + def visible(user = User.current) if user.admin? visible_for_admins else diff --git a/app/models/project.rb b/app/models/project.rb index 05c86a7635c..456b615bc54 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -174,9 +174,9 @@ class Project < ApplicationRecord register_journal_formatted_fields "status_code", formatter_key: :project_status_code register_journal_formatted_fields "public", formatter_key: :visibility register_journal_formatted_fields "parent_id", formatter_key: :subproject_named_association - register_journal_formatted_fields /custom_fields_\d+/, formatter_key: :custom_field - register_journal_formatted_fields /^project_phase_\d+_active$/, formatter_key: :project_phase_active - register_journal_formatted_fields /^project_phase_\d+_date_range$/, formatter_key: :project_phase_dates + register_journal_formatted_fields /\Acustom_fields_\d+\z/, formatter_key: :custom_field + register_journal_formatted_fields /\Aproject_phase_\d+_active\z/, formatter_key: :project_phase_active + register_journal_formatted_fields /\Aproject_phase_\d+_date_range\z/, formatter_key: :project_phase_dates has_paper_trail diff --git a/app/models/project/pdf_export/project_initiation.rb b/app/models/project/pdf_export/project_initiation.rb index 1feb1276647..4ebe6d26175 100644 --- a/app/models/project/pdf_export/project_initiation.rb +++ b/app/models/project/pdf_export/project_initiation.rb @@ -188,12 +188,12 @@ class Project::PDFExport::ProjectInitiation < Exports::Exporter .where(id: enabled_in_wizard_ids) .group_by(&:project_custom_field_section) .map do |section, custom_fields| - { - caption: section.name, - fields: custom_fields.map do |custom_field| - { key: "cf_#{custom_field.id}", caption: custom_field.name, custom_field: } - end - } + { + caption: section.name, + fields: custom_fields.map do |custom_field| + { key: "cf_#{custom_field.id}", caption: custom_field.name, custom_field: } + end + } end end @@ -284,7 +284,7 @@ class Project::PDFExport::ProjectInitiation < Exports::Exporter def project_initiation_work_package_status return nil if project.project_creation_wizard_artifact_work_package_id.blank? - work_package = WorkPackage.find_by(id: project.project_creation_wizard_artifact_work_package_id) + work_package = WorkPackage.visible.find_by(id: project.project_creation_wizard_artifact_work_package_id) work_package&.status end diff --git a/app/models/queries/principals/filters/internal_mentionable_on_work_package_filter.rb b/app/models/queries/principals/filters/internal_mentionable_on_work_package_filter.rb index e6df3a2e57c..4ed8254cd33 100644 --- a/app/models/queries/principals/filters/internal_mentionable_on_work_package_filter.rb +++ b/app/models/queries/principals/filters/internal_mentionable_on_work_package_filter.rb @@ -82,6 +82,6 @@ class Queries::Principals::Filters::InternalMentionableOnWorkPackageFilter < end def work_package - WorkPackage.find(values.first) + WorkPackage.visible.find(values.first) end end diff --git a/app/models/queries/work_packages/filter/relatable_filter.rb b/app/models/queries/work_packages/filter/relatable_filter.rb index e5cea285c9e..988cbb5e5ce 100644 --- a/app/models/queries/work_packages/filter/relatable_filter.rb +++ b/app/models/queries/work_packages/filter/relatable_filter.rb @@ -49,7 +49,7 @@ class Queries::WorkPackages::Filter::RelatableFilter < Queries::WorkPackages::Fi end def apply_to(query_scope) - query_scope.relatable(WorkPackage.find_by(id: values.first), scope_operator) + query_scope.relatable(WorkPackage.visible.find_by(id: values.first), scope_operator) end private diff --git a/app/models/setting.rb b/app/models/setting.rb index 84e9cfc7786..4a02d000988 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -31,6 +31,7 @@ class Setting < ApplicationRecord class NotWritableError < StandardError; end + extend Accessors extend Aliases extend MailSettings @@ -74,73 +75,6 @@ class Setting < ApplicationRecord Big5-HKSCS TIS-620).freeze - class << self - def create_setting(name, value = {}) - ::Settings::Definition.add(name, **value.symbolize_keys) - end - - def create_setting_accessors(name) - return if [:installation_uuid].include?(name.to_sym) - - # Defines getter and setter for each setting - # Then setting values can be read using: Setting.some_setting_name - # or set using Setting.some_setting_name = "some value" - src = <<-END_SRC - def self.#{name} - # when running too early, there is no settings table. do nothing - self[:#{name}] if settings_table_exists_yet? - end - - def self.#{name}? - # when running too early, there is no settings table. do nothing - return unless settings_table_exists_yet? - definition = Settings::Definition[:#{name}] - - if definition.format != :boolean - ActiveSupport::Deprecation.new.warn "Calling #{self}.#{name}? is deprecated since it is not a boolean", caller_locations - end - - value = self[:#{name}] - ActiveRecord::Type::Boolean.new.cast(value) || false - end - - def self.#{name}=(value) - if settings_table_exists_yet? - self[:#{name}] = value - else - logger.warn "Trying to save a setting named '#{name}' while there is no 'setting' table yet. This setting will not be saved!" - nil # when running too early, there is no settings table. do nothing - end - end - - def self.#{name}_writable? - Settings::Definition[:#{name}].writable? - end - END_SRC - class_eval src, __FILE__, __LINE__ - end - - def method_missing(method, *, &) - if exists?(accessor_base_name(method)) - create_setting_accessors(accessor_base_name(method)) - - send(method, *) - else - super - end - end - - def respond_to_missing?(method_name, include_private = false) - exists?(accessor_base_name(method_name)) || super - end - - private - - def accessor_base_name(name) - name.to_s.sub(/(_writable\?)|(\?)|=\z/, "") - end - end - validates :name, uniqueness: true, inclusion: { @@ -223,28 +157,6 @@ class Setting < ApplicationRecord Settings::Definition[name].present? end - def self.installation_uuid - if settings_table_exists_yet? - # we avoid the default getters and setters since the cache messes things up - setting = find_or_initialize_by(name: "installation_uuid") - if setting.value.blank? - setting.value = generate_installation_uuid - setting.save! - end - setting.value - else - "unknown" - end - end - - def self.generate_installation_uuid - if Rails.env.test? - "test" - else - SecureRandom.uuid - end - end - %i[emails_header emails_footer].each do |mail| src = <<-END_SRC def self.localized_#{mail} diff --git a/app/models/setting/accessors.rb b/app/models/setting/accessors.rb new file mode 100644 index 00000000000..7459da8b595 --- /dev/null +++ b/app/models/setting/accessors.rb @@ -0,0 +1,109 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class Setting + # Dynamically defines getter, setter, boolean, and writable? class methods + # for each setting. Methods are lazily created via method_missing when a + # setting is first accessed. + # + # After creation, setting values can be read using: Setting.some_setting_name + # or set using: Setting.some_setting_name = "some value" + module Accessors + def create_setting(name, value = {}) + ::Settings::Definition.add(name, **value.symbolize_keys) + end + + def create_setting_accessors(name) + define_setting_getter(name) + define_setting_boolean_getter(name) + define_setting_setter(name) + define_setting_writable_check(name) + end + + def method_missing(method, *, &) + if exists?(accessor_base_name(method)) + create_setting_accessors(accessor_base_name(method)) + + send(method, *) + else + super + end + end + + def respond_to_missing?(method_name, include_private = false) + exists?(accessor_base_name(method_name)) || super + end + + private + + def define_setting_getter(name) + define_singleton_method(name) do + # when running too early, there is no settings table. do nothing + self[name] if settings_table_exists_yet? + end + end + + def define_setting_boolean_getter(name) + define_singleton_method(:"#{name}?") do + definition = Settings::Definition[name] + + if definition.format != :boolean + ActiveSupport::Deprecation.new.warn "Calling #{self}.#{name}? is deprecated since it is not a boolean", caller_locations + end + + # Use accessor to go through same table check + value = public_send(name) + ActiveRecord::Type::Boolean.new.cast(value) || false + end + end + + def define_setting_setter(name) + define_singleton_method(:"#{name}=") do |value| + if settings_table_exists_yet? + self[name] = value + else + logger.warn "Trying to save a setting named '#{name}' while there is no 'setting' table yet. " \ + "This setting will not be saved!" + nil # when running too early, there is no settings table. do nothing + end + end + end + + def define_setting_writable_check(name) + define_singleton_method(:"#{name}_writable?") do + Settings::Definition[name].writable? + end + end + + def accessor_base_name(name) + name.to_s.sub(/(_writable\?)|(\?)|=\z/, "") + end + end +end diff --git a/app/models/token/api.rb b/app/models/token/api.rb index 2a28819405c..fe25f61356b 100644 --- a/app/models/token/api.rb +++ b/app/models/token/api.rb @@ -30,5 +30,6 @@ module Token class API < Named + prefix :opapi end end diff --git a/app/models/token/auto_login.rb b/app/models/token/auto_login.rb index 8e8e83ca1d4..4bf3692209d 100644 --- a/app/models/token/auto_login.rb +++ b/app/models/token/auto_login.rb @@ -32,6 +32,8 @@ module Token class AutoLogin < HashedToken include ExpirableToken + prefix :opal + has_many :autologin_session_links, class_name: "Sessions::AutologinSessionLink", foreign_key: "token_id", diff --git a/app/models/token/backup.rb b/app/models/token/backup.rb index 9d621d234d9..7b418c7dbe0 100644 --- a/app/models/token/backup.rb +++ b/app/models/token/backup.rb @@ -30,6 +30,8 @@ module Token class Backup < HashedToken + prefix :opbk + def ready? return false if created_at.nil? diff --git a/app/models/token/base.rb b/app/models/token/base.rb index b7aa526928d..7ec88b1e9c9 100644 --- a/app/models/token/base.rb +++ b/app/models/token/base.rb @@ -64,22 +64,37 @@ module Token # Delete previous token of this type upon save before_save :delete_previous_token - ## - # Find a token from the token value - def self.find_by_plaintext_value(input) - find_by(value: input) - end + class << self + ## + # A DSL method allowing to define a prefix for all generated tokens, making it possible to recognize + # the purpose of a token by looking at the token value. + # + # class MyToken < HashedToken + # prefix :my + # end + def prefix(value = nil) + @prefix = value.to_s if value - ## - # Find tokens for the given user - def self.for_user(user) - where(user:) - end + @prefix + end - ## - # Generate a random hex token value - def self.generate_token_value - SecureRandom.hex(32) + ## + # Find a token from the token value + def find_by_plaintext_value(input) + find_by(value: input) + end + + ## + # Find tokens for the given user + def for_user(user) + where(user:) + end + + ## + # Generate a random hex token value + def generate_token_value + [prefix, SecureRandom.hex(32)].compact.join("-") + end end ## diff --git a/app/models/token/hashed_token.rb b/app/models/token/hashed_token.rb index c9c16788ffc..37054f23afb 100644 --- a/app/models/token/hashed_token.rb +++ b/app/models/token/hashed_token.rb @@ -53,7 +53,7 @@ module Token class << self def create_and_return_value(user) - create(user:).plain_value + create!(user:).plain_value end ## diff --git a/app/models/token/ical.rb b/app/models/token/ical.rb index 058756aa2ec..9911bd30ae8 100644 --- a/app/models/token/ical.rb +++ b/app/models/token/ical.rb @@ -30,6 +30,8 @@ module Token class ICal < HashedToken + prefix :opical + # restrict the usage of one ical token to one query (calendar) has_one :ical_token_query_assignment, required: true, dependent: :destroy, foreign_key: :ical_token_id, class_name: "ICalTokenQueryAssignment", inverse_of: :ical_token diff --git a/app/models/user.rb b/app/models/user.rb index b8ca21b3aad..0a7fd69bbc9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -448,7 +448,7 @@ class User < Principal end def self.find_by_api_key(key) - return nil unless Setting.rest_api_enabled? + return nil unless Setting.api_tokens_enabled? token = Token::API.find_by_plaintext_value(key) diff --git a/app/models/work_package/journalized.rb b/app/models/work_package/journalized.rb index 518b6919d64..01347465f5e 100644 --- a/app/models/work_package/journalized.rb +++ b/app/models/work_package/journalized.rb @@ -96,11 +96,11 @@ module WorkPackage::Journalized register_journal_formatted_fields "done_ratio", "derived_done_ratio", formatter_key: :percentage register_journal_formatted_fields "description", formatter_key: :diff register_journal_formatted_fields "schedule_manually", formatter_key: :schedule_manually - register_journal_formatted_fields /attachments_?\d+/, formatter_key: :attachment - register_journal_formatted_fields /custom_fields_\d+/, formatter_key: :custom_field + register_journal_formatted_fields /\Aattachments_?\d+\z/, formatter_key: :attachment + register_journal_formatted_fields /\Acustom_fields_\d+\z/, formatter_key: :custom_field register_journal_formatted_fields "ignore_non_working_days", formatter_key: :ignore_non_working_days register_journal_formatted_fields "cause", formatter_key: :cause - register_journal_formatted_fields /file_links_?\d+/, formatter_key: :file_link + register_journal_formatted_fields /\Afile_links_?\d+\z/, formatter_key: :file_link register_journal_formatted_fields "project_phase_definition_id", formatter_key: :project_phase_definition # Joined @@ -110,6 +110,7 @@ module WorkPackage::Journalized :assigned_to_id, :priority_id, :category_id, :version_id, :author_id, :responsible_id, + :sprint_id, formatter_key: :named_association register_journal_formatted_fields :start_date, :due_date, formatter_key: :datetime register_journal_formatted_fields :subject, formatter_key: :plaintext diff --git a/app/models/work_package/validations.rb b/app/models/work_package/validations.rb index 01102c87289..990bf402e88 100644 --- a/app/models/work_package/validations.rb +++ b/app/models/work_package/validations.rb @@ -32,9 +32,7 @@ module WorkPackage::Validations extend ActiveSupport::Concern included do - validates :subject, :priority, :project, :type, :author, :status, presence: true - - validates :subject, length: { maximum: 255 } + validates :priority, :project, :type, :author, :status, presence: true validates :done_ratio, inclusion: { in: 0..100 }, numericality: true, allow_nil: true validates :estimated_hours, numericality: { allow_nil: true, greater_than_or_equal_to: 0 } validates :remaining_hours, numericality: { allow_nil: true, greater_than_or_equal_to: 0 } diff --git a/app/seeders/basic_data/model_seeder.rb b/app/seeders/basic_data/model_seeder.rb index e58c8288886..5aece770aee 100644 --- a/app/seeders/basic_data/model_seeder.rb +++ b/app/seeders/basic_data/model_seeder.rb @@ -52,6 +52,9 @@ module BasicData model_class .create!(model_attributes(model_data)) .tap { |model| seed_data.store_reference(model_data["reference"], model) } + rescue ActiveRecord::RecordInvalid => e + Rails.logger.error { "Failed to create #{model_class} seed_data: %e" } + raise e end def mapped_models_data diff --git a/app/services/journals/create_service/customizable.rb b/app/services/journals/create_service/customizable.rb index ef52d3c3915..5efb4cadf51 100644 --- a/app/services/journals/create_service/customizable.rb +++ b/app/services/journals/create_service/customizable.rb @@ -58,6 +58,7 @@ class Journals::CreateService FROM custom_values WHERE #{only_if_created_sql} + AND #{availability_condition} AND custom_values.customized_id = :journable_id AND custom_values.customized_type = :journable_class_name AND custom_values.value IS NOT NULL @@ -72,16 +73,17 @@ class Journals::CreateService FROM ( SELECT - custom_field_id, - ARRAY_AGG(#{normalize_newlines_sql('custom_values.value')} ORDER BY value) AS value + custom_values.custom_field_id, + ARRAY_AGG(#{normalize_newlines_sql('custom_values.value')} ORDER BY value) AS value FROM - custom_values + custom_values WHERE - custom_values.customized_id = :journable_id + #{availability_condition} + AND custom_values.customized_id = :journable_id AND custom_values.customized_type = :customized_type AND custom_values.value != '' GROUP BY - custom_field_id + custom_values.custom_field_id ) current_values FULL JOIN ( @@ -100,5 +102,23 @@ class Journals::CreateService current_values.value IS DISTINCT FROM journal_values.value SQL end + + private + + def availability_condition + return "1 = 1" unless journable.is_a?(Project) + + <<~SQL # rubocop:disable Rails/SquishedSQLHeredocs + EXISTS ( + SELECT 1 + FROM custom_fields + LEFT JOIN project_custom_field_project_mappings + ON project_custom_field_project_mappings.custom_field_id = custom_fields.id + AND project_custom_field_project_mappings.project_id = :journable_id + WHERE custom_fields.id = custom_values.custom_field_id + AND (custom_fields.is_for_all = TRUE OR project_custom_field_project_mappings.project_id IS NOT NULL) + ) + SQL + end end end diff --git a/app/services/mcp_resources/work_package.rb b/app/services/mcp_resources/work_package.rb index 58fe41ca792..0537e6c3a3d 100644 --- a/app/services/mcp_resources/work_package.rb +++ b/app/services/mcp_resources/work_package.rb @@ -37,9 +37,8 @@ module McpResources default_description "Access work packages of this OpenProject instance." def read(id:) - work_package = ::WorkPackage.find_by(id:) + work_package = ::WorkPackage.visible.find_by(id:) return nil if work_package.nil? - return nil unless current_user.allowed_in_work_package?(:view_work_packages, work_package) API::V3::WorkPackages::WorkPackageRepresenter.create(work_package, current_user:, embed_links: true) end diff --git a/app/services/projects/creation_wizard/create_artifact_work_package_service.rb b/app/services/projects/creation_wizard/create_artifact_work_package_service.rb index e573500d066..3661668e220 100644 --- a/app/services/projects/creation_wizard/create_artifact_work_package_service.rb +++ b/app/services/projects/creation_wizard/create_artifact_work_package_service.rb @@ -182,9 +182,8 @@ module Projects::CreationWizard end def assignee_mention_tag - return if assigned_to_id.nil? - - principal = Principal.find(assigned_to_id) + principal = Principal.visible.find_by(id: assigned_to_id) + return "" if principal.nil? ApplicationController.helpers.content_tag( "mention", diff --git a/app/services/types/apply_patterns.rb b/app/services/types/apply_patterns.rb new file mode 100644 index 00000000000..600d8a10278 --- /dev/null +++ b/app/services/types/apply_patterns.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ + +module Types + module ApplyPatterns + extend ActiveSupport::Concern + + included do + private + + def apply_patterns(model, save: true) + model.type&.enabled_patterns&.each do |key, pattern| + model.public_send(:"#{key}=", pattern.resolve(model)) + end + + model.save!(validate: false) if save && model.changed? + end + end + end +end diff --git a/app/services/work_packages/create_service.rb b/app/services/work_packages/create_service.rb index 9050a940fb3..41500a9defa 100644 --- a/app/services/work_packages/create_service.rb +++ b/app/services/work_packages/create_service.rb @@ -31,6 +31,7 @@ class WorkPackages::CreateService < BaseServices::BaseCallable include ::WorkPackages::Shared::UpdateAncestors include ::Shared::ServiceContext + include Types::ApplyPatterns attr_reader :user, :contract_class, :contract_options @@ -56,12 +57,12 @@ class WorkPackages::CreateService < BaseServices::BaseCallable result = set_attributes(attributes, work_package) if result.success? - # Set attributes service passed, meaning the contract is fullfilled. + # Set attributes service passed, meaning the contract is fulfilled. # Avoid running validations again as we might be in a project copy scenario. work_package.attachments = work_package.attachments_replacements if work_package.attachments_replacements work_package.save(validate: false) - update_subject_if_automatically_generated(work_package) + apply_patterns(work_package) # update ancestors before rescheduling, as the parent might switch to automatic mode multi_update_ancestors(result.all_results).each do |ancestor_result| @@ -76,15 +77,6 @@ class WorkPackages::CreateService < BaseServices::BaseCallable result end - def update_subject_if_automatically_generated(work_package) - if work_package.type&.replacement_pattern_defined_for?(:subject) - Journal::NotificationConfiguration.with(false) do - work_package.subject = work_package.type.enabled_patterns[:subject].resolve(work_package) - work_package.save(validate: false) - end - end - end - def set_attributes(attributes, work_package) attributes_service_class.new(user:, model: work_package, contract_class:, contract_options:).call(attributes) end diff --git a/app/services/work_packages/set_attributes_service.rb b/app/services/work_packages/set_attributes_service.rb index b0c40bef6df..e9f0f65d470 100644 --- a/app/services/work_packages/set_attributes_service.rb +++ b/app/services/work_packages/set_attributes_service.rb @@ -47,14 +47,6 @@ class WorkPackages::SetAttributesService < BaseServices::SetAttributes set_custom_attributes(attributes) set_custom_values_to_validate(attributes, validate_custom_fields) - - mark_templated_subject - end - - def mark_templated_subject - if work_package.type&.replacement_pattern_defined_for?(:subject) - work_package.subject = I18n.t("work_packages.templated_subject_hint", type: work_package.type.name) - end end def set_custom_values_to_validate(attributes, validate_custom_fields = nil) @@ -69,7 +61,7 @@ class WorkPackages::SetAttributesService < BaseServices::SetAttributes def set_static_attributes(attributes) assignable_attributes = attributes.select do |key, _| - !CustomField.custom_field_attribute?(key) && work_package.respond_to?(key) + !CustomField.custom_field_attribute?(key) && work_package.respond_to?("#{key}=") end work_package.attributes = assignable_attributes diff --git a/app/services/work_packages/update_service.rb b/app/services/work_packages/update_service.rb index 36e73d6cb07..89c4aa2da5d 100644 --- a/app/services/work_packages/update_service.rb +++ b/app/services/work_packages/update_service.rb @@ -31,6 +31,7 @@ class WorkPackages::UpdateService < BaseServices::Update include ::WorkPackages::Shared::UpdateAncestors include Attachments::ReplaceAttachments + include Types::ApplyPatterns attr_accessor :cause_of_rescheduling @@ -41,18 +42,12 @@ class WorkPackages::UpdateService < BaseServices::Update private - def set_templated_attributes + def after_perform(service_call) # TODO: code smell here: saving the automatically generated subject depends # on running the UpdateAncestorsService right after. The subject gets saved # only thanks to this. If the UpdateAncestorsService is not run, the subject # is not saved. That's an odd coupling. - model.type.enabled_patterns.each do |key, pattern| - model.public_send(:"#{key}=", pattern.resolve(model)) - end - end - - def after_perform(service_call) - set_templated_attributes + apply_patterns(service_call.result, save: false) update_related_work_packages(service_call) cleanup(service_call.result) @@ -142,7 +137,7 @@ class WorkPackages::UpdateService < BaseServices::Update # if parent changed, the former parent needs to be rescheduled too. if parent_just_changed?(work_package) - former_parent = WorkPackage.find_by(id: work_package.parent_id_before_last_save) + former_parent = WorkPackage.visible(user).find_by(id: work_package.parent_id_before_last_save) work_packages_to_reschedule << former_parent if former_parent end @@ -165,11 +160,11 @@ class WorkPackages::UpdateService < BaseServices::Update service_calls .group_by { |sc| sc.result.id } .map do |(_, same_work_package_calls)| - same_work_package_calls.pop.tap do |master| - same_work_package_calls.each do |sc| - master.result.attributes = sc.result.changes.transform_values(&:last) + same_work_package_calls.pop.tap do |master| + same_work_package_calls.each do |sc| + master.result.attributes = sc.result.changes.transform_values(&:last) + end end - end end end end diff --git a/app/views/custom_fields/new.html.erb b/app/views/custom_fields/new.html.erb index feb38394b5c..47f84a9c93d 100644 --- a/app/views/custom_fields/new.html.erb +++ b/app/views/custom_fields/new.html.erb @@ -31,7 +31,10 @@ See COPYRIGHT and LICENSE files for more details. <%= render(Primer::OpenProject::PageHeader.new(test_selector: "custom-fields--page-header")) do |header| - header.with_title { t(:label_custom_field_new) } + header.with_title do + concat t(:label_custom_field_new) + concat render(Primer::Beta::Text.new(color: :muted)) { " (#{label_for_custom_field_format(@custom_field.field_format)})" } + end header.with_breadcrumbs( [{ href: admin_index_path, text: t(:label_administration) }, { href: custom_fields_path, text: t(:label_custom_field_plural) }, diff --git a/app/views/forums/show.html.erb b/app/views/forums/show.html.erb index 869f1fbfe50..f3cba8d9b19 100644 --- a/app/views/forums/show.html.erb +++ b/app/views/forums/show.html.erb @@ -102,7 +102,7 @@ See COPYRIGHT and LICENSE files for more details. <% if message.locked? %> <%= op_icon("icon-locked", title: I18n.t("js.label_board_locked")) %> <% end %> - <%= link_to message.subject, topic_path(message) %> + <%= link_to message.subject, project_forum_topic_path(message.forum.project, message.forum, message) %>
<%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> - <%= link_to t(:button_cancel), news_path(@news), class: "button -with-icon icon-cancel" %> + <%= link_to t(:button_cancel), project_news_path(@project, @news), class: "button -with-icon icon-cancel" %> <% end %> diff --git a/app/views/news/index.html.erb b/app/views/news/index.html.erb index 05352ffd934..d4771f38c6d 100644 --- a/app/views/news/index.html.erb +++ b/app/views/news/index.html.erb @@ -57,15 +57,15 @@ See COPYRIGHT and LICENSE files for more details. %> <% end %> -<% if @newss.any? %> - <% @newss.each do |news| %> +<% if @news.any? %> + <% @news.each do |news| %>
<%= avatar(news.author) %><%= link_to_project(news.project) + ": " unless news.project == @project %> - <%= link_to h(news.title), news_path(news) %> +
<%= avatar(news.author) %><%= "#{link_to_project(news.project)}: " unless news.project == @project %> + <%= link_to h(news.title), project_news_path(news.project, news) %> <%= "(#{t(:label_x_comments, count: news.comments_count)})" if news.comments_count > 0 %>
<%= authoring news.created_at, news.author %>
- <% user = User.find(@application.client_credentials_user_id) %> + <% user = User.visible.find(@application.client_credentials_user_id) %> <%= t("oauth.client_credentials_impersonation_set_to") %> <%= link_to_user user %>
diff --git a/app/views/oauth/applications/show.html.erb b/app/views/oauth/applications/show.html.erb index abf63bb0592..cbb765714aa 100644 --- a/app/views/oauth/applications/show.html.erb +++ b/app/views/oauth/applications/show.html.erb @@ -54,7 +54,7 @@ See COPYRIGHT and LICENSE files for more details. <% component.with_attribute( key: t("oauth.client_credentials_impersonation_set_to") ) do %> - <%= link_to_user User.find_by(id: user_id) %> + <%= link_to_user User.visible.find_by(id: user_id) %><%= t("oauth.client_credentials_impersonation_warning") %> <% end %> diff --git a/app/views/user_mailer/news_added.html.erb b/app/views/user_mailer/news_added.html.erb index 28ef3745901..3062fbe6750 100644 --- a/app/views/user_mailer/news_added.html.erb +++ b/app/views/user_mailer/news_added.html.erb @@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -
<%= link_to @news.title, news_url(@news) %>
+<%= link_to @news.title, project_news_url(@news.project, @news) %>
<%= @news.author&.name %> <%= format_text @news.description, only_path: false %> diff --git a/app/views/user_mailer/news_added.text.erb b/app/views/user_mailer/news_added.text.erb index 90701cd335e..d9ff46760f5 100644 --- a/app/views/user_mailer/news_added.text.erb +++ b/app/views/user_mailer/news_added.text.erb @@ -28,7 +28,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <%= @news.title %> -<%= news_url(@news) %> +<%= project_news_url(@news.project, @news) %> <%= @news.author&.name %> <%= @news.description %> diff --git a/app/views/user_mailer/news_comment_added.html.erb b/app/views/user_mailer/news_comment_added.html.erb index 2e0c9b6ef3b..c38167d0336 100644 --- a/app/views/user_mailer/news_comment_added.html.erb +++ b/app/views/user_mailer/news_comment_added.html.erb @@ -27,7 +27,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= link_to(@news.title, news_url(@news)) %>
+<%= link_to(@news.title, project_news_url(@news.project, @news)) %>
<%= t(:text_user_wrote, value: @comment.author) %>
diff --git a/app/views/user_mailer/news_comment_added.text.erb b/app/views/user_mailer/news_comment_added.text.erb index d40f5daf976..3c0ecdb495d 100644 --- a/app/views/user_mailer/news_comment_added.text.erb +++ b/app/views/user_mailer/news_comment_added.text.erb @@ -28,7 +28,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> <%= @news.title %> -<%= news_url(@news) %> +<%= project_news_url(@news.project, @news) %> <%= t(:text_user_wrote, value: @comment.author) %> diff --git a/app/views/wiki_menu_items/select_main_menu_item.html.erb b/app/views/wiki_menu_items/select_main_menu_item.html.erb index 50ef64461e8..6a16a9842e5 100644 --- a/app/views/wiki_menu_items/select_main_menu_item.html.erb +++ b/app/views/wiki_menu_items/select_main_menu_item.html.erb @@ -43,8 +43,7 @@ See COPYRIGHT and LICENSE files for more details. <%= f.select :id, wiki_page_options_for_select(@possible_wiki_pages), { label: WikiPage.human_attribute_name(:title) }, - { size: @possible_wiki_pages.size, - id: "main-menu-item-select" } %> + { id: "main-menu-item-select" } %> <%= submit_tag t(:button_save), class: "button -primary" %> diff --git a/bin/rubocop b/bin/rubocop index 40330c0ff1c..5a20504716c 100755 --- a/bin/rubocop +++ b/bin/rubocop @@ -2,7 +2,7 @@ require "rubygems" require "bundler/setup" -# explicit rubocop config increases performance slightly while avoiding config confusion. +# Explicit RuboCop config increases performance slightly while avoiding config confusion. ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) load Gem.bin_path("rubocop", "rubocop") diff --git a/config/configuration.yml.example b/config/configuration.yml.example index adc6bfe86ad..fe4f7b4d9aa 100644 --- a/config/configuration.yml.example +++ b/config/configuration.yml.example @@ -384,7 +384,7 @@ default: # password: admin # By default, the APIv3 allows authentication through basic auth. - # Uncomment the following line to restrict APIv3 access to session. + # Uncomment the following line to prevent APIv3 access using Basic auth. # apiv3_enable_basic_auth: false # You can configure where users should be sent after the login diff --git a/config/constants/settings/definition.rb b/config/constants/settings/definition.rb index 9ab4a0b1d3c..cfd2af505b6 100644 --- a/config/constants/settings/definition.rb +++ b/config/constants/settings/definition.rb @@ -126,6 +126,13 @@ module Settings default: :quarantine, allowed: %i[quarantine delete] }, + api_tokens_enabled: { + default: true, + description: "Decide whether users can create personal API tokens in their account settings", + # Keeping old name only for backwards-compatibility, can be removed in OpenProject 18.0 + env_alias: "OPENPROJECT_REST__API__ENABLED", + format: :boolean + }, auth_source_sso: { description: "Configuration for Header-based Single Sign-On", format: :hash, @@ -657,7 +664,11 @@ module Settings }, installation_uuid: { format: :string, - default: nil + default: -> { SecureRandom.uuid }, + persist_on_first_read: true, + default_by_env: { + test: "test_uuid" + } }, internal_password_confirmation: { description: "Require password confirmations for certain administrative actions", @@ -965,9 +976,6 @@ module Settings repository_truncate_at: { default: 500 }, - rest_api_enabled: { - default: true - }, scm: { format: :hash, default: {}, @@ -1293,6 +1301,11 @@ module Settings description: "Redirect external links through a warning page before leaving the application", default: false, writable: -> { EnterpriseToken.allows_to?(:capture_external_links) } + }, + capture_external_links_require_login: { + description: "Require users to be logged in before being able to navigate to external links", + default: false, + writable: -> { EnterpriseToken.allows_to?(:capture_external_links) } } }.freeze diff --git a/config/initializers/feature_decisions.rb b/config/initializers/feature_decisions.rb index 51d3c11c9eb..e2519b3a26f 100644 --- a/config/initializers/feature_decisions.rb +++ b/config/initializers/feature_decisions.rb @@ -49,9 +49,6 @@ OpenProject::FeatureDecisions.add :calculated_value_project_attribute, description: "Allows the use of calculated values as a project attribute.", force_active: true -OpenProject::FeatureDecisions.add :beta_widgets, - description: "Enables BETA versions of widgets." - OpenProject::FeatureDecisions.add :mcp_server, description: "Enables the experimental MCP API." diff --git a/config/initializers/inplace_edit_fields.rb b/config/initializers/inplace_edit_fields.rb new file mode 100644 index 00000000000..5845f6864c7 --- /dev/null +++ b/config/initializers/inplace_edit_fields.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +Rails.application.config.to_prepare do + # Register the edit fields per attribute + OpenProject::InplaceEdit::FieldRegistry.register(:description, OpenProject::Common::InplaceEditFields::RichTextAreaComponent) + OpenProject::InplaceEdit::FieldRegistry.register(:status_explanation, OpenProject::Common::InplaceEditFields::RichTextAreaComponent) + + # Register the update handler per model + OpenProject::InplaceEdit::UpdateRegistry.register(Project, + handler: OpenProject::InplaceEdit::Handlers::ProjectUpdate, + contract: Projects::UpdateContract) +end diff --git a/config/initializers/new_framework_defaults_8_1.rb b/config/initializers/new_framework_defaults_8_1.rb new file mode 100644 index 00000000000..af849867498 --- /dev/null +++ b/config/initializers/new_framework_defaults_8_1.rb @@ -0,0 +1,79 @@ +# frozen_string_literal: true + +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 8.1 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `8.1`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +### +# Skips escaping HTML entities and line separators. When set to `false`, the +# JSON renderer no longer escapes these to improve performance. +# +# Example: +# class PostsController < ApplicationController +# def index +# render json: { key: "\u2028\u2029<>&" } +# end +# end +# +# Renders `{"key":"\u2028\u2029\u003c\u003e\u0026"}` with the previous default, but `{"key":" <>&"}` with the config +# set to `false`. +# +# Applications that want to keep the escaping behavior can set the config to `true`. +#++ +# OpenProject should not be affected by this change. At least the vast majority of JSON responses +# are rendered in the APIv3 which do not use the JSON renderer of ActionController. +# But keeping it set to true does not cost anything. +Rails.configuration.action_controller.escape_json_responses = true + +### +# Skips escaping LINE SEPARATOR (U+2028) and PARAGRAPH SEPARATOR (U+2029) in JSON. +# +# Historically these characters were not valid inside JavaScript literal strings but that changed in ECMAScript 2019. +# As such it's no longer a concern in modern browsers: https://caniuse.com/mdn-javascript_builtins_json_json_superset. +#++ +Rails.configuration.active_support.escape_js_separators_in_json = false + +### +# Raises an error when order dependent finder methods (e.g. `#first`, `#second`) are called without `order` values +# on the relation, and the model does not have any order columns (`implicit_order_column`, `query_constraints`, or +# `primary_key`) to fall back on. +# +# The current behavior of not raising an error has been deprecated, and this configuration option will be removed in +# Rails 8.2. +#++ +Rails.configuration.active_record.raise_on_missing_required_finder_order_columns = true + +### +# Controls how Rails handles path relative URL redirects. +# When set to `:raise`, Rails will raise an `ActionController::Redirecting::UnsafeRedirectError` +# for relative URLs without a leading slash, which can help prevent open redirect vulnerabilities. +# +# Example: +# redirect_to "example.com" # Raises UnsafeRedirectError +# redirect_to "@attacker.com" # Raises UnsafeRedirectError +# redirect_to "/safe/path" # Works correctly +# +# Applications that want to allow these redirects can set the config to `:log` (previous default) +# to only log warnings, or `:notify` to send ActiveSupport notifications. +#++ +Rails.configuration.action_controller.action_on_path_relative_redirect = :raise + +### +# Use a Ruby parser to track dependencies between Action View templates +#++ +Rails.configuration.action_view.render_tracker = :ruby + +### +# When enabled, hidden inputs generated by `form_tag`, `token_tag`, `method_tag`, and the hidden parameter fields +# included in `button_to` forms will omit the `autocomplete="off"` attribute. +# +# Applications that want to keep generating the `autocomplete` attribute for those tags can set it to `false`. +#++ +Rails.configuration.action_view.remove_hidden_field_autocomplete = true diff --git a/config/initializers/warden.rb b/config/initializers/warden.rb index 0918ad64492..a19ec7d5f0a 100644 --- a/config/initializers/warden.rb +++ b/config/initializers/warden.rb @@ -30,24 +30,20 @@ namespace = OpenProject::Authentication::Strategies::Warden -strategies = [ - [:basic_auth_failure, namespace::BasicAuthFailure, "Basic"], - [:global_basic_auth, namespace::GlobalBasicAuth, "Basic"], - [:user_basic_auth, namespace::UserBasicAuth, "Basic"], - [:oauth, namespace::DoorkeeperOAuth, "Bearer"], - [:anonymous_fallback, namespace::AnonymousFallback, "Basic"], - [:jwt_oidc, namespace::JwtOidc, "Bearer"], - [:session, namespace::Session, "Session"] -] - -strategies.each do |name, clazz, auth_scheme| - OpenProject::Authentication.add_strategy(name, clazz, auth_scheme) -end +OpenProject::Authentication.add_strategy(:basic_auth_failure, namespace::BasicAuthFailure, "Basic") +OpenProject::Authentication.add_strategy(:global_basic_auth, namespace::GlobalBasicAuth, "Basic") +OpenProject::Authentication.add_strategy(:user_basic_auth, namespace::UserBasicAuth, "Basic") +OpenProject::Authentication.add_strategy(:user_api_token, namespace::UserAPIToken, "Bearer") +OpenProject::Authentication.add_strategy(:oauth, namespace::DoorkeeperOAuth, "Bearer") +OpenProject::Authentication.add_strategy(:anonymous_fallback, namespace::AnonymousFallback, "Basic") +OpenProject::Authentication.add_strategy(:jwt_oidc, namespace::JwtOidc, "Bearer") +OpenProject::Authentication.add_strategy(:session, namespace::Session, "Session") OpenProject::Authentication.update_strategies(OpenProject::Authentication::Scope::API_V3, { store: false }) do |_| %i[global_basic_auth user_basic_auth basic_auth_failure + user_api_token oauth jwt_oidc session @@ -59,7 +55,7 @@ OpenProject::Authentication.update_strategies(OpenProject::Authentication::Scope end OpenProject::Authentication.update_strategies(OpenProject::Authentication::Scope::MCP_SCOPE, { store: false }) do |_| - %i[oauth jwt_oidc user_basic_auth basic_auth_failure] + %i[user_api_token oauth jwt_oidc user_basic_auth basic_auth_failure] end Rails.application.configure do |app| diff --git a/config/locales/crowdin/af.yml b/config/locales/crowdin/af.yml index 1c0d7673085..52217420d47 100644 --- a/config/locales/crowdin/af.yml +++ b/config/locales/crowdin/af.yml @@ -1039,7 +1039,6 @@ af: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ af: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Vertoon tot" attachment: @@ -1538,6 +1540,7 @@ af: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ af: role: "Rol" roles: "Rolle" search: "Search" + sprint: "Sprint" start_date: "Begindatum" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ af: label_duplicate: "duplicate" label_duplicates: "duplikate" label_edit: "Redigeer" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Wissel multikies" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ af: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ af: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ af: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index e63aad725a3..a2eba1a8d85 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -1075,7 +1075,6 @@ ar: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1200,6 +1199,9 @@ ar: dependencies: "الاعتماديات" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "أظهِر حتّى" attachment: @@ -1574,6 +1576,7 @@ ar: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2211,6 +2214,7 @@ ar: role: "الدور" roles: "دور" search: "البحث" + sprint: "Sprint" start_date: "تاريخ البدء" status: "الحالة" state: "State" @@ -3416,7 +3420,6 @@ ar: label_duplicate: "مكرر" label_duplicates: "التكرارات" label_edit: "تعديل" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "تبديل متعدد الخيارات" label_enabled_project_custom_fields: "تمكين الحقول المخصصة" @@ -4175,6 +4178,7 @@ ar: notice_successful_delete: "حذف ناجح." notice_successful_cancel: "Successful cancellation." notice_successful_update: "تحديث ناجح." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4340,6 +4344,7 @@ ar: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4522,6 +4527,9 @@ ar: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/az.yml b/config/locales/crowdin/az.yml index a06303477c9..9d36a2cee2e 100644 --- a/config/locales/crowdin/az.yml +++ b/config/locales/crowdin/az.yml @@ -1039,7 +1039,6 @@ az: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ az: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ az: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ az: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ az: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Düzəliş et" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ az: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ az: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ az: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/be.yml b/config/locales/crowdin/be.yml index 297a58379e1..5275aa233e0 100644 --- a/config/locales/crowdin/be.yml +++ b/config/locales/crowdin/be.yml @@ -1057,7 +1057,6 @@ be: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1182,6 +1181,9 @@ be: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1556,6 +1558,7 @@ be: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2155,6 +2158,7 @@ be: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3318,7 +3322,6 @@ be: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Рэдагаваць" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -4075,6 +4078,7 @@ be: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4238,6 +4242,7 @@ be: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4422,6 +4427,9 @@ be: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 5c9ebef1543..b0a521c3e3b 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -1039,7 +1039,6 @@ bg: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ bg: dependencies: "Зависимости" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Показване до" attachment: @@ -1538,6 +1540,7 @@ bg: not_available: "не е наличен поради системна конфигурация." not_deletable: "не може да бъде изтрито." not_current_user: "не е текущият потребител." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "е невалидна дата" not_a_datetime: "не е валидна дата и час." @@ -2099,6 +2102,7 @@ bg: role: "Роля" roles: "Роли" search: "Търсене" + sprint: "Sprint" start_date: "Начална дата" status: "Състояние" state: "State" @@ -3220,7 +3224,6 @@ bg: label_duplicate: "дубликат" label_duplicates: "дублирания" label_edit: "Редактиране" - label_edit_attribute: "Edit attribute" label_edit_x: "Редактиране: %{x}" label_enable_multi_select: "Превключване към множествен избор" label_enabled_project_custom_fields: "Разрешени потребителски полета" @@ -3975,6 +3978,7 @@ bg: notice_successful_delete: "Успешно изтриване." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Успешно обновяване." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ bg: permission_edit_project_query: "Редактиране на заявката за проект" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ bg: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index bf697d1d859..a7b88070a45 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -1036,7 +1036,6 @@ ca: title: "Falta un flux de treball per compartir paquets de treball" message: "No s'ha configurat cap flux de treball per a la funció \"Editor de paquets de treball\". Sense un flux de treball, el que es comparteix amb l'usuari no pot alterar l'estat del paquet de treball. Els fluxos de treball es poden copiar. Seleccioneu un tipus d'origen (p. ex., \"Tasca\") i una funció d'origen (p. ex., \"Membre\"). A continuació, seleccioneu els tipus d'objectius. Per començar, podeu seleccionar tots els tipus com a objectius. Finalment, seleccioneu la funció \"Editor de paquets de treball\" com a objectiu i premeu \"Copia\". Després d'haver creat els valors predeterminats, ajusteu els fluxos de treball com ho feu per a qualsevol altra funció." link_message: "Configura els fluxos de treball a l'administració." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1161,6 +1160,9 @@ ca: dependencies: "Dependències" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Mostrar fins" attachment: @@ -1535,6 +1537,7 @@ ca: not_available: "no és disponible degut a la configuració del sistema." not_deletable: "no es pot eliminar." not_current_user: "no és l'usuari actual." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "no és una data vàlida." not_a_datetime: "no és una data-i-hora vàlida." @@ -2096,6 +2099,7 @@ ca: role: "Rol" roles: "Rols" search: "Cercar" + sprint: "Sprint" start_date: "Data d'inici" status: "Estat" state: "State" @@ -3217,7 +3221,6 @@ ca: label_duplicate: "duplicats" label_duplicates: "duplicats" label_edit: "Editar" - label_edit_attribute: "Edit attribute" label_edit_x: "Edita: %{x}" label_enable_multi_select: "Activa/desactiva selecció múltiple" label_enabled_project_custom_fields: "Habilita camps personalitzats" @@ -3969,6 +3972,7 @@ ca: notice_successful_delete: "Esborrat correctament." notice_successful_cancel: "Successful cancellation." notice_successful_update: "S'ha modificat correctament." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4129,6 +4133,7 @@ ca: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4311,6 +4316,9 @@ ca: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ckb-IR.yml b/config/locales/crowdin/ckb-IR.yml index decbba5bf42..12d66461d9b 100644 --- a/config/locales/crowdin/ckb-IR.yml +++ b/config/locales/crowdin/ckb-IR.yml @@ -1039,7 +1039,6 @@ ckb-IR: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ ckb-IR: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ ckb-IR: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ ckb-IR: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ ckb-IR: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ ckb-IR: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ ckb-IR: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ ckb-IR: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index 72ec908ccc7..0ba86696b55 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -1057,7 +1057,6 @@ cs: title: "Pro sdílení pracovního balíčku chybí pracovní postup" message: "Pro roli 'Pracovní balíček' není nastaven žádný pracovní postup. Bez pracovního postupu nemůže sdílení s uživatelem změnit stav pracovního balíčku. Pracovní toky mohou být zkopírovány. Vyberte typ zdroje (např. 'Úkol') a zdrojovou roli (např. 'Člen'). Pak vyberte cílové typy. Na začátku můžete vybrat všechny typy jako cíle. Nakonec vyberte roli 'Editor pracovních balíčků' jako cíl a stiskněte 'Kopírovat'. Poté, co jste tak vytvořili výchozí nastavení, vyladit pracovní postupy, jak to děláte pro každou jinou roli." link_message: "Konfigurace pracovních postupů v administraci." - templated_subject_hint: Automaticky generováno pomocí typu %{type} summary: reports: category: @@ -1182,6 +1181,9 @@ cs: dependencies: "Závislosti" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Zobrazit do" attachment: @@ -1296,7 +1298,7 @@ cs: enabled_modules: "Povolené moduly" identifier: "Identifikátor" latest_activity_at: "Poslední aktivita" - parent: "Nadřazený projekt" + parent: "Podprojekt" project_creation_wizard_enabled: "Project initiation request" public_value: title: "Viditelnost" @@ -1556,6 +1558,7 @@ cs: not_available: "není k dispozici kvůli konfiguraci systému." not_deletable: "nelze odstranit" not_current_user: "není aktuální uživatel." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "nenalezeno." not_a_date: "není platné datum." not_a_datetime: "není platný čas." @@ -1665,7 +1668,7 @@ cs: meeting: error_conflict: "Nelze uložit, protože schůzku mezitím aktualizoval někdo jiný. Znovu načtěte stránku." notifications: - at_least_one_channel: "Pro odesílání notifikací musí být specifikován alespoň jeden kanál" + at_least_one_channel: "Alespoň jeden kanál pro odesílání oznámení musí být specifikován." attributes: read_ian: read_on_creation: "nelze nastavit na pravdivé při vytváření oznámení " @@ -1963,11 +1966,11 @@ cs: member: "Člen" news: "Novinky" notification: - one: "Notifikace" - few: "Notifikací" - many: "Notifikací" - other: "Notifikace" - placeholder_user: "Placeholder uživatel" + one: "Oznámení" + few: "Oznámení" + many: "Oznámení" + other: "Oznámení" + placeholder_user: "placeholder uživatel" project: one: "Projekt" few: "Projekty" @@ -2155,6 +2158,7 @@ cs: role: "Role" roles: "Role" search: "Vyhledávání" + sprint: "Sprint" start_date: "Datum zahájení" status: "Stav" state: "Stav" @@ -3037,7 +3041,7 @@ cs: ai: "Artificial Intelligence (AI)" aggregation: "Agregace" api_and_webhooks: "API & Webhooky" - mail_notification: "E-mailové notifikace" + mail_notification: "E-mailová upozornění" mails_and_notifications: "E-maily a oznámení" mcp_configurations: "Model Context Protocol (MCP)" quick_add: @@ -3123,7 +3127,7 @@ cs: by_project: "Nepřečteno dle projektu" by_reason: "Důvod" inbox: "Doručená pošta" - send_notifications: "Pro tuto akci odeslat notifikaci" + send_notifications: "Odeslat oznámení pro tuto akci" work_packages: subject: created: "Pracovní balíček byl vytvořen." @@ -3318,7 +3322,6 @@ cs: label_duplicate: "duplikovat" label_duplicates: "duplikovaů" label_edit: "Upravit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Přepnout multiselect" label_enabled_project_custom_fields: "Povoleno volitelné pole" @@ -3566,9 +3569,9 @@ cs: label_permissions: "Práva" label_permissions_report: "Přehled oprávnění" label_personalize_page: "Přizpůsobit tuto stránku" - label_placeholder_user: "Placeholder uživatel" + label_placeholder_user: "placeholder uživatel" label_placeholder_user_new: "" - label_placeholder_user_plural: "Placeholder uživatelé" + label_placeholder_user_plural: "placeholder uživatelé" label_planning: "Plánování" label_please_login: "Přihlaste se prosím" label_plugins: "Pluginy" @@ -3592,7 +3595,7 @@ cs: label_project_attribute_plural: "Atributy projektu" label_project_attribute_manage_link: "Správa atributů produktu" label_project_count: "Celkový počet projektů" - label_project_copy_notifications: "Během kopírování projektu odeslat notifikace e-mailem" + label_project_copy_notifications: "Během kopie projektu odeslat oznámení e-mailem" label_project_initiation_export_pdf: "Export PDF for %{project_creation_name}" label_project_latest: "Nejnovější projekty" label_project_default_type: "Povolit prázdný typ" @@ -3757,7 +3760,7 @@ cs: label_version_new: "Nová verze" label_version_edit: "Upravit verzi" label_version_plural: "Verze" - label_version_sharing_descendants: "S podprojekty" + label_version_sharing_descendants: "S Podprojekty" label_version_sharing_hierarchy: "S hierarchií projektu" label_version_sharing_none: "Není sdíleno" label_version_sharing_system: "Se všemi projekty" @@ -3865,28 +3868,28 @@ cs: digests: including_mention_singular: "včetně zmínky" including_mention_plural: "včetně %{number_mentioned} zmínění" - unread_notification_singular: "1 nepřečtená notifikace" - unread_notification_plural: "%{number_unread} nepřečtených notifikací" + unread_notification_singular: "1 nepřečtené oznámení" + unread_notification_plural: "%{number_unread} nepřečtených oznámení" you_have: "Máte" logo_alt_text: "Logo" mention: subject: "%{user_name} vás zmínil v #%{id} - %{subject}" notification: - center: "Centrum notifikací" + center: "Centrum oznámení" see_in_center: "Zobrazit komentář v oznamovacím centru" settings: "Změnit nastavení e-mailu" salutation: "Ahoj %{user}!" salutation_full_name: "Jméno a příjmení" work_packages: created_at: "Vytvořeno v %{timestamp} uživatelem %{user} " - login_to_see_all: "Přihlaste se pro zobrazení všech notifikací." + login_to_see_all: "Přihlaste se pro zobrazení všech oznámení." mentioned: "Byli jste zmíněni v komentáři" mentioned_by: "%{user} vás zmínil v komentáři OpenProject" more_to_see: - one: "Existuje ještě 1 pracovní balíček s notifikací." - few: "Existuje ještě %{count} pracovních balíčků s notifikacema." - many: "Existuje ještě %{count} pracovních balíčků s notifikacema." - other: "Existuje ještě %{count} pracovních balíčků s notifikacema." + one: "Máte ještě 1 pracovní balíček s notifikací." + few: "Existuje ještě %{count} pracovních balíčků s oznámeními." + many: "Máte ještě %{count} pracovních balíčků s notifikacemi." + other: "Existuje ještě %{count} pracovních balíčků s oznámeními." open_in_browser: "Otevřít v prohlížeči" reason: watched: "Sledováno" @@ -3895,7 +3898,7 @@ cs: mentioned: "Zmíněné" shared: "Sdílené" subscribed: "vše" - prefix: "Obdrženo z důvodu nastavení notifikací: %{reason}" + prefix: "Obdrženo z důvodu nastavení oznámení: %{reason}" date_alert_start_date: "Upozornění na datum" date_alert_due_date: "Upozornění na datum" reminder: "Připomínka" @@ -4074,6 +4077,7 @@ cs: notice_successful_delete: "Úspěšné odstranění." notice_successful_cancel: "Úspěšné zrušení." notice_successful_update: "Úspěšná aktualizace." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4194,7 +4198,7 @@ cs: permission_move_work_packages: "Přesun pracovních balíčků" permission_protect_wiki_pages: "Ochrana stránky wiki" permission_rename_wiki_pages: "Přejmenovat stránky wiki" - permission_save_queries: "Uložit zobrazení" + permission_save_queries: "Uložit pohled" permission_search_project: "Hledat projekt" permission_select_custom_fields: "Vybrat vlastní pole" permission_select_project_custom_fields: "Vyberte atributy projektu" @@ -4237,6 +4241,7 @@ cs: permission_edit_project_query: "Upravit dotaz projektu" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4421,6 +4426,9 @@ cs: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/page@@ -4675,7 +4683,7 @@ cs: enable_subscriptions_text_html: Umožňuje uživatelům s nezbytnými oprávněními přihlásit se do OpenProject kalendářů a získat přístup k informacím o pracovním balíčku prostřednictvím externího klienta kalendáře. Poznámka: Před povolením si prosím přečtěte podrobnosti o odběru. language_name_being_default: "%{language_name} (výchozí)" notifications: - events_explanation: "Určuje, pro kterou událost je odeslán e-mail. Pracovní balíčky jsou z tohoto seznamu vyloučeny, protože notifikace pro ně mohou být nastavena speciálně pro každého uživatele." + events_explanation: "Určuje, pro kterou událost je odeslán e-mail. Pracovní balíčky jsou z tohoto seznamu vyloučeny, protože oznámení pro ně mohou být nastavena speciálně pro každého uživatele." delay_minutes_explanation: "Odesílání e-mailu může být pozdrženo, aby bylo uživatelům s nakonfigurovaným v oznámení aplikace před odesláním pošty potvrzeno oznámení. Uživatelé, kteří si přečtou oznámení v aplikaci, nedostanou e-mail pro již přečtené oznámení." other: "Ostatní" passwords: "Hesla" @@ -4856,7 +4864,7 @@ cs: text_destroy_with_associated: "Existují další objekty, které jsou přiřazeny k pracovním balíčkům a které mají být odstraněny. Tyto objekty jsou následující typy:" text_destroy_what_to_do: "Co chcete udělat?" text_diff_truncated: "... Toto rozlišení bylo zkráceno, protože přesahuje maximální velikost, kterou lze zobrazit." - text_email_delivery_not_configured: "Doručení e-mailu není nakonfigurováno a notifikace jsou zakázány.\nNakonfigurujte váš SMTP server pro jejich povolení." + text_email_delivery_not_configured: "Doručení e-mailu není nakonfigurováno a oznámení jsou zakázána.\nNakonfigurujte váš SMTP server pro jejich povolení." text_enumeration_category_reassign_to: "Přiřadit je k této hodnotě:" text_enumeration_destroy_question: "%{count} objektů je přiřazeno k této hodnotě." text_file_repository_writable: "Do adresáře příloh lze zapisovat" diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index 1ffe1439150..52eae6cb415 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -1037,7 +1037,6 @@ da: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1162,6 +1161,9 @@ da: dependencies: "Aflæggere" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1536,6 +1538,7 @@ da: not_available: "is not available due to a system configuration." not_deletable: "Kan ikke slettes" not_current_user: "er ikke den aktuelle bruger." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2097,6 +2100,7 @@ da: role: "Rolle" roles: "Rollee" search: "Søg" + sprint: "Sprint" start_date: "Start dato" status: "Status" state: "State" @@ -3218,7 +3222,6 @@ da: label_duplicate: "duplicate" label_duplicates: "dubletter" label_edit: "Redigér" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Vælg multivalg" label_enabled_project_custom_fields: "Aktiverede brugerdefinerede felter" @@ -3973,6 +3976,7 @@ da: notice_successful_delete: "Sletning gennemført." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Opdatering gennemført." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4134,6 +4138,7 @@ da: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4316,6 +4321,9 @@ da: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index 8ce289d11cf..65a0f442963 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -85,11 +85,11 @@ de: title: "Enterprise-Token hinzufügen" type_token_text: "Enterprise-Token Text" token_placeholder: "Enterprise-Token Text hier einfügen" - add_token: "Enterprise-Edition Support Token hochladen" + add_token: "Enterprise edition Support Token hochladen" replace_token: "Aktuellen Enterprise edition Support Token ersetzen" order: "Enterprise on-premises bestellen" - paste: "Enterprise-Edition Support Token hier einfügen" - required_for_feature: "Dieses Add-on ist nur mit einem aktiven Enterprise-Edition Support-Token verfügbar." + paste: "Enterprise edition Support Token hier einfügen" + required_for_feature: "Dieses Add-on ist nur mit einem aktiven Enterprise edition Support-Token verfügbar." enterprise_link: "Klicken Sie hier für weitere Informationen." start_trial: "Kostenlose Testversion starten" book_now: "Jetzt buchen" @@ -921,10 +921,10 @@ de: tab: "Titel konfigurieren" manually_editable_subjects: label: "Manuell bearbeitbare Titel" - caption: "Nutzer:innen können die Titel der Arbeitspakete ohne Einschränkungen manuell eingeben und bearbeiten." + caption: "Benutzer können die Titel der Arbeitspakete ohne Einschränkungen manuell eingeben und bearbeiten." automatically_generated_subjects: label: "Automatisch generierte Titel" - caption: "Definieren Sie ein Schema aus referenzierten Attributen und Freitext für die automatische Generierung von Arbeitspakettiteln. Nutzer:innen können diese nicht manuell editieren." + caption: "Definieren Sie ein Schema aus referenzierten Attributen und Freitext für die automatische Generierung von Arbeitspakettiteln. Nutzer können diese nicht manuell editieren." token: label_with_context: "%{attribute_context}: %{attribute_label}" context: @@ -978,7 +978,7 @@ de: manual_with_children: "Hat Unteraufgaben aber ihre Startdaten werden ignoriert." title: automatic_mobile: "Automatisch geplant." - automatic_with_children: "Unteraufgaben bestimmen Termine." + automatic_with_children: "Die Termine sind durch untergeordnete Arbeitspakete bestimmt." automatic_with_predecessor: "Der Anfangstermin wird von einem Vorgänger festgelegt." manual_mobile: "Manuell geplant." manually_scheduled: "Manuell geplant – Daten unabhängig von Beziehungen." @@ -1032,7 +1032,6 @@ de: title: "Der Workflow für das Teilen von Arbeitspaketen fehlt" message: "Es ist kein Workflow für die Rolle ‚Work package editor‘ konfiguriert. Nur mit einem solchen Workflow können Benutzer, mit denen ein Arbeitspaket geteilt wurde, den Status des Arbeitspakets ändern. Workflows lassen sich einfach kopieren. Wählen Sie dazu einen Quell-Typ (z. B. ‚Task‘) und eine Quell-Rolle (z. B. 'Member') aus. Wählen Sie dann die Ziel-Typen aus. Als ersten Schritt können Sie alle Typen als Ziel-Typen auswählen. Danach wählen Sie die Ziel-Rolle ‚Work package editor‘ aus und drücken Sie auf den Knopf ‚Kopieren‘. Nachdem Sie hiermit eine Grundlage geschaffen haben, können Sie danach diese Workflows weiter anpassen, ganz genau wie Sie es für jede andere Rolle bereits getan haben." link_message: "Konfigurieren Sie die Workflows in der Administration." - templated_subject_hint: Automatisch durch den Typ %{type} erzeugt summary: reports: category: @@ -1081,7 +1080,7 @@ de: label_child_plural: "Unteraufgaben" new_child: "Neue Unteraufgabe" new_child_description: "Erstellt ein zugehöriges Arbeitspaket als Unteraufgabe des aktuellen (übergeordneten) Arbeitspakets" - child: "Unteraufgabe" + child: "Kind" child_description: "Macht das zugehörige Arbeitspaket zu einer Unteraufgabe des aktuellen (übergeordneten) Arbeitspakets" parent: "Übergeordnetes Arbeitspaket" parent_description: "Wandelt das verknüpfte in ein übergeordnetes Arbeitspaket dieses Arbeitspakets um" @@ -1156,6 +1155,9 @@ de: dependencies: "Abhängigkeiten" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "Enddatum" announcements: show_until: "Anzeigen bis" attachment: @@ -1325,7 +1327,7 @@ de: column_names: "Spalten" relations_to_type_column: "Beziehungen zu %{type}" relations_of_type_column: "Beziehungen der Art: %{type}" - child_work_packages: "Unteraufgaben" + child_work_packages: "Kinder" group_by: "Gruppiere Ergebnisse nach" sort_by: "Ergebnisse sortieren nach" filters: "Filter" @@ -1530,6 +1532,7 @@ de: not_available: "ist aufgrund einer Systemkonfiguration nicht verfügbar." not_deletable: "kann nicht entfernt werden." not_current_user: "ist nicht der aktuelle Benutzer." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "nicht gefunden." not_a_date: "ist kein gültiges Datum." not_a_datetime: "ist kein gültiges Datum." @@ -1822,7 +1825,7 @@ de: status_transition_invalid: "ist ungültig, da kein valider Übergang vom alten zum neuen Status für die aktuelle Rolle des Nutzers existiert." status_invalid_in_type: "ist ungültig, da der aktuelle Status nicht in diesem Typ vorhanden ist." type: - cannot_be_milestone_due_to_children: "kann kein Meilenstein werden, da dieses Arbeitspaket Unteraufgaben besitzt." + cannot_be_milestone_due_to_children: "kann kein Meilenstein werden, da dieses Arbeitspaket Unterelemente besitzt." priority_id: only_active_priorities_allowed: "muss aktiv sein." category: @@ -2091,6 +2094,7 @@ de: role: "Rollen" roles: "Rollen" search: "Suche" + sprint: "Sprint" start_date: "Anfangstermin" status: "Status" state: "Status" @@ -2626,7 +2630,7 @@ de: error_custom_option_not_found: "Option ist nicht vorhanden." error_enterprise_plan_needed: "Sie benötigen den Enterprise-Plan %{plan}, um diese Aktion durchzuführen." error_enterprise_activation_user_limit: "Ihr Konto konnte nicht aktiviert werden (Nutzerlimit erreicht). Bitte kontaktieren Sie Ihren Administrator um Zugriff zu erhalten." - error_enterprise_token_invalid_domain: "Die Enterprise-Edition ist nicht aktiv. Die aktuelle Domain (%{actual}) entspricht nicht dem erwarteten Hostnamen (%{expected})." + error_enterprise_token_invalid_domain: "Die Enterprise edition ist nicht aktiv. Die aktuelle Domain (%{actual}) entspricht nicht dem erwarteten Hostnamen (%{expected})." error_failed_to_delete_entry: "Fehler beim Löschen dieses Eintrags." error_in_dependent: "Fehler beim Versuch, abhängiges Objekt zu ändern: %{dependent_class} #%{related_id} - %{related_subject}: %{error}" error_in_new_dependent: "Fehler beim Versuch, abhängiges Objekt zu erstellen: %{dependent_class} - %{related_subject}: %{error}" @@ -2901,7 +2905,7 @@ de: dates: working: "%{date} ist jetzt ein Arbeitstag" non_working: "%{date} ist jetzt ein arbeitsfreier Tag" - progress_mode_changed_to_status_based: Fortschrittberechnung wurde auf Status-bezogen gesetzt + progress_mode_changed_to_status_based: Fortschrittberechnung wurde auf Status-basiert gesetzt status_excluded_from_totals_set_to_false_message: jetzt in den Gesamtwerten der Hierarchie enthalten status_excluded_from_totals_set_to_true_message: jetzt von den Hierarchie-Gesamtwerten ausgeschlossen status_percent_complete_changed: "% abgeschlossen von %{old_value}% auf %{new_value} % geändert" @@ -3212,7 +3216,6 @@ de: label_duplicate: "Duplikat" label_duplicates: "Duplikat von" label_edit: "Bearbeiten" - label_edit_attribute: "Attribut bearbeiten" label_edit_x: "Bearbeiten: %{x}" label_enable_multi_select: "Mehrfachauswahl umschalten" label_enabled_project_custom_fields: "Aktivierte benutzerdefinierte Felder" @@ -3225,7 +3228,7 @@ de: label_enumerations: "Aufzählungen" label_enterprise: "Enterprise" label_enterprise_active_users: "%{current}/%{limit} gebuchte aktive Nutzer" - label_enterprise_edition: "Enterprise Edition" + label_enterprise_edition: "Enterprise edition" label_enterprise_support: "Enterprise Support" label_environment: "Umgebung" label_estimates_and_progress: "Schätzungen und Fortschritt" @@ -3967,6 +3970,7 @@ de: notice_successful_delete: "Erfolgreich gelöscht." notice_successful_cancel: "Erfolgreiche Absage." notice_successful_update: "Erfolgreich aktualisiert." + notice_successful_move: "Erfolgreich von %{from} nach %{to} verschoben." notice_unsuccessful_create: "Erstellung fehlgeschlagen." notice_unsuccessful_create_with_reason: "Erstellung fehlgeschlagen: %{reason}" notice_unsuccessful_update: "Aktualisierung fehlgeschlagen." @@ -4128,6 +4132,7 @@ de: permission_edit_project_query: "Projektabfrage bearbeiten" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "Keine Portfolios" @@ -4281,7 +4286,7 @@ de: update_timeout: "Speichere die Informationen bzgl. des genutzten Festplattenspeichers eines Projektarchivs für N Minuten.\nErhöhen Sie diesen Wert zur Verbesserung der Performance, da die Erfassung des genutzten Festplattenspeichers Ressourcen-intensiv ist." oauth_application_details: "Der Client Geheimcode wird nach dem Schließen dieses Fensters nicht mehr zugänglich sein. Bitte kopieren Sie diese Werte in die Nextcloud OpenProject Integrationseinstellungen:" oauth_application_details_link_text: "Zu den Einstellungen gehen" - setup_documentation_details: "Wenn Sie Hilfe bei der Konfiguration eines neuen Dateispeichers benötigen, konsultieren Sie bitte die Dokumentation: " + setup_documentation_details: "Wenn Sie Hilfe bei der Konfiguration eines neuen Datei-Speichers benötigen, konsultieren Sie bitte die Dokumentation: " setup_documentation_details_link_text: "Dateispeicher einrichten" show_warning_details: "Um diesen Dateispeicher nutzen zu können, müssen Sie das Modul und den spezifischen Speicher in den Projekteinstellungen jedes gewünschten Projekts aktivieren." subversion: @@ -4312,6 +4317,9 @@ de: setting_capture_external_links: "Externe Links abfangen" setting_capture_external_links_text: > Wenn diese Funktion aktiviert ist, werden alle externen Links in formatiertem Text auf eine vor dem Link warnende Seite in der Applikation umgeleitet, bevor sie die Anwendung verlassen. Dies hilft, Benutzer vor potenziell bösartigen externen Websites zu schützen. + setting_capture_external_links_require_login: "Benutzer müssen angemeldet sein" + setting_capture_external_links_require_login_text: > + Wenn aktiviert, müssen Benutzer angemeldet sein, um externe Links anklicken und fortfahren zu können. setting_after_first_login_redirect_url: "Weiterleitung nach erster Anmeldung" setting_after_first_login_redirect_url_text_html: > Legen Sie einen Pfad fest, an den Nutzer:innen nach der ersten Anmeldung weitergeleitet werden. Wenn leer, führt er auf die Startseite des Onboarding-Tours.Beispiel:
/meine/seite@@ -4919,7 +4927,7 @@ de: warning_user_limit_reached_admin: > Das Hinzufügen zusätzlicher Benutzer überschreitet das aktuelle Benutzerlimit. Bitte aktualisieren Sie Ihr Abonnement um sicherzustellen, dass externe Benutzer auf diese Instanz zugreifen können. warning_user_limit_reached_instructions: > - Du hast dein Nutzerlimit erreicht (%{current}/%{max} active users). Bitte kontaktiere sales@openproject.com um deinen Enterprise Edition Plan upzugraden und weitere Nutzer hinzuzufügen. + Du hast dein Nutzerlimit erreicht (%{current}/%{max} active users). Bitte kontaktiere sales@openproject.com um deinen Enterprise edition Plan upzugraden und weitere Nutzer hinzuzufügen. warning_protocol_mismatch_html: > warning_bar: diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index 2d4337e06c3..599f5c171d0 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -1035,7 +1035,6 @@ el: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1160,6 +1159,9 @@ el: dependencies: "Εξαρτήσεις" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Εμφάνιση μέχρι" attachment: @@ -1534,6 +1536,7 @@ el: not_available: "is not available due to a system configuration." not_deletable: "δεν μπορεί να διαγραφεί." not_current_user: "δεν είναι ο τρέχων χρήστης." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "δεν είναι έγκυρη ημερομηνία." not_a_datetime: "δεν είναι έγκυρη ημερομηνία και ώρα." @@ -2095,6 +2098,7 @@ el: role: "Ρόλος" roles: "Ρόλοι" search: "Αναζήτηση" + sprint: "Sprint" start_date: "Ημερομηνία έναρξης" status: "Κατάσταση" state: "State" @@ -3216,7 +3220,6 @@ el: label_duplicate: "αντιγραφή" label_duplicates: "αντίγραφα" label_edit: "Επεξεργασία" - label_edit_attribute: "Edit attribute" label_edit_x: "Επεξεργασία: %{x}" label_enable_multi_select: "Ενεργοποίηση πολλαπλής επιλογής" label_enabled_project_custom_fields: "Ενεργοποίηση προσαρμοσμένων πεδίων" @@ -3970,6 +3973,7 @@ el: notice_successful_delete: "Επιτυχής διαγραφή." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Επιτυχής ενημέρωση." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4131,6 +4135,7 @@ el: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4315,6 +4320,9 @@ el: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/eo.yml b/config/locales/crowdin/eo.yml index 8c6fc801754..cf846739ac0 100644 --- a/config/locales/crowdin/eo.yml +++ b/config/locales/crowdin/eo.yml @@ -1039,7 +1039,6 @@ eo: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ eo: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Montri ĝis" attachment: @@ -1538,6 +1540,7 @@ eo: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "ne estas la nuna uzanto." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "Ĝi ne estas valida dato." not_a_datetime: "Ĝi ne estas valida dato/horo." @@ -2099,6 +2102,7 @@ eo: role: "Rolo" roles: "Roloj" search: "Search" + sprint: "Sprint" start_date: "Komencdato" status: "Stato" state: "State" @@ -3220,7 +3224,6 @@ eo: label_duplicate: "duobligi" label_duplicates: "duobligoj" label_edit: "Redakti" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Baskuligi plurelekton" label_enabled_project_custom_fields: "Ŝalti proprajn kampojn" @@ -3975,6 +3978,7 @@ eo: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ eo: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ eo: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index 5460c591e81..d2abdd8bd33 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -986,7 +986,7 @@ es: automatic_with_children: "Fechas determinadas por paquetes de trabajo secundarios." automatic_with_predecessor: "La fecha de inicio la fija un predecesor." manual_mobile: "Programado manualmente." - manually_scheduled: "Programado manualmente. No afectadas por relaciones." + manually_scheduled: "Programado manualmente. Fechas no afectadas por relaciones." blankslate: title: "Sin predecesores" description: "Para activar la programación automática, este paquete de trabajo debe tener al menos un predecesor. Entonces se programará automáticamente para que comience después del predecesor más cercano." @@ -1037,7 +1037,6 @@ es: title: "Falta el flujo de trabajo para compartir paquetes de trabajo" message: "Ningún flujo de trabajo está configurado para el rol 'Editor de paquetes de trabajo'. Sin un flujo de trabajo, el usuario compartido no puede alterar el estado del paquete de trabajo. Los flujos de trabajo pueden ser copiados. Seleccione un tipo de base (por ejemplo, 'Tarea') y el rol de base (por ejemplo, 'Miembro'). Luego seleccione los tipos de destino. Para empezar, puede seleccionar todos los tipos como objetivos. Por último, seleccione el papel de \"Editor de paquetes de trabajo\" como objetivo y presione \"Copiar\". Después de haber creado así los valores predeterminados, ajuste los flujos de trabajo como lo hace para cualquier otro rol." link_message: "Configure un flujo de trabajo en la administración." - templated_subject_hint: Generado automáticamente a través del tipo %{type} summary: reports: category: @@ -1161,6 +1160,9 @@ es: dependencies: "Dependencias" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Mostrar hasta" attachment: @@ -1535,6 +1537,7 @@ es: not_available: "no está disponible debido a una configuración del sistema." not_deletable: "no se puede eliminar." not_current_user: "no es el usuario actual." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "no encontrado." not_a_date: "no es una fecha válida." not_a_datetime: "no es una fecha/hora válida." @@ -2096,6 +2099,7 @@ es: role: "Perfil" roles: "Roles" search: "Buscar" + sprint: "Sprint" start_date: "Fecha de inicio" status: "Estado" state: "Estado" @@ -3217,7 +3221,6 @@ es: label_duplicate: "duplicar" label_duplicates: "duplicados" label_edit: "Editar" - label_edit_attribute: "Editar atributo" label_edit_x: "Editar: %{x}" label_enable_multi_select: "Selección multiple" label_enabled_project_custom_fields: "Habilitar campos personalizados" @@ -3972,6 +3975,7 @@ es: notice_successful_delete: "Eliminado con éxito." notice_successful_cancel: "Cancelación exitosa." notice_successful_update: "Actualización correcta." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Error en la creación." notice_unsuccessful_create_with_reason: "Error en la creación: %{reason}" notice_unsuccessful_update: "Error al actualizar." @@ -4132,6 +4136,7 @@ es: permission_edit_project_query: "Editar vistas de proyecto" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 carteras" @@ -4316,6 +4321,9 @@ es: setting_capture_external_links: "Detección de enlaces externos" setting_capture_external_links_text: > Cuando esta función está habilitada, todos los enlaces externos en texto formateado redirigirán a una página de advertencia antes de salir de la aplicación. Esto ayuda a proteger a los usuarios de sitios web externos potencialmente maliciosos. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Primera redirección de inicio de sesión" setting_after_first_login_redirect_url_text_html: > Establezca una ruta para redirigir a los usuarios tras su primer inicio de sesión. Si está vacía, redirige a la página de inicio del recorrido de incorporación.Ejemplo:
/my/pagediff --git a/config/locales/crowdin/et.yml b/config/locales/crowdin/et.yml index 4d8bb73e86b..75a95df714b 100644 --- a/config/locales/crowdin/et.yml +++ b/config/locales/crowdin/et.yml @@ -1039,7 +1039,6 @@ et: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ et: dependencies: "Sõltuvused" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ et: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "ei leitud." not_a_date: "pole korrektne kuupäev." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ et: role: "Roll" roles: "Rollid" search: "Otsi" + sprint: "Sprint" start_date: "Alguskuupäev" status: "Olek" state: "State" @@ -3220,7 +3224,6 @@ et: label_duplicate: "duplicate" label_duplicates: "duplikaadid" label_edit: "Muuda" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Võimalda mitmene valik" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ et: notice_successful_delete: "Kustutamine õnnestus." notice_successful_cancel: "Tühistatud." notice_successful_update: "Uuendamine õnnestus." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Uuendamine ebaõnnestus." @@ -4136,6 +4140,7 @@ et: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ et: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/eu.yml b/config/locales/crowdin/eu.yml index 713fb7f24c4..e0c8940b1cc 100644 --- a/config/locales/crowdin/eu.yml +++ b/config/locales/crowdin/eu.yml @@ -1039,7 +1039,6 @@ eu: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ eu: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ eu: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ eu: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Hasiera data" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ eu: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ eu: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ eu: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ eu: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/fa.yml b/config/locales/crowdin/fa.yml index e9effa20d1b..9e268c881f7 100644 --- a/config/locales/crowdin/fa.yml +++ b/config/locales/crowdin/fa.yml @@ -1039,7 +1039,6 @@ fa: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ fa: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ fa: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ fa: role: "Role" roles: "نقشها" search: "Search" + sprint: "Sprint" start_date: "تاریخ شروع" status: "وضعیت" state: "State" @@ -3220,7 +3224,6 @@ fa: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "ویرایش" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ fa: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ fa: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ fa: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index efcfadb42f8..4ba7336bd1b 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -1039,7 +1039,6 @@ fi: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ fi: dependencies: "Riippuvuudet" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Näytä tähän päivään asti" attachment: @@ -1538,6 +1540,7 @@ fi: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "ei ole kelvollinen päivämäärä." not_a_datetime: "ei ole kelvollinen aika." @@ -2099,6 +2102,7 @@ fi: role: "Rooli" roles: "Roolit" search: "Haku" + sprint: "Sprint" start_date: "Aloituspäivä" status: "Tila" state: "State" @@ -3220,7 +3224,6 @@ fi: label_duplicate: "kaksoiskappale" label_duplicates: "kaksoiskappaleet" label_edit: "Muokkaa" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Vaihda monivalinta" label_enabled_project_custom_fields: "Käytössä olevat mukautetut kentät" @@ -3975,6 +3978,7 @@ fi: notice_successful_delete: "Poisto onnistui." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Päivitys onnistui." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ fi: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ fi: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index ae265d040d8..bc2071f4e92 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -1039,7 +1039,6 @@ fil: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ fil: dependencies: "Dependencia" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "I-displey hanggang" attachment: @@ -1538,6 +1540,7 @@ fil: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "ay hindi balido ang petsa." not_a_datetime: "ay hindi balido ang petsa ng oras." @@ -2099,6 +2102,7 @@ fil: role: "Tungkulin" roles: "Ang mga tungkulin" search: "Hanapin" + sprint: "Sprint" start_date: "Petsa ng pagsimula" status: "Estado" state: "State" @@ -3220,7 +3224,6 @@ fil: label_duplicate: "gayahin" label_duplicates: "mga ginaya" label_edit: "I-edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multi select" label_enabled_project_custom_fields: "Pinagana ang mga custom na patlang" @@ -3975,6 +3978,7 @@ fil: notice_successful_delete: "Matagumpay ang pagtanggal." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Matagumpay nai-update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ fil: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4318,6 +4323,9 @@ fil: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 8b02f5d1a22..86d2f8c4353 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -1037,7 +1037,6 @@ fr: title: "Flux de travail manquant pour le partage de lots de travaux" message: "Aucun flux de travail n'est configuré pour le rôle 'Éditeur de lots de travaux'. Sans flux de travail, le partage avec l'utilisateur ne permet pas de modifier l'état du lot de travaux. Les flux de travail peuvent être copiés. Sélectionnez un type de source (par exemple 'Tâche') et un rôle de source (par exemple 'Membre'). Sélectionnez ensuite les types cibles. Pour commencer, vous pouvez sélectionner tous les types comme cibles. Enfin, sélectionnez le rôle 'Éditeur de lot de travaux' comme cible et cliquez sur 'Copier'. Après avoir ainsi créé les valeurs par défaut, affinez les flux de travail comme vous le faites pour tous les autres rôles." link_message: "Configurez les flux de travail dans l'administration." - templated_subject_hint: Généré automatiquement par le type %{type} summary: reports: category: @@ -1162,6 +1161,9 @@ fr: dependencies: "Dépendances" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Afficher jusqu'à" attachment: @@ -1536,6 +1538,7 @@ fr: not_available: "n'est pas disponible en raison d'une configuration système." not_deletable: "ne peut pas être supprimé" not_current_user: "n'est pas l'utilisateur actuel." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "introuvable." not_a_date: "n'est pas une date valide." not_a_datetime: "n'est pas une heure valide." @@ -2097,6 +2100,7 @@ fr: role: "Rôle" roles: "Rôles" search: "Recherche" + sprint: "Sprint" start_date: "Date de début" status: "Statut" state: "État" @@ -3218,7 +3222,6 @@ fr: label_duplicate: "dupliquer" label_duplicates: "Doublons" label_edit: "Éditer" - label_edit_attribute: "Modifier l'attribut" label_edit_x: "Modifier : %{x}" label_enable_multi_select: "Basculer multisélection" label_enabled_project_custom_fields: "Champs personnalisés activés" @@ -3973,6 +3976,7 @@ fr: notice_successful_delete: "Suppression réussie." notice_successful_cancel: "Annulation réussie." notice_successful_update: "Mise à jour réussie." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Échec de la création." notice_unsuccessful_create_with_reason: "Échec de la création : %{reason}" notice_unsuccessful_update: "Mise à jour échouée." @@ -4134,6 +4138,7 @@ fr: permission_edit_project_query: "Modifier la requête du projet" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portefeuille" @@ -4318,6 +4323,9 @@ fr: setting_capture_external_links: "Saisir les liens externes" setting_capture_external_links_text: > Lorsque cette option est activée, tous les liens externes en texte formaté sont redirigés vers une page d'avertissement avant de quitter l'application. Cela permet de protéger les utilisateurs contre les sites web externes potentiellement malveillants. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Redirection de première connexion" setting_after_first_login_redirect_url_text_html: > Définissez un chemin pour rediriger les utilisateurs après leur première connexion. S’il est vide, les utilisateurs seront redirigés vers la page d'accueil de la visite d'intégration.Exemple :
/my/pagediff --git a/config/locales/crowdin/he.yml b/config/locales/crowdin/he.yml index 891fc7cdc34..4934ceb0501 100644 --- a/config/locales/crowdin/he.yml +++ b/config/locales/crowdin/he.yml @@ -1057,7 +1057,6 @@ he: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1182,6 +1181,9 @@ he: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1556,6 +1558,7 @@ he: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "זה לא המשתמש הנכון." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2155,6 +2158,7 @@ he: role: "תפקיד" roles: "תפקידים" search: "חיפוש" + sprint: "Sprint" start_date: "תאריך התחלה" status: "מצב" state: "State" @@ -3318,7 +3322,6 @@ he: label_duplicate: "duplicate" label_duplicates: "פריטים כפולים" label_edit: "עריכה" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "בטל בחירה מרובה" label_enabled_project_custom_fields: "Enabled custom fields" @@ -4075,6 +4078,7 @@ he: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4238,6 +4242,7 @@ he: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4422,6 +4427,9 @@ he: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/hi.yml b/config/locales/crowdin/hi.yml index 6a9508fce44..1b2fcd87e1d 100644 --- a/config/locales/crowdin/hi.yml +++ b/config/locales/crowdin/hi.yml @@ -1037,7 +1037,6 @@ hi: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1162,6 +1161,9 @@ hi: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1536,6 +1538,7 @@ hi: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2097,6 +2100,7 @@ hi: role: "भूमिका" roles: "भूमिकाएं" search: "Search" + sprint: "Sprint" start_date: "प्रारंभ दिनांक" status: "अवस्था" state: "State" @@ -3218,7 +3222,6 @@ hi: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "संपादित करें" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3973,6 +3976,7 @@ hi: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4134,6 +4138,7 @@ hi: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4318,6 +4323,9 @@ hi: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index 428024bfe29..0d7a0a87dc3 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -1048,7 +1048,6 @@ hr: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1173,6 +1172,9 @@ hr: dependencies: "Ovisnosti" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Prikaži do" attachment: @@ -1547,6 +1549,7 @@ hr: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2127,6 +2130,7 @@ hr: role: "Role" roles: "Role" search: "Pretraživanje" + sprint: "Sprint" start_date: "Datum Početka" status: "Status" state: "State" @@ -3269,7 +3273,6 @@ hr: label_duplicate: "duplicate" label_duplicates: "duplikati" label_edit: "Uredi" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Aktiviraj/deaktiviraj selekciju" label_enabled_project_custom_fields: "Omogućena prilagođena polja" @@ -4025,6 +4028,7 @@ hr: notice_successful_delete: "Brisanje uspješno." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Ažuriranje je uspješno završeno." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4187,6 +4191,7 @@ hr: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4371,6 +4376,9 @@ hr: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index 458d5f4d5a7..e358ae7c5d5 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -1038,7 +1038,6 @@ hu: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1163,6 +1162,9 @@ hu: dependencies: "Szükséges összetevők" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Megjelenít eddig" attachment: @@ -1537,6 +1539,7 @@ hu: not_available: "nem érhető el a rendszer konfigurációja miatt.\n" not_deletable: "nem törölhető" not_current_user: "nem az aktuális felhasználó" + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "nem érvényes dátum." not_a_datetime: "ez nem érvényes dátum." @@ -2098,6 +2101,7 @@ hu: role: "Szerepkör" roles: "Szerepkörök" search: "Keresés" + sprint: "Sprint" start_date: "Indulási dátum" status: "Állapot" state: "State" @@ -3219,7 +3223,6 @@ hu: label_duplicate: "duplikált" label_duplicates: "Ismétlődések" label_edit: "Szerkesztés" - label_edit_attribute: "Edit attribute" label_edit_x: "Szerkesztés: %{x}" label_enable_multi_select: "Multiselect ki-/ bekapcsolása" label_enabled_project_custom_fields: "Egyéni mezők engedélyezve" @@ -3973,6 +3976,7 @@ hu: notice_successful_delete: "Sikeres törlés." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Sikeres frissítés." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4134,6 +4138,7 @@ hu: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4318,6 +4323,9 @@ hu: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index d316441255a..1936d3d9fce 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -1026,7 +1026,6 @@ id: title: "Alur kerja yang hilang untuk berbagi paket kerja" message: "Tidak ada alur kerja yang dikonfigurasikan untuk peran 'Editor paket kerja'. Tanpa alur kerja, pengguna yang dibagikan tidak dapat mengubah status paket kerja. Alur kerja dapat disalin. Pilih jenis sumber (misalnya 'Tugas') dan peran sumber (misalnya 'Anggota'). Kemudian pilih jenis target. Sebagai permulaan, Anda dapat memilih semua jenis sebagai target. Terakhir, pilih peran 'Editor paket kerja' sebagai target dan tekan 'Salin'. Setelah membuat default, sesuaikan alur kerja seperti yang Anda lakukan untuk setiap peran lainnya." link_message: "Mengonfigurasi alur kerja dalam administrasi." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1151,6 +1150,9 @@ id: dependencies: "Dependensi" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1525,6 +1527,7 @@ id: not_available: "tidak tersedia karena konfigurasi sistem." not_deletable: "tidak dapat dihapus." not_current_user: "bukan pengguna saat ini." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "bukan tanggal yang valid." not_a_datetime: "bukan tanggal waktu yang valid." @@ -2067,6 +2070,7 @@ id: role: "Role" roles: "Roles" search: "Cari" + sprint: "Sprint" start_date: "Tanggal start" status: "Status" state: "State" @@ -3167,7 +3171,6 @@ id: label_duplicate: "duplicate" label_duplicates: "duplikat" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Beralih ke multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3921,6 +3924,7 @@ id: notice_successful_delete: "Berhasil dihapus." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Berhasil diperbarui." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4080,6 +4084,7 @@ id: permission_edit_project_query: "Edit project query" placeholders: default: "Hapus nilai" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4262,6 +4267,9 @@ id: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 18aed44a241..fa1104aef1d 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -1036,7 +1036,6 @@ it: title: "Flusso di lavoro mancante per la condivisione di macro-attività" message: "Nessun flusso di lavoro è configurato per il ruolo \"Editor di macro-attività\". Senza un flusso di lavoro, la condivisione con l'utente non può alterare lo stato della macro-attività. I flussi di lavoro possono essere copiati. Seleziona un tipo di origine (ad esempio \"Attività\") e un ruolo di origine (ad esempio \"Membro\"). Quindi seleziona i tipi obiettivo. Per cominciare, potresti selezionare tutti i tipi come obiettivi. Infine, seleziona il ruolo \"Editor di macro-attività\" come destinazione e premi \"Copia\". Dopo aver creato le impostazioni predefinite, perfeziona i flussi di lavoro come fai per ogni altro ruolo." link_message: "Configura i flussi di lavoro nell'amministrazione." - templated_subject_hint: Generato automaticamente attraverso il tipo %{type} summary: reports: category: @@ -1161,6 +1160,9 @@ it: dependencies: "Dipendenze" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Visualizza fino a" attachment: @@ -1535,6 +1537,7 @@ it: not_available: "non è disponibile a causa di una configurazione di sistema." not_deletable: "non può essere eliminato." not_current_user: "non è l'utente attuale." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "non trovato." not_a_date: "non è una data valida." not_a_datetime: "non è un'orario valido." @@ -2096,6 +2099,7 @@ it: role: "Ruolo" roles: "Ruoli" search: "Cerca" + sprint: "Sprint" start_date: "Data di inizio" status: "Stato" state: "Stato" @@ -3217,7 +3221,6 @@ it: label_duplicate: "duplica" label_duplicates: "duplica" label_edit: "Modifica" - label_edit_attribute: "Modifica attributo" label_edit_x: "Modifica: %{x}" label_enable_multi_select: "Attiva/disattiva multiselezione" label_enabled_project_custom_fields: "Campi personalizzati abilitati" @@ -3972,6 +3975,7 @@ it: notice_successful_delete: "Cancellato con successo." notice_successful_cancel: "Cancellato con successo." notice_successful_update: "Aggiornato con successo." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creazione fallita." notice_unsuccessful_create_with_reason: "Creazione non riuscita: %{reason}" notice_unsuccessful_update: "Aggiornamento non riuscito." @@ -4133,6 +4137,7 @@ it: permission_edit_project_query: "Modifica elenco di progetti" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolio" @@ -4317,6 +4322,9 @@ it: setting_capture_external_links: "Intercetta link esterni" setting_capture_external_links_text: > Se l'opzione è abilitata, tutti i link esterni in testo formattato verranno reindirizzati a una pagina di avviso prima di uscire dall'applicazione. Questo aiuta a proteggere gli utenti da siti web esterni potenzialmente dannosi. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Reindirizzamento di primo accesso" setting_after_first_login_redirect_url_text_html: > Imposta un percorso per reindirizzare gli utenti dopo il loro primo accesso. Se vuoto, reindirizza alla home page per il tour di onboarding.Esempio:
/my/pagediff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index ef4bbbeadb5..dfb3b9dadd5 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -107,7 +107,7 @@ ja: jemalloc_allocator: Jemalloc メモリアロケータ journal_aggregation: explanation: - text: "ユーザーの個々のアクション(例えば、ワークパッケージを2回更新する)は、それらの年齢差が指定されたタイムスパン未満である場合、単一のアクションに集約されます。これらはアプリケーション内で1つのアクションとして表示されます。これはまた、送信されるメールの数を減らし、 %{webhook_link} の遅延にも影響します。" + text: "ユーザーの個々のアクション (例:ワークパッケージを2回更新する)は、指定された時間範囲よりも時間差が小さい場合、単一のアクションに集約されます。 これらはアプリケーション内で単一のアクションとして表示されます。 これにより、送信されるメールの数が減少し、 %{webhook_link} の遅延にも影響します。" link: "webhook" mcp_configurations: index: @@ -128,13 +128,13 @@ ja: success: "MCP configuration was updated successfully." scim_clients: authentication_methods: - sso: "IDプロバイダーからのJWT" - oauth2_client: "OAuth 2.0クライアント認証情報" + sso: "アイデンティティプロバイダからのJWT" + oauth2_client: "OAuth 2.0 クライアント資格情報" oauth2_token: "静的アクセストークン" created_client_credentials_dialog_component: - title: "クライアント認証情報の作成" - heading: "クライアント認証情報が生成されました" - one_time_hint: "クライアント・シークレットが表示されるのはこの時だけです。必ずコピーしてください。" + title: "クライアントの資格情報が作成されました" + heading: "クライアントの資格情報が生成されました" + one_time_hint: "クライアントのシークレットが表示される唯一の時間です。今すぐコピーしてください。" created_token_dialog_component: title: "トークンを作成しました" heading: "トークンが生成されました" @@ -147,21 +147,21 @@ ja: edit: label_delete_scim_client: "SCIM クライアントを削除" form: - auth_provider_description: "これは、SCIM プロバイダによって追加されたユーザが OpenProject で認証するために使用するサービスです。" - authentication_method_description_html: "これは SCIM クライアントが OpenProject で認証する方法です。OAuth トークンにscim_v2スコープが含まれていることを確認してください。" - description: "これらの設定オプションの詳細については、[SCIMクライアントの設定に関する文書](docs_url)を参照してください。" + auth_provider_description: "これは、SCIMプロバイダが追加したユーザーがOpenProjectでの認証に使用するサービスです。" + authentication_method_description_html: "これは SCIM クライアントが OpenProject で認証する方法です。OAuth トークンにscim_v2スコープが含まれていることを確認してください。" + description: "設定オプションの詳細については、[SCIM クライアントの設定に関するドキュメント](docs_url)を参照してください。" jwt_sub_description: "例えば、Keycloakの場合、これはSCIMクライアントに関連付けられたサービスアカウントのUUIDです。あなたのユースケースにあった Subject claim を見つける方法については [ドキュメント](docs_url) を参照してください。" - name_description: "このクライアントが設定された理由を他の管理者が理解しやすい名前を選んでください。" + name_description: "他の管理者がこのクライアントが設定された理由を理解するのに役立つ名前を選択してください。" index: - description: "ここで設定された SCIM クライアントは、OpenProject SCIM サーバ API と対話し、ユーザアカウントやグループのプロビジョニング、更新、デプロビジョニングを行うことができます。" - label_create_button: "SCIMクライアントの追加" + description: "ここで設定されたSCIMクライアントは、OpenProjectのSCIMサーバー APIと相互作用して、ユーザーアカウントとグループのプロビジョニング、更新、およびデプロビジョニングを行うことができます。" + label_create_button: "SCIMクライアントを追加" new: title: "新しいSCIMクライアント" revoke_static_token_dialog_component: confirm_button: "取り消す" - title: "静的トークンの失効" - heading: "このトークンを本当に取り消しますか?" - description: "このトークンを使っている SCIM クライアントは、OpenProject の SCIM サーバ API にアクセスできなくなります。" + title: "静的トークンを取り消す" + heading: "このトークンを取り消してもよろしいですか?" + description: "このトークンを使用する SCIM クライアントは、OpenProject の SCIM サーバ API にアクセスできなくなります。" table_component: blank_slate: title: "SCIMクライアントがまだ設定されていません" @@ -696,26 +696,26 @@ ja: other: "また、 %{shared_work_packages_link} はこのユーザーと共有されています。" remove_project_membership_or_work_package_shares_too: "直接のメンバーとしてのユーザーだけを削除したい(および共有を維持したい)、またはワークパッケージの共有も削除しますか?" will_remove_all_user_access_priveleges: "このメンバーを削除すると、プロジェクトへのユーザーのすべてのアクセス権が削除されます。ユーザーはまだサイトの一部として存在します。" - will_remove_all_group_access_priveleges: "このメンバを削除すると、プロジェクトに対するグループのすべてのアクセス権が削除されます。グループはサイトの一部としてまだ存在します。" - cannot_delete_inherited_membership: "このプロジェクトのメンバーであるグループに所属しているため、このメンバーを削除することはできません。" - cannot_delete_inherited_membership_note_admin_html: "%{administration_settings_link}で、プロジェクトのメンバーとしてグループを削除することも、特定のメンバーをグループから削除することもできます。" - cannot_delete_inherited_membership_note_non_admin: "プロジェクトのメンバーとしてグループを削除するか、管理者に連絡してこの特定のメンバーをグループから削除することができます。" + will_remove_all_group_access_priveleges: "このメンバーを削除すると、グループのすべてのアクセス権がプロジェクトに削除されます。グループはサイトの一部として存在します。" + cannot_delete_inherited_membership: "このメンバーはこのプロジェクトのメンバーであるグループに属しているため、削除できません。" + cannot_delete_inherited_membership_note_admin_html: "プロジェクトのメンバーとしてグループを削除するか、 %{administration_settings_link} のグループからこの特定のメンバーを削除することができます。" + cannot_delete_inherited_membership_note_non_admin: "プロジェクトのメンバーとしてグループを削除するか、管理者に問い合わせてグループから特定のメンバーを削除することができます。" delete_work_package_shares_dialog: - title: "ワーク・パッケージ・シェアの破棄" + title: "ワークパッケージの共有の取り消し" shared_with_this_user_html: other: "%{all_shared_work_packages_link} はこのユーザーと共有されています。" shared_with_this_group_html: other: "%{all_shared_work_packages_link} はこのグループと共有されています。" shared_with_permission_html: other: "%{shared_work_packages_link} のみが %{shared_role_name} 権限と共有されています。" - revoke_all_or_with_role: "すべての共有ワークパッケージ、または %{shared_role_name} 権限を持つワークパッケージのみへのアクセス権を剥奪しますか?" - will_not_affect_inherited_shares: "(これは、そのグループと共有しているワークパッケージには影響しません)。" - cannot_remove_inherited: "グループで共有されたワークパッケージの共有は削除できません。" - cannot_remove_inherited_with_role: "ロール %{shared_role_name} で共有されるワークパッケージは、グループを介して共有され、削除することはできません。" - cannot_remove_inherited_note_admin_html: "%{administration_settings_link}、グループへの共有を取り消すか、グループからこの特定のメンバーを削除することができます。" - cannot_remove_inherited_note_non_admin: "グループへの共有を取り消すか、管理者に連絡して特定のメンバーをグループから削除することができます。" - will_revoke_directly_granted_access: "このアクションは、グループと共有されているワークパッケージ以外の、すべてのワークパッケージへのアクセス権を剥奪する。" - will_revoke_access_to_all: "このアクションは、すべてのアクセス権を剥奪する。" + revoke_all_or_with_role: "共有されたワークパッケージ、または %{shared_role_name} 権限を持つワークパッケージのみへのアクセスを取り消しますか?" + will_not_affect_inherited_shares: "(これはグループと共有されているワークパッケージには影響しません)。" + cannot_remove_inherited: "グループ間で共有されるワークパッケージは削除できません。" + cannot_remove_inherited_with_role: "ワークパッケージとロール %{shared_role_name} が共有されているため、削除できません。" + cannot_remove_inherited_note_admin_html: "あなたは、グループへの共有を取り消すか、 %{administration_settings_link} のグループからこの特定のメンバーを削除することができます。" + cannot_remove_inherited_note_non_admin: "共有をグループに取り消すか、管理者に問い合わせてグループから特定のメンバーを削除することができます。" + will_revoke_directly_granted_access: "このアクションは、すべてのユーザーへのアクセスを取り消しますが、グループと共有されているワークパッケージです。" + will_revoke_access_to_all: "このアクションは、すべてのユーザーへのアクセスを取り消します。" my: access_token: dialog: @@ -739,7 +739,7 @@ ja: no_results_title_text: "現在、有効なアクセス トークンはありません。" notice_api_token_revoked: "APIトークンが削除されました。新しいトークンを作成するには、APIセクションの作成ボタンを使用してください。" notice_rss_token_revoked: "RSSトークンが削除されました。新しいトークンを作成するには、RSSセクションのリンクを使用してください。" - notice_ical_token_revoked: 'プロジェクト "%{project_name}" のカレンダー "%{calendar_name}" の iCalendar トークン "%{token_name}" が失効しました。このトークンを持つiCalendar URLは無効になりました。' + notice_ical_token_revoked: 'プロジェクト "%{token_name}" のカレンダー "%{calendar_name}" の iCalendar トークン "%{project_name}" が取り消されました。 このトークンのiCalendar URLは無効です。' password_confirmation_dialog: confirmation_required: "You need to enter your account password to confirm this change." title: "Confirm your password to continue" @@ -760,7 +760,7 @@ ja: matrix_check_uncheck_all_in_col_label_html: "Toggle all %{module} permissions for %{role} role" users: autologins: - prompt: "ログインしたまま %{num_days}" + prompt: "%{num_days} のログインを維持" sessions: session_name: "%{browser_name} %{browser_version} の %{os_name}" browser: "ブラウザ" @@ -774,17 +774,17 @@ ja: current: "Current (this device)" title: "セッション管理" instructions: "You are logged in to your account through the following devices. Revoke sessions that you do not recognise or from devices you do not control." - may_not_delete_current: "現在のセッションを削除することはできません。" + may_not_delete_current: "現在のセッションは削除できません。" deletion_warning: "Are you sure you want to revoke this session? You will be logged out on this device." groups: member_in_these_groups: "このユーザーは現在以下のグループのメンバーです:" no_results_title_text: このユーザーは現在どのグループのメンバーでもありません。 - summary_with_more: '%{names} と %{count_link}のメンバー。' - more: "%{count} もっと見る" - summary: '%{names}のメンバー。' + summary_with_more: '%{names} と %{count_link} のメンバー。' + more: "%{count} 以上" + summary: '%{names} のメンバー .' memberships: no_results_title_text: このユーザは現在プロジェクトのメンバーではありません。 - open_profile: "プロフィール" + open_profile: "プロファイルを開く" invite_user_modal: invite: "招待" title: @@ -841,7 +841,7 @@ ja: placeholder_users: right_to_manage_members_missing: > プレースホルダーユーザを削除する権限がありません。 プレースホルダー ユーザーがメンバーであるすべてのプロジェクトのメンバーを管理する権利はありません。 - delete_tooltip: "プレースホルダー・ユーザーの削除" + delete_tooltip: "プレースホルダー ユーザーを削除" deletion_info: heading: "プレースホルダー ユーザー %{name} を削除" data_consequences: > @@ -859,11 +859,11 @@ ja: reactions: action_title: "リアクト" add_reaction: "リアクションを追加" - react_with: "%{reaction} と リアクト" - and_user: "および %{user}" + react_with: "%{reaction} で反応する" + and_user: "と %{user}" and_others: other: と %{count} その他 - reaction_by: "%{reaction} によって" + reaction_by: "%{reaction} による" reportings: index: no_results_title_text: 現在、ステータス報告はありません。 @@ -874,19 +874,20 @@ ja: このステータスの色を割り当てたり変更する場合にクリックします。 ステータスボタンに表示され、テーブル内のワークパッケージを強調表示するために使用できます。 status_default_text: |- - 新しいワークパッケージは、デフォルトでこのタイプに設定される。読み取り専用にはできない。 + 新しいワークパッケージはデフォルトでこのタイプに設定されています。読み取り専用にすることはできません。 status_excluded_from_totals_text: |- - このステータスを持つワークパッケージを、階層内の「作業」、「 - 残作業」、「完了率」の合計から除外するには、このオプションをオンにします。 + このオプションをオンにすると、このステータスのワークパッケージを合計作業量、 + 残作業量、および階層構造で完了させることができます。 status_percent_complete_text: |- ステータスベースの進捗計算モードでは、このステータスが選択されると、作業 パッケージの「完了%」が自動的にこの値に設定される。 ワークベースモードでは無視される。 status_readonly_html: | - このステータスを持つワークパッケージを読み取り専用としてマークするには、このオプションをチェックする。 - ステータス以外の属性は変更できません。 + ワークパッケージを読み取り専用としてマークするには、このオプションをオンにしてください。 + ステータスを除いて変更することはできません。 +- 注意: 継承された値 (子やリレーションなど) は適用されます。 + メモ: 継承された値 (例えば、子や関連) が適用されます。 index: no_results_title_text: 現在、ワークパッケージのステータスはありません。 no_results_content_text: 新しいステータスを追加 @@ -896,7 +897,7 @@ ja: is_readonly: "読み取り専用" excluded_from_totals: "合計から除外" themes: - dark: "暗い" + dark: "ダーク" light: "ライト" sync_with_os: "自動(OSのテーマ設定に追従)" types: @@ -1014,21 +1015,20 @@ ja: could_not_be_saved: "次のワークパッケージを保存できませんでした:" none_could_be_saved: "%{total} ワークパッケージのどれも更新できませんでした。" x_out_of_y_could_be_saved: "%{failing} の %{total} ワークパッケージのうち、 %{success} を更新できませんでした。" - selected_because_descendants: "%{selected} のワークパッケージが選択されたが、合計 %{total} のワークパッケージが影響を受け、その中には子孫も含まれる。" - descendant: "選択された子孫" + selected_because_descendants: "%{selected} ワークパッケージが選択されている間、合計で %{total} ワークパッケージが子孫を含む影響を受けます。" + descendant: "選択された子孫です" move: no_common_statuses_exists: "選択されたすべてのワークパッケージに利用できるステータスはありません。 それらの状態は変更できません。" unsupported_for_multiple_projects: "複数のプロジェクトからのワークパッケージの一括移動 / コピーはサポートされていません" current_type_not_available_in_target_project: > - ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + ターゲット プロジェクトで現在のワークパッケージのタイプが有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 bulk_current_type_not_available_in_target_project: > - ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + 現在のタイプのワークパッケージはターゲット プロジェクトで有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 sharing: missing_workflow_warning: title: "ワークパッケージの共有のためのワークフローがありません" message: "「ワークパッケージエディタ」ロールに対してワークフローが設定されていません。ワークフローがなければ、ユーザーと共有されたワークパッケージのステータスは変更できません。 ワークフローをコピーすることができます。ソースタイプ(例:「タスク」)とソースロール(例:「メンバー」)を選択します。 次に、ターゲットタイプを選択します。最初に、すべてのタイプをターゲットとして選択できます。 最後に、「ワークパッケージの編集」ロールをターゲットとして選択し、「コピー」を押します。 このようにしてデフォルトを作成した後、他のすべてのロールに対して行うようにワークフローを微調整します。" link_message: "管理画面でワークフローを構成します。" - templated_subject_hint: '%{type}タイプで自動生成されます' summary: reports: category: @@ -1047,9 +1047,9 @@ ja: no_results_title_text: 現在、有効なバージョンはありません。 work_package_relations_tab: index: - action_bar_title: "他のワークパッケージとのリレーションを追加して、それらの間にリンクを作成する。" - no_results_title_text: 現在、利用可能な関係はない。 - blankslate_heading: "関係なし" + action_bar_title: "他のワークパッケージにリレーションを追加して、その間にリンクを作成します。" + no_results_title_text: 現在利用可能なリレーションはありません。 + blankslate_heading: "リレーションなし" blankslate_description: "このワークパッケージにはまだリレーションがありません。" label_add_child_button: "子要素" label_add_x: "%{x} を追加" @@ -1153,6 +1153,9 @@ ja: dependencies: "依存関係" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "までを表示" attachment: @@ -1527,6 +1530,7 @@ ja: not_available: "はシステム構成のため使用できません。" not_deletable: "削除できません。" not_current_user: "現在のユーザーではありません。" + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "見つかりません。" not_a_date: "は有効な日付ではありません。" not_a_datetime: "は有効な日時ではありません。" @@ -2069,6 +2073,7 @@ ja: role: "ロール" roles: "ロール" search: "検索" + sprint: "Sprint" start_date: "開始日" status: "ステータス" state: "状態" @@ -3169,7 +3174,6 @@ ja: label_duplicate: "重複" label_duplicates: "次と重複" label_edit: "編集" - label_edit_attribute: "Edit attribute" label_edit_x: "編集: %{x}" label_enable_multi_select: "複数選択の切り替え" label_enabled_project_custom_fields: "有効なカスタム フィールド" @@ -3923,6 +3927,7 @@ ja: notice_successful_delete: "正常に削除しました。" notice_successful_cancel: "キャンセルしました。" notice_successful_update: "正常に更新しました。" + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "作成に失敗しました" notice_unsuccessful_create_with_reason: "作成失敗: %{reason}" notice_unsuccessful_update: "更新に失敗しました。" @@ -4083,6 +4088,7 @@ ja: permission_edit_project_query: "プロジェクトのクエリを編集" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4267,6 +4273,9 @@ ja: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "最初のログインリダイレクト" setting_after_first_login_redirect_url_text_html: > 最初のログイン後にユーザーをリダイレクトするパスを設定します。空の場合は、オンボーディングツアーのホームページにリダイレクトします。
例:
/my/pagediff --git a/config/locales/crowdin/js-ca.yml b/config/locales/crowdin/js-ca.yml index b4773fd1995..1ad220d568b 100644 --- a/config/locales/crowdin/js-ca.yml +++ b/config/locales/crowdin/js-ca.yml @@ -104,7 +104,7 @@ ca: button_save: "Desa" button_settings: "Configuració" button_uncheck_all: "Desmarca-ho tot" - button_update: "Actualitzar" + button_update: "Actualitza" button_export-atom: "Descarregar Atom" button_generate_pdf: "Generate PDF" button_create: "Crear" diff --git a/config/locales/crowdin/js-de.yml b/config/locales/crowdin/js-de.yml index 902f1884686..d37cb74b060 100644 --- a/config/locales/crowdin/js-de.yml +++ b/config/locales/crowdin/js-de.yml @@ -138,7 +138,7 @@ de: description_available_columns: "Verfügbare Spalten" description_current_position: "Sie sind hier: " description_select_work_package: "Arbeitspaket #%{id} auswählen" - description_subwork_package: "Unteraufgabe von Arbeitspaket #%{id}" + description_subwork_package: "Kind von Arbeitspaket #%{id}" editor: revisions: "Lokale Änderungen anzeigen" no_revisions: "Keine lokalen Änderungen gefunden" @@ -455,7 +455,7 @@ de: label_total_progress: "%{percent}% Gesamtfortschritt" label_total_amount: "Gesamt: %{amount}" label_updated_on: "aktualisiert am" - label_value_derived_from_children: "(aggregierter Wert von Unteraufgaben)" + label_value_derived_from_children: "(aggregierter Wert von Kindelementen)" label_children_derived_duration: "Aggregierte Dauer der Unteraufgaben" label_warning: "Warnung" label_work_package: "Arbeitspaket" @@ -864,7 +864,7 @@ de: title: "Neues Arbeitspaket" header: "Neu: %{type}" header_no_type: "Neues Arbeitspaket (Typ noch nicht gesetzt)" - header_with_parent: "Neu: %{type} (Unteraufgabe von %{parent_type} #%{id})" + header_with_parent: "Neu: %{type} (Kind von %{parent_type} #%{id})" button: "Erstellen" duplicate: title: "Arbeitspaket duplizieren" @@ -1061,7 +1061,7 @@ de: single_text: "Sind Sie sicher, dass Sie das Arbeitspaket löschen möchten?" bulk_text: "Sind Sie sicher, dass Sie die folgenden %{label} löschen möchten?" has_children: "Dieses Arbeitspaket hat %{childUnits}:" - confirm_deletion_children: "Ich bestätige, dass alle Unteraufgaben der hier aufgeführten Arbeitspakete rekursiv entfernt werden." + confirm_deletion_children: "Ich bestätige, dass alle untergordneten Elemente der hier aufgeführten Arbeitspakete rekursiv entfernt werden." deletes_children: "Alle Unteraufgaben und deren Nachkommen werden auch rekursiv gelöscht." destroy_time_entry: title: "Löschen der Zeitbuchung bestätigen" diff --git a/config/locales/crowdin/js-ja.yml b/config/locales/crowdin/js-ja.yml index f4cba405f83..7121902ba95 100644 --- a/config/locales/crowdin/js-ja.yml +++ b/config/locales/crowdin/js-ja.yml @@ -32,12 +32,12 @@ ja: draggable_hint: | 埋め込み画像または添付ファイルをエディタにドラッグします。 ドラッグしつづけると閉じているエディタ領域が開きます。 - quarantined_hint: "ウイルスが発見されたように、ファイルは隔離されています。ダウンロードできません。" + quarantined_hint: "ウイルスが発見されたため,ファイルは隔離されています。ダウンロードできません。" autocomplete_ng_select: - add_tag: "アイテムを追加" + add_tag: "項目を追加" clear_all: "すべてクリア" loading: "読み込み中..." - not_found: "アイテムが見つかりません" + not_found: "見つかりませんでした" type_to_search: "検索キーワードを入力" autocomplete_select: placeholder: @@ -67,7 +67,7 @@ ja: button_back_to_list_view: "リスト表示に戻る" button_cancel: "キャンセル" button_close: "閉じる" - button_change_project: "別のプロジェクトに移動" + button_change_project: "他のプロジェクトに移る" button_check_all: "全てを選択" button_configure-form: "フォームを設定" button_confirm: "確認" @@ -75,7 +75,7 @@ ja: button_copy: "コピー" button_copy_to_clipboard: "クリップボードにコピー" button_copy_link_to_clipboard: "クリップボードにリンクをコピー" - button_copy_to_other_project: "別のプロジェクトで複製" + button_copy_to_other_project: "別のプロジェクトで複製する" button_custom-fields: "カスタムフィールド" button_delete: "削除" button_delete_watcher: "ウォッチャーを削除" @@ -97,7 +97,7 @@ ja: button_open_fullscreen: "全画面表示を開く" button_show_cards: "カードビュー表示" button_show_list: "リストビュー表示" - button_show_table: "テーブルビューを表示" + button_show_table: "テーブル表示" button_show_gantt: "ガントビューを表示" button_show_fullscreen: "全画面表示" button_more_actions: "その他の操作" @@ -107,7 +107,7 @@ ja: button_uncheck_all: "全てを選択解除" button_update: "更新" button_export-atom: "Atomをダウンロード" - button_generate_pdf: "PDFを生成" + button_generate_pdf: "PDF作成" button_create: "作成" card: add_new: "新規カード追加" @@ -141,8 +141,8 @@ ja: description_select_work_package: "ワークパッケージを選択 #%{id}" description_subwork_package: "ワークパッケージの子 #%{id}" editor: - revisions: "ローカルの変更を表示" - no_revisions: "ローカルの変更は見つかりませんでした" + revisions: "ローカルの修正を表示" + no_revisions: "ローカルでの修正は見つからず" preview: "プレビューモードの切り替え" source_code: "Markdown ソースモードの切り替え" error_saving_failed: "次のエラーで文書を保存するのに失敗しました: %{error}" @@ -155,7 +155,7 @@ ja: attribute_reference: macro_help_tooltip: "このテキストセグメントはマクロによって動的にレンダリングされています。" not_found: "要求されたリソースが見つかりませんでした" - nested_macro: "このマクロは %{model} %{id} を再帰的に参照しています。" + nested_macro: "このマクロは %{model} %{id}を再帰的に参照している。" invalid_attribute: "選択した属性 '%{name}' は存在しません。" child_pages: button: "子ページへのリンク" @@ -211,10 +211,10 @@ ja: calendar: empty_state_header: "休業日" empty_state_description: '休業日が定義されていません。「休業日を追加」ボタンをクリックして日付を追加してください。' - new_date: "(新規)" + new_date: "(新)" add_non_working_day: "休業日を追加" - already_added_error: "この日付の非作業日はすでに存在します。それぞれの日付に1つの非作業日が作成されます。" - change_button: "保存してスケジュールを変更" + already_added_error: "この日付の非営業日はすでに存在します。一意の日付に対して作成できる非営業日は1つだけです。" + change_button: "保存して再スケジュール" change_title: "営業日を変更する" removed_title: "以下の日を非稼働日リストから削除します:" change_description: "営業日とみなす曜日を変更すると、このサイト内のすべてのプロジェクトのすべてのワークパッケージの開始日と終了日に影響を与える可能性があります。" @@ -296,14 +296,14 @@ ja: ical_sharing_modal: title: "カレンダーを購読する" inital_setup_error_message: "データ取得中にエラーが発生しました。" - description: "URL(iCalendar)を使って外部クライアントでこのカレンダーを購読し、そこから最新のワークパッケージ情報を見ることができます。" - warning: "このURLを他のユーザーと共有しないでください。このリンクがあれば、誰でもアカウントやパスワードなしでワークパッケージの詳細を見ることができます。" - token_name_label: "どこで使うのですか?" + description: "URL(iCalendar)を使用して、外部クライアントでこのカレンダーを購読し、そこから最新のワークパッケージ情報を表示することができます。" + warning: "このURLを他のユーザーと共有しないでください。このリンクを持つ誰でもアカウントやパスワードなしでワークパッケージの詳細を表示することができます。" + token_name_label: "どこで使うのですか??" token_name_placeholder: '名前を入力してください。例:"電話"' token_name_description_text: 'If you subscribe to this calendar from multiple devices, this name will help you distinguish between them in your access tokens list.' copy_url_label: "URLをコピー" - ical_generation_error_text: "カレンダー URL の生成中にエラーが発生しました。" - success_message: 'URL "%{name}" は正常にクリップボードにコピーされました。サブスクリプションを完了するためにカレンダークライアントに貼り付けてください。' + ical_generation_error_text: "カレンダーのURL生成時にエラーが発生しました。" + success_message: 'URL "%{name}" がクリップボードにコピーされました。カレンダークライアントに貼り付けて購読を完了してください。' label_activate: "有効にする" label_assignee: "担当者" label_assignee_alt_text: "This work package is assigned to %{name}" @@ -316,7 +316,7 @@ ja: label_add_row_before: "前に行を追加" label_add_selected_columns: "選択した列を追加" label_added_by: "追加した人" - label_added_time_by: '%{author} が %{age} に追加しました' + label_added_time_by: '追加 %{author} %{age}' label_ago: "○日前" label_all: "全て" label_all_projects: "すべてのプロジェクト" @@ -429,7 +429,7 @@ ja: label_repository_plural: "リポジトリ" label_resize_project_menu: "Resize project menu" label_save_as: "名前をつけて保存" - label_search_columns: "列を検索" + label_search_columns: "列を検索する" label_select_watcher: "ウォッチャーを選択..." label_selected_filter_list: "選択されたフィルタ" label_show_attributes: "すべての属性を表示" @@ -467,8 +467,8 @@ ja: label_watch_work_package: "ワークパッケージをウォッチ" label_watcher_added_successfully: "ウォッチャーが正常に追加されました !" label_watcher_deleted_successfully: "ウォッチャーが正常に削除されました !" - label_work_package_details_you_are_here: "あなたは %{tab} %{type} %{subject} のタブにいます。" - label_work_package_context_menu: "ワークパッケージのコンテキスト メニュー" + label_work_package_details_you_are_here: "あなたは %{type} %{subject}の %{tab} タブを表示しています。" + label_work_package_context_menu: "ワークパッケージのコンテキストメニュー" label_unwatch: "ウォッチしない" label_unwatch_work_package: "ワークパッケージのウォッチを削除" label_uploaded_by: "アップロードした人" @@ -499,7 +499,7 @@ ja: label_version_plural: "バージョン" label_view_has_changed: "このビューには未保存の変更があります。 クリックすると保存します。" help_texts: - show_modal: "ヘルプテキストを表示" + show_modal: "ヘルプテキストを表示する" onboarding: buttons: skip: "スキップ" @@ -507,7 +507,7 @@ ja: got_it: "了承" steps: help_menu: "ヘルプ(?)メニューは、その他のヘルプリソースを提供します。ここでは、ユーザーガイド、役立つハウツービデオなどを見つけることができます。OpenProjectでの作業をお楽しみください!" - members: "新しい メンバー をプロジェクトに招待します。" + members: "新しいメンバーをプロジェクトに招待する。" quick_add_button: "ヘッダーナビゲーションにあるプラス(+)アイコンをクリックして、新規プロジェクトを作成したり、同僚を招待したりできます。" sidebar_arrow: "プロジェクトのメインメニューに戻るには、左上の矢印を使います。" welcome: "3分間のイントロダクションツアーで、最も重要な機能を学びましょう。
最後までステップを完了することをお勧めします。ツアーはいつでも再開できます。" @@ -614,33 +614,33 @@ ja: work_package_commented: "すべての新着コメント" work_package_created: "新しいワークパッケージ" work_package_processed: "すべてのステータス変更" - work_package_prioritized: "すべての優先度の変更" - work_package_scheduled: "すべての日付の変更" + work_package_prioritized: "すべての優先順位の変更" + work_package_scheduled: "すべての日付変更" global: immediately: title: "参加" - description: "自分が関与しているワークパッケージのすべてのアクティビティに関する通知(アサイニー、アカウンタブル、ウォッチャー)。" + description: "自分が関与しているワークパッケージのすべてのアクティビティに関する通知(担当、責任、ウォッチャー)。" delayed: title: "不参加" - description: "すべてのプロジェクトでのアクティビティの追加通知。" + description: "全プロジェクトにおける活動の追加通知。" date_alerts: title: "日付アラート" - description: "あなたが関与している(アサイニー、アカウンタブル、ウォッチャー)オープンワークパッケージの重要な日付が近づくと自動通知。" + description: "あなたが関与している(担当、責任、ウォッチャー)オープンワークパッケージの重要な日付が近づくと自動通知。" overdue: 期限を過ぎた場合 project_specific: title: "プロジェクト固有の通知設定" - description: "これらのプロジェクト固有の設定は、上記のデフォルト設定を上書きする。" + description: "これらのプロジェクト固有の設定は、上記のデフォルト設定を上書きします。" add: "プロジェクトの設定を追加する" - already_selected: "このプロジェクトは既に選択されています" + already_selected: "このプロジェクトはすでに選ばれている" remove: "プロジェクトの設定を削除する" pagination: no_other_page: "このページだけです。" - pages_skipped: "ページがスキップされました。" + pages_skipped: "ページスキップ。" page_navigation: "ページネーション・ナビゲーション" per_page_navigation: 'ページ毎のアイテム選択' pages: page_number: ページ %{number} - show_per_page: ページあたり %{number} を表示 + show_per_page: ページごとに %{number} placeholders: default: "-" subject: "ここにタイトルを入力します" @@ -650,7 +650,7 @@ ja: project: autocompleter: label: "プロジェクト名の入力補完" - click_to_switch_to_project: "プロジェクト: %{projectname}" + click_to_switch_to_project: "プロジェクト: %{projectname}" context: "プロジェクトのコンテキスト" not_available: "プロジェクトなし" required_outside_context: > @@ -658,30 +658,30 @@ ja: reminders: settings: daily: - add_time: "時間を追加" + add_time: "時間を追加する" enable: "毎日のEメールリマインダーを有効にする" explanation: "このリマインダーは、未読の通知に対してのみ、指定した時間帯にのみ届きます。 %{no_time_zone}" no_time_zone: "アカウントにタイムゾーンを設定するまでは、時間はUTCで解釈されます。" time_label: "時間 %{counter}:" - title: "未読の通知を毎日メールで通知する" + title: "未読通知メールのリマインダーを毎日送信する" workdays: title: "これらの日にリマインダーメールを受け取る" immediate: title: "電子メールのリマインダーを送信" mentioned: "@mentionするとすぐに" - personal_reminder: "個人的なリマインダーを受け取ったら直ちに" + personal_reminder: "個人的なリマインダーを受け取ったとき" alerts: title: "その他の項目(ワークパッケージではないもの)に対する電子メールアラート" explanation: > 本日の通知はワークパッケージに限定されています。これらのイベントが通知に含まれるようになるまで、Eメールアラートを受信し続けることを選択できます: news_added: "ニュースが追加されました。" news_commented: "ニュースへのコメント" - document_added: "追加された書類" + document_added: "ドキュメントの追加" forum_messages: "新しいフォーラムメッセージ" wiki_page_added: "Wikiページが追加されました。" wiki_page_updated: "Wikiページが更新されました。" - membership_added: "メンバーシップが追加されました" - membership_updated: "メンバーシップ更新" + membership_added: "メンバーシップの追加" + membership_updated: "メンバーシップの更新" title: "電子メールによるリマインダー" pause: label: "毎日のEメールリマインダーを一時停止する" @@ -1172,7 +1172,7 @@ ja: toggle_title: "ベースライン" clear: "クリア" apply: "適用" - header_description: "過去のいずれかの時点からこのリストに加えられた変更を強調する。" + header_description: "過去の選択した時点からこのリストに加えられた変更をハイライト" show_changes_since: "以降の変更を表示する" help_description: "ベースラインの基準タイムゾーン。" time_description: "現地時間: %{datetime}" diff --git a/config/locales/crowdin/js-no.yml b/config/locales/crowdin/js-no.yml index 37d78447d18..1594fc635ae 100644 --- a/config/locales/crowdin/js-no.yml +++ b/config/locales/crowdin/js-no.yml @@ -104,7 +104,7 @@ button_save: "Lagre" button_settings: "Innstillinger" button_uncheck_all: "Avmerk alle" - button_update: "Oppdater" + button_update: "Oppdatèr" button_export-atom: "Last ned Atom" button_generate_pdf: "Generate PDF" button_create: "Opprett" diff --git a/config/locales/crowdin/js-ro.yml b/config/locales/crowdin/js-ro.yml index 16b185d9890..7b76eb3d468 100644 --- a/config/locales/crowdin/js-ro.yml +++ b/config/locales/crowdin/js-ro.yml @@ -104,7 +104,7 @@ ro: button_save: "Salvează" button_settings: "Setări" button_uncheck_all: "Deselectează tot" - button_update: "Actualizează" + button_update: "Actualizare" button_export-atom: "Descarcă Atom" button_generate_pdf: "Generează PDF" button_create: "Creează" diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index 9a21927c4a5..66046d33eda 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -104,7 +104,7 @@ ru: button_save: "Сохранить" button_settings: "Настройки" button_uncheck_all: "Снять все отметки" - button_update: "Обновить" + button_update: "Обновление" button_export-atom: "Скачать Atom" button_generate_pdf: "Создать PDF" button_create: "Создать" diff --git a/config/locales/crowdin/js-vi.yml b/config/locales/crowdin/js-vi.yml index 8be53ce498e..fd2da032e6c 100644 --- a/config/locales/crowdin/js-vi.yml +++ b/config/locales/crowdin/js-vi.yml @@ -104,7 +104,7 @@ vi: button_save: "lưu lại" button_settings: "cài đặt" button_uncheck_all: "Bỏ chọn tất cả" - button_update: "cập nhật" + button_update: "Cập Nhật" button_export-atom: "Tải xuống nguyên tử" button_generate_pdf: "Tạo PDF" button_create: "Tạo mới" diff --git a/config/locales/crowdin/ka.yml b/config/locales/crowdin/ka.yml index c10de281663..b485914de81 100644 --- a/config/locales/crowdin/ka.yml +++ b/config/locales/crowdin/ka.yml @@ -1039,7 +1039,6 @@ ka: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ ka: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "ჩვენება სადამდე" attachment: @@ -1538,6 +1540,7 @@ ka: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ ka: role: "როლი" roles: "როლები" search: "ძებნა" + sprint: "Sprint" start_date: "დაწყების თარიღი" status: "სტატუსი" state: "State" @@ -3220,7 +3224,6 @@ ka: label_duplicate: "დუბლირება" label_duplicates: "დუბლიკატები" label_edit: "ჩასწორება" - label_edit_attribute: "Edit attribute" label_edit_x: "ჩასწორება: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ ka: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ ka: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ ka: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.
Example:
/my/pagediff --git a/config/locales/crowdin/kk.yml b/config/locales/crowdin/kk.yml index 7a227ae4b52..27b12e6e65f 100644 --- a/config/locales/crowdin/kk.yml +++ b/config/locales/crowdin/kk.yml @@ -1039,7 +1039,6 @@ kk: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ kk: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ kk: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ kk: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ kk: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ kk: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ kk: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ kk: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index bda826a411e..638a26e7480 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -1030,7 +1030,6 @@ ko: title: "작업 패키지 공유에 대한 워크플로 누락" message: "'작업 패키지 편집자' 역할에 대해 구성된 워크플로가 없습니다. 워크플로가 없으면 사용자와 공유된 워크플로는 작업 패키지의 상태를 변경할 수 없습니다. 워크플로를 복사할 수는 있습니다. 소스 유형(예: '작업')과 소스 역할(예: '멤버')을 선택하세요. 그런 다음 대상 유형을 선택하세요. 시작하기 위해 모든 유형을 대상으로 선택할 수 있습니다. 마지막으로 '작업 패키지 편집자' 역할을 대상으로 선택하고 '복사'를 누르세요. 기본값을 생성한 후 다른 모든 역할에 대해 수행하는 것처럼 워크플로를 미세 조정하세요." link_message: "관리에서 워크플로를 구성하세요." - templated_subject_hint: '%{type} 유형을 통해 자동으로 생성됨' summary: reports: category: @@ -1155,6 +1154,9 @@ ko: dependencies: "종속성" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "표시 기한" attachment: @@ -1529,6 +1531,7 @@ ko: not_available: "- 시스템 구성으로 인해 사용 가능하지 않습니다." not_deletable: "- 삭제할 수 없습니다." not_current_user: "은(는) 현재 유효한 사용자가 아닙니다." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "- 찾을 수 없습니다." not_a_date: "은(는) 유효한 날짜가 아닙니다." not_a_datetime: "은(는) 유효한 날짜가 아닙니다." @@ -2071,6 +2074,7 @@ ko: role: "역할" roles: "역할" search: "검색" + sprint: "Sprint" start_date: "시작 날짜" status: "상태" state: "상태" @@ -3171,7 +3175,6 @@ ko: label_duplicate: "중복" label_duplicates: "복제" label_edit: "편집" - label_edit_attribute: "특성 편집" label_edit_x: "편집: %{x}" label_enable_multi_select: "다중 선택 토글" label_enabled_project_custom_fields: "사용자 정의 필드 사용" @@ -3925,6 +3928,7 @@ ko: notice_successful_delete: "삭제에 성공했습니다." notice_successful_cancel: "취소에 성공했습니다." notice_successful_update: "업데이트에 성공했습니다." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "생성이 실패했습니다." notice_unsuccessful_create_with_reason: "생성 실패: %{reason}" notice_unsuccessful_update: "업데이트에 실패했습니다." @@ -4084,6 +4088,7 @@ ko: permission_edit_project_query: "프로젝트 쿼리 편집" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0개 포트폴리오" @@ -4266,6 +4271,9 @@ ko: setting_capture_external_links: "외부 링크 캡처" setting_capture_external_links_text: > 활성화된 경우, 서식이 지정된 텍스트의 모든 외부 링크는 애플리케이션을 종료하기 전에 경고 페이지를 통해 리디렉션됩니다. 따라서 잠재적인 악성 외부 웹사이트로부터 사용자를 보호할 수 있습니다. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "첫 번째 로그인 리디렉션" setting_after_first_login_redirect_url_text_html: > 첫 로그인 후 사용자를 리디렉션할 경로를 설정하세요. 비어 있으면 온보딩 투어의 홈페이지로 리디렉션됩니다.예:
/my/pagediff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index 9236f2757c1..aa5a87de83f 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -1054,7 +1054,6 @@ lt: title: "Darbo paketo dalinimuisi trūksta darbo proceso" message: "Vaidmeniui „Darbo paketo redaktorius“ nesukonfigūruotas procesas. Be proceso naudotojas negali pakeisti darbo paketo būsenos. Procesą galima nukopijuoti. Parinkite šaltinio tipą („pvz. „Užduotis“) ir šaltinio vaidmenį (pvz. „Narys“). Tada parinkite paskirties tipus. Pradžiai galėtumėte kaip paskirtį parinkti visus tipus. Galų gale parinkite vaidmenį „Darbo paketų redaktorius“ kaip paskirtį ir spauskite „Kopijuoti“. Taip sukūrę numatytąjį variantą, patikslinkite procesus kaip tai darote su kitais vaidmenimis." link_message: "Konfigūruokite procesus administravime." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1179,6 +1178,9 @@ lt: dependencies: "Priklausomybės" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Rodyti iki" attachment: @@ -1553,6 +1555,7 @@ lt: not_available: "yra nepasiekiamas dėl sistemos konfigūracijos" not_deletable: "negali būti pašalintas." not_current_user: "nėra dabartinis naudotojas" + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "nėra tinkama data." not_a_datetime: "nėra tinkama data ir laikas." @@ -2152,6 +2155,7 @@ lt: role: "Vaidmuo" roles: "Vaidmenys" search: "Paieška" + sprint: "Sprint" start_date: "Pradžios data" status: "Būsena" state: "State" @@ -3315,7 +3319,6 @@ lt: label_duplicate: "dubliuoti" label_duplicates: "dubliuojasi" label_edit: "Redaguoti" - label_edit_attribute: "Edit attribute" label_edit_x: "Redaguoti: %{x}" label_enable_multi_select: "Perjungti daugybinį pažymėjimą" label_enabled_project_custom_fields: "Įgalinti papildomi laukai" @@ -4072,6 +4075,7 @@ lt: notice_successful_delete: "Sėkmingas panaikinimas." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Sėkmingai atnaujinta." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4234,6 +4238,7 @@ lt: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4416,6 +4421,9 @@ lt: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/lv.yml b/config/locales/crowdin/lv.yml index 860bbafffe8..42d4ad231c2 100644 --- a/config/locales/crowdin/lv.yml +++ b/config/locales/crowdin/lv.yml @@ -1048,7 +1048,6 @@ lv: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1173,6 +1172,9 @@ lv: dependencies: "Saistītie projekti" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1547,6 +1549,7 @@ lv: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2127,6 +2130,7 @@ lv: role: "Loma" roles: "Lomas" search: "Meklēšana" + sprint: "Sprint" start_date: "Sākuma datums" status: "Statuss" state: "State" @@ -3269,7 +3273,6 @@ lv: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Labot" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Iespējotie pielāgotie lauki" @@ -4025,6 +4028,7 @@ lv: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4187,6 +4191,7 @@ lv: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4371,6 +4376,9 @@ lv: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/mn.yml b/config/locales/crowdin/mn.yml index 298ec1cb4d8..a2759ab42a5 100644 --- a/config/locales/crowdin/mn.yml +++ b/config/locales/crowdin/mn.yml @@ -1039,7 +1039,6 @@ mn: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ mn: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ mn: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ mn: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Төлөв" state: "State" @@ -3220,7 +3224,6 @@ mn: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ mn: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ mn: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ mn: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ms.yml b/config/locales/crowdin/ms.yml index e86291f6fb2..8675e77b1e2 100644 --- a/config/locales/crowdin/ms.yml +++ b/config/locales/crowdin/ms.yml @@ -1028,7 +1028,6 @@ ms: title: "Aliran kerja hilang untuk perkongsian pakej kerja" message: "Tiada aliran kerja yang dikonfigurasi untuk peranan 'Pengedit Pakej Kerja'. Tanpa aliran kerja, perkongsian dengan pengguna tidak boleh mengubah status pakej kerja tersebut. Aliran kerja boleh disalin. Pilih jenis sumber (cth. 'Tugasan') dan peranan sumber (cth. 'Ahli'). Kemudian pilih jenis sasaran. Sebagai permulaan, anda boleh pilih semua jenis sebagai sasaran. Akhirnya, pilih peranan 'Pengedit Pakej Kerja' sebagai sasaran dan tekan 'Salin'. Setelah mencipta default ini, selaraskan semula aliran kerja sebagaimana yang anda lakukan untuk setiap peranan yang lain." link_message: "Konfigurasi aliran kerja dalam pentadbiran." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1153,6 +1152,9 @@ ms: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Paparkan sehingga" attachment: @@ -1527,6 +1529,7 @@ ms: not_available: "adalah tidak tersedia kerana konfigurasi sistem." not_deletable: "tidak dapat dipadamkan." not_current_user: "adalah bukan pengguna semasa." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "tidak dijumpai." not_a_date: "bukan tarikh yang sah." not_a_datetime: "bukan tarikh masa yang sah." @@ -2069,6 +2072,7 @@ ms: role: "Peranan" roles: "Peranan\n" search: "Cari" + sprint: "Sprint" start_date: "Tarikh mula" status: "Status" state: "State" @@ -3169,7 +3173,6 @@ ms: label_duplicate: "duplikasi" label_duplicates: "pendua" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Tukar pilihan berganda" label_enabled_project_custom_fields: "Ruang tersuai yang diaktifkan" @@ -3923,6 +3926,7 @@ ms: notice_successful_delete: "Pemadaman yang berjaya." notice_successful_cancel: "Pembatalan yang berjaya." notice_successful_update: "Kemas kini berjaya." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4082,6 +4086,7 @@ ms: permission_edit_project_query: "Edit pertanyaan projek" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4266,6 +4271,9 @@ ms: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ne.yml b/config/locales/crowdin/ne.yml index 20b64478728..f26b67caf71 100644 --- a/config/locales/crowdin/ne.yml +++ b/config/locales/crowdin/ne.yml @@ -1039,7 +1039,6 @@ ne: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ ne: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ ne: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ ne: role: "Role" roles: "भूमिकाहरु" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ ne: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ ne: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ ne: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ ne: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index 99e6340144e..7c912c974e3 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -1035,7 +1035,6 @@ nl: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configureer de workflows in de administratie." - templated_subject_hint: Automatisch gegenereerd via type %{type} summary: reports: category: @@ -1160,6 +1159,9 @@ nl: dependencies: "Afhankelijkheden" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Toon tot" attachment: @@ -1534,6 +1536,7 @@ nl: not_available: "is niet beschikbaar vanwege een systeemconfiguratie." not_deletable: "kan niet worden verwijderd." not_current_user: "is niet de huidige gebruiker." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "niet gevonden." not_a_date: "is geen geldige datum." not_a_datetime: "is geen geldige datum tijd." @@ -2095,6 +2098,7 @@ nl: role: "Rol" roles: "Rollen" search: "Zoeken" + sprint: "Sprint" start_date: "Startdatum" status: "Status" state: "State" @@ -3216,7 +3220,6 @@ nl: label_duplicate: "dupliceren" label_duplicates: "duplicaten" label_edit: "Wijzig" - label_edit_attribute: "Edit attribute" label_edit_x: "Bewerken: %{x}" label_enable_multi_select: "Omschakelen multiselect" label_enabled_project_custom_fields: "Ingeschakelde aangepaste velden" @@ -3970,6 +3973,7 @@ nl: notice_successful_delete: "Verwijdering geslaagd." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Succesvolle wijziging." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4131,6 +4135,7 @@ nl: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4315,6 +4320,9 @@ nl: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index 36286ed662a..30636f8c55c 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -1038,7 +1038,6 @@ title: "Arbeidsflyt mangler for deling av arbeidspakker" message: "Ingen arbeidsflyt er konfigurert for rollen 'Arbeidspakke redaktør'. Uten en arbeidsflyt kan ikke brukeren endre status på arbeidspakken. Arbeidsflyt kan kopieres. Velg en kildetype (f.eks. 'oppgave') og en kilderolle (f.eks. 'medlem'). Velg så måltyper. Til å begynne med, kan du velge alle typene som mål. Til slutt velger du \"Redigeringsprogrammet for arbeidspakke\" som mål og press \"Copy\". Etter å ha opprettet standardinnstillingene, kan du finjustere arbeidsflytene som du gjør for hver annen rolle." link_message: "Konfigurer arbeidsmetodene i administrasjonen." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1163,6 +1162,9 @@ dependencies: "Avhengigheter" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Vise til" attachment: @@ -1537,6 +1539,7 @@ not_available: "er ikke tilgjengelig på grunn av en systemkonfigurasjon." not_deletable: "kan ikke slettes." not_current_user: "er ikke gjeldende bruker." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "er ikke en gyldig dato." not_a_datetime: "er ikke et gyldig tidspunkt for datoen." @@ -2098,6 +2101,7 @@ role: "Rolle" roles: "Rolle" search: "Søk" + sprint: "Sprint" start_date: "Startdato" status: "Status" state: "State" @@ -3219,7 +3223,6 @@ label_duplicate: "duplikat" label_duplicates: "duplikater" label_edit: "Rediger" - label_edit_attribute: "Edit attribute" label_edit_x: "Rediger: %{x}" label_enable_multi_select: "Veksle multivalg" label_enabled_project_custom_fields: "Aktiverte egendefinerte felt" @@ -3974,6 +3977,7 @@ notice_successful_delete: "Slettingen var vellykket." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Oppdateringen var vellykket." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4135,6 +4139,7 @@ permission_edit_project_query: "Rediger prosjektspørring" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4319,6 +4324,9 @@ setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index bcc1628ff4a..ebe94ed4c95 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -1054,7 +1054,6 @@ pl: title: "Brak przepływu pracy udostępniania pakietu roboczego" message: "Dla roli „Edytor pakietów roboczych” nie skonfigurowano przepływu pracy. Bez przepływu pracy użytkownik, któremu ją udostępniono nie może zmienić statusu pakietu roboczego. Przepływy pracy można kopiować. Wybierz typ źródłowy (np. „Zadanie”) i rolę źródłową (np. „Członek”). Następnie wybierz typy docelowe. Na początek możesz wybrać wszystkie typy jako docelowe. Na koniec wybierz rolę „Edytor pakietów roboczych” jako cel i naciśnij przycisk „Kopiuj”. Po utworzeniu ustawień domyślnych dostosuj przepływy pracy tak, jak w przypadku każdej innej roli." link_message: "Skonfiguruj przepływy pracy w administracji." - templated_subject_hint: Automatycznie wygenerowany przez typ %{type} summary: reports: category: @@ -1178,6 +1177,9 @@ pl: dependencies: "Zależności" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Wyświetlaj do" attachment: @@ -1552,6 +1554,7 @@ pl: not_available: "jest niedostępne z powodu konfiguracji systemu." not_deletable: "— nie można usunąć." not_current_user: "nie jest bieżącym użytkownikiem." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "nie znaleziono." not_a_date: "nie jest poprawną datą." not_a_datetime: "nie jest poprawną datą i czasem." @@ -2151,6 +2154,7 @@ pl: role: "Rola" roles: "Role" search: "Wyszukaj" + sprint: "Sprint" start_date: "Data rozpoczęcia" status: "Status" state: "Stan" @@ -3314,7 +3318,6 @@ pl: label_duplicate: "duplikat" label_duplicates: "Duplikaty" label_edit: "Edytuj" - label_edit_attribute: "Edytuj atrybut" label_edit_x: "Edytuj: %{x}" label_enable_multi_select: "Włącz wybór wielokrotny" label_enabled_project_custom_fields: "Aktywne pola niestandardowe" @@ -4070,6 +4073,7 @@ pl: notice_successful_delete: "Usuwanie zakończone sukcesem." notice_successful_cancel: "Anulowanie powiodło się." notice_successful_update: "Aktualizacja zakończona sukcesem." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Utworzenie nie powiodło się." notice_unsuccessful_create_with_reason: "Utworzenie nie powiodło się: %{reason}" notice_unsuccessful_update: "Aktualizacja nie powiodła się." @@ -4233,6 +4237,7 @@ pl: permission_edit_project_query: "Edytuj zapytanie dotyczące projektu" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfoliów" @@ -4415,6 +4420,9 @@ pl: setting_capture_external_links: "Przechwyć linki zewnętrzne" setting_capture_external_links_text: > Po włączeniu tej funkcji wszystkie linki zewnętrzne w tekście formatowanym będą przekierowywać na stronę z ostrzeżeniem przed opuszczeniem aplikacji. Pomaga to chronić użytkowników przed potencjalnie złośliwymi zewnętrznymi witrynami internetowymi. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Przekierowanie pierwszego logowania" setting_after_first_login_redirect_url_text_html: > Ustaw ścieżkę przekierowania użytkowników po ich pierwszym zalogowaniu. Jeśli jest pusta, przekierowuje do strony głównej wycieczki wdrożeniowej.Przykład:
/my/pagediff --git a/config/locales/crowdin/pt-BR.yml b/config/locales/crowdin/pt-BR.yml index 2b540b5e571..341d806f82e 100644 --- a/config/locales/crowdin/pt-BR.yml +++ b/config/locales/crowdin/pt-BR.yml @@ -1036,7 +1036,6 @@ pt-BR: title: "Fluxo de trabalho ausente para compartilhamento de pacotes de trabalho" message: "Nenhum fluxo de trabalho está configurado para a função \"Editor de pacote de trabalho\". Sem um fluxo de trabalho, o usuário compartilhado não pode alterar o status do pacote de trabalho. Os fluxos de trabalho podem ser copiados. Selecione um tipo de origem (ex.: \"Tarefa\") e uma função de origem (ex.: \"Membro\"). Em seguida, selecione os tipos de destino. Para começar, você pode selecionar todos os tipos como alvos. Por fim, selecione a função \"Editor de pacote de trabalho\" como o destino e pressione \"Copiar\". Depois de criar os padrões, ajuste os fluxos de trabalho da mesma forma que faz para todas as outras funções." link_message: "Configure os fluxos de trabalho na administração." - templated_subject_hint: Gerado automaticamente através do tipo %{type} summary: reports: category: @@ -1161,6 +1160,9 @@ pt-BR: dependencies: "Dependências" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Exibir até" attachment: @@ -1535,6 +1537,7 @@ pt-BR: not_available: "não está disponível devido a uma configuração do sistema." not_deletable: "não pode ser excluído." not_current_user: "não é o usuário atual." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "não encontrado." not_a_date: "não é uma data válida." not_a_datetime: "não é uma data/hora válida." @@ -2096,6 +2099,7 @@ pt-BR: role: "Função" roles: "Papéis" search: "Pesquisar" + sprint: "Sprint" start_date: "Data de início" status: "Status" state: "Estado" @@ -3217,7 +3221,6 @@ pt-BR: label_duplicate: "duplicado" label_duplicates: "Duplicados" label_edit: "Editar" - label_edit_attribute: "Editar atributo" label_edit_x: "Editar: %{x}" label_enable_multi_select: "Alterna para seleção múltipla" label_enabled_project_custom_fields: "Campos personalizados habilitados" @@ -3971,6 +3974,7 @@ pt-BR: notice_successful_delete: "Exclusão bem sucedida." notice_successful_cancel: "Cancelamento bem-sucedido." notice_successful_update: "Atualizado com sucesso." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Falha na criação." notice_unsuccessful_create_with_reason: "Falha na criação: %{reason}" notice_unsuccessful_update: "Falha na atualização." @@ -4132,6 +4136,7 @@ pt-BR: permission_edit_project_query: "Editar consulta do projeto" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfólios" @@ -4316,6 +4321,9 @@ pt-BR: setting_capture_external_links: "Capturar links externos" setting_capture_external_links_text: > Quando ativado, todos os links externos em textos formatados serão redirecionados por uma página de aviso antes de sair do aplicativo. Isso ajuda a proteger os usuários de sites externos potencialmente maliciosos. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Redirecionamento de primeiro login" setting_after_first_login_redirect_url_text_html: > Defina um caminho para redirecionar os usuários após o primeiro login. Se deixado em branco, eles serão redirecionados para a página inicial do tour de introdução.Exemplo:
/my/pagediff --git a/config/locales/crowdin/pt-PT.yml b/config/locales/crowdin/pt-PT.yml index 252e35cb532..b301c268bf9 100644 --- a/config/locales/crowdin/pt-PT.yml +++ b/config/locales/crowdin/pt-PT.yml @@ -1036,7 +1036,6 @@ pt-PT: title: "Falta um fluxo de trabalho para a partilha de pacotes de trabalho" message: "Nenhum fluxo de trabalho está configurado para a função \"Editor do pacote de trabalho\". Sem um fluxo de trabalho, o utilizador não pode alterar o estado do pacote de trabalho. Os fluxos de trabalho podem ser copiados. Selecione um tipo de fonte (por exemplo, \"Tarefa\") e uma função da fonte (por exemplo, \"Membro\"). Em seguida, selecione os tipos de destino. Para começar, pode selecionar todos os tipos como alvos. Por fim, selecione a função \"Editor do pacote de trabalho\" como destino e prima \"Copiar\". Depois de ter criado as predefinições, ajuste os fluxos de trabalho como faz para todas as outras funções." link_message: "Configure os fluxos de trabalho na administração." - templated_subject_hint: Gerado automaticamente através do tipo %{type} summary: reports: category: @@ -1161,6 +1160,9 @@ pt-PT: dependencies: "Dependências" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Exibir até" attachment: @@ -1535,6 +1537,7 @@ pt-PT: not_available: "não está disponível devido a uma configuração do sistema." not_deletable: "não pode ser eliminado" not_current_user: "não é o utilizador atual." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "não encontrado." not_a_date: "não é uma data válida." not_a_datetime: "não é uma data/hora válida." @@ -2096,6 +2099,7 @@ pt-PT: role: "Função" roles: "Papel" search: "Pesquisar" + sprint: "Sprint" start_date: "Data de início" status: "Situação" state: "Estado" @@ -3217,7 +3221,6 @@ pt-PT: label_duplicate: "duplicado" label_duplicates: "duplicados" label_edit: "Editar" - label_edit_attribute: "Editar atributo" label_edit_x: "Editar: %{x}" label_enable_multi_select: "Alternar para selecção múltipla" label_enabled_project_custom_fields: "Campos personalizados activados" @@ -3971,6 +3974,7 @@ pt-PT: notice_successful_delete: "Eliminado com sucesso." notice_successful_cancel: "Cancelamento bem-sucedido." notice_successful_update: "Actualizado com sucesso." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "A criação falhou." notice_unsuccessful_create_with_reason: "A criação falhou: %{reason}" notice_unsuccessful_update: "Falha na atualização." @@ -4132,6 +4136,7 @@ pt-PT: permission_edit_project_query: "Editar consulta de projeto" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 carteiras" @@ -4314,6 +4319,9 @@ pt-PT: setting_capture_external_links: "Capturar links externos" setting_capture_external_links_text: > Quando ativada, todos os links externos no texto formatado serão redirecionados através de uma página de aviso antes de sair da aplicação. Isto ajuda a proteger os utilizadores de sites externos potencialmente maliciosos. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Redirecionamento do primeiro início de sessão" setting_after_first_login_redirect_url_text_html: > Defina um caminho para redirecionar os utilizadores após o primeiro início de sessão. Se estiver vazio, redireciona para a página inicial do tour de integração.Exemplo:
/my/pagediff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index fea351aaa2e..66ad70a6d1c 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -1048,7 +1048,6 @@ ro: title: "Lipsește fluxul pentru partajarea pachetului de lucru" message: "Niciun flux de lucru nu este configurat pentru rolul 'Editor pachete de lucru'. Fără un flux de lucru, utilizatorul nu poate modifica statusul pachetului de lucru. Fluxurile de lucru pot fi copiate. Selectează un tip sursă (de ex. 'Task') şi rolul sursă (de ex. 'Membru'). Apoi selectează tipurile țintă. Pentru a începe, ai putea selecta toate tipurile ca ținte. După aceea, selectează rolul de „editor pachete de lucru” ca țintă și apăsă „Copiază”. După ce ai creat cele implicite, reglează fluxurile de lucru așa cum faci pentru orice alt rol." link_message: "Configurează fluxurile de lucru din administrare." - templated_subject_hint: Generat automat prin tipul %{type} summary: reports: category: @@ -1173,6 +1172,9 @@ ro: dependencies: "Dependenţe" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Afişare până la" attachment: @@ -1547,6 +1549,7 @@ ro: not_available: "nu este disponibil din cauza unei configurații a sistemului." not_deletable: "%s nu poate fi șters." not_current_user: "nu este utilizatorul curent." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "nu a fost găsit." not_a_date: "Acest câmp trebuie să conțină o dată validă." not_a_datetime: "nu este o dată-ora validă." @@ -2127,6 +2130,7 @@ ro: role: "Rol" roles: "Roluri" search: "Caută" + sprint: "Sprint" start_date: "Dată început" status: "Stare" state: "State" @@ -3268,8 +3272,7 @@ ro: label_duplicated_by: "dublat de" label_duplicate: "duplicat" label_duplicates: "dublează" - label_edit: "Editează" - label_edit_attribute: "Edit attribute" + label_edit: "Editare" label_edit_x: "Editare: %{x}" label_enable_multi_select: "Comutare selecție multiplă" label_enabled_project_custom_fields: "Câmpuri personalizate activate" @@ -3326,7 +3329,7 @@ ro: label_global_roles: "Roluri globale" label_git_path: "Calea catre directorul .git" label_greater_or_equal: ">=" - label_group_by: "Grupează după" + label_group_by: "Grupare după" label_group_new: "Grupare nouă" label_group: "Grup" label_group_named: "Grup %{name}" @@ -4024,6 +4027,7 @@ ro: notice_successful_delete: "Ştergere reuşită." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Actualizare reușită." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4186,6 +4190,7 @@ ro: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4370,6 +4375,9 @@ ro: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 0efcddfdd11..989fd3a6dcd 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -1055,7 +1055,6 @@ ru: title: "Отсутствует рабочий процесс для совместного использования пакета работ" message: "Рабочий процесс не настроен для роли 'Редактора пакетов работ'. Без рабочего процесса, общий с пользователем не может изменить статус рабочего пакета. Рабочие процессы могут быть скопированы. Выберите исходный тип (например, «Задача») и исходную роль (например, «Участник»). Затем выберите нужные типы. Чтобы начать, можно выбрать все типы в качестве целей. Наконец, выберите роль 'Редактор пакетов работ' в качестве цели и нажмите 'Копировать'. После создания таким образом по умолчанию, тонкая настройка рабочих процессов как вы делаете для каждой другой роли." link_message: "Настройка рабочих процессов в администрации." - templated_subject_hint: Автоматически сгенерировано по типу %{type} summary: reports: category: @@ -1180,6 +1179,9 @@ ru: dependencies: "Связи" activerecord: attributes: + agile/sprint: + sharing: "Совместное использование" + finish_date: "Дата окончания" announcements: show_until: "Отобразить до" attachment: @@ -1554,6 +1556,7 @@ ru: not_available: "недоступно из-за конфигурации системы." not_deletable: "не может быть удален." not_current_user: "не является текущим пользователем." + only_one_active_sprint_allowed: "для каждого проекта допускается только один активный спринт." not_found: "не найдено." not_a_date: "не является допустимой датой." not_a_datetime: "дата и время не являются допустимыми." @@ -2153,6 +2156,7 @@ ru: role: "Роль" roles: "Роли" search: "Поиск" + sprint: "Спринт" start_date: "Дата начала" status: "Статус" state: "Область" @@ -3316,7 +3320,6 @@ ru: label_duplicate: "дублировать" label_duplicates: "Дублирует" label_edit: "Правка" - label_edit_attribute: "Изменить атрибут" label_edit_x: "Правка: %{x}" label_enable_multi_select: "Разрешен множественный выбор" label_enabled_project_custom_fields: "Доступные настраиваемые поля" @@ -4072,6 +4075,7 @@ ru: notice_successful_delete: "Удаление выполнено." notice_successful_cancel: "Отмена прошла успешно." notice_successful_update: "Обновление выполнено." + notice_successful_move: "Успешное перемещение из %{from} в %{to}." notice_unsuccessful_create: "Создание не удалось." notice_unsuccessful_create_with_reason: "Создание не удалось: %{reason}" notice_unsuccessful_update: "Ошибка обновления." @@ -4235,6 +4239,7 @@ ru: permission_edit_project_query: "Редактирование запроса проекта" placeholders: default: "-" + templated_hint: Автоматически сгенерировано через тип %{type} portfolio: count: zero: "0 портфолио" @@ -4417,6 +4422,9 @@ ru: setting_capture_external_links: "Перехват внешних ссылок" setting_capture_external_links_text: > Если эта функция включена, все внешние ссылки в форматированном тексте будут перенаправляться через страницу предупреждения, прежде чем покинуть приложение. Это помогает защитить пользователей от потенциально вредоносных внешних сайтов. + setting_capture_external_links_require_login: "Требуйте, чтобы пользователи входили в систему" + setting_capture_external_links_require_login_text: > + Когда эта функция включена, пользователи, желающие перейти по внешним ссылкам, должны войти в систему, прежде чем смогут продолжить. setting_after_first_login_redirect_url: "Перенаправление первого входа" setting_after_first_login_redirect_url_text_html: > Задайте путь для перенаправления пользователей после их первого входа в систему. Если не задано, то пользователь будет перенаправлен на домашнюю страницу обзорного тура.Например:
/my/pagediff --git a/config/locales/crowdin/rw.yml b/config/locales/crowdin/rw.yml index cd01ac68b62..a60cd8cc473 100644 --- a/config/locales/crowdin/rw.yml +++ b/config/locales/crowdin/rw.yml @@ -1039,7 +1039,6 @@ rw: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ rw: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ rw: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ rw: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ rw: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ rw: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ rw: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ rw: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/si.yml b/config/locales/crowdin/si.yml index 3302e32e485..47967f8a039 100644 --- a/config/locales/crowdin/si.yml +++ b/config/locales/crowdin/si.yml @@ -1039,7 +1039,6 @@ si: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ si: dependencies: "පරායත්තතා" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "ප්රදර්ශනය වන තුරු" attachment: @@ -1538,6 +1540,7 @@ si: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "වලංගු දිනයක් නොවේ." not_a_datetime: "වලංගු දිනය කාලය නොවේ." @@ -2099,6 +2102,7 @@ si: role: "කාර්යභාරය" roles: "භූමිකාවන්" search: "සොයන්න" + sprint: "Sprint" start_date: "ආරම්භක දිනය" status: "තත්වය" state: "State" @@ -3220,7 +3224,6 @@ si: label_duplicate: "අනුපිටපත්" label_duplicates: "අනුපිටපත්" label_edit: "සංස්කරණය කරන්න" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "බහු ටොගල් කරන්න" label_enabled_project_custom_fields: "සක්රීය අභිරුචි ක්ෂේත්ර" @@ -3975,6 +3978,7 @@ si: notice_successful_delete: "සාර්ථක මකාදැමීම." notice_successful_cancel: "Successful cancellation." notice_successful_update: "සාර්ථක යාවත්කාලීන කිරීම." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ si: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ si: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index d4b3dbed4df..1fdd0d87d0a 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -1057,7 +1057,6 @@ sk: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1182,6 +1181,9 @@ sk: dependencies: "Závislosti" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Zobrazovať, až kým" attachment: @@ -1556,6 +1558,7 @@ sk: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "nie je platný dátum." not_a_datetime: "nie je platný dátum a čas." @@ -2155,6 +2158,7 @@ sk: role: "Rola" roles: "Roly" search: "Vyhľadávanie" + sprint: "Sprint" start_date: "Dátum začiatku" status: "Stav" state: "State" @@ -3318,7 +3322,6 @@ sk: label_duplicate: "skopírovať" label_duplicates: "duplikáty" label_edit: "Upraviť" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Prepnúť multiselect" label_enabled_project_custom_fields: "Povolené vlastné polia" @@ -4074,6 +4077,7 @@ sk: notice_successful_delete: "Úspešne zmazané." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Úspešne aktualizované." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4237,6 +4241,7 @@ sk: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4421,6 +4426,9 @@ sk: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index 8c7bfb09100..c1d9f712d15 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -1056,7 +1056,6 @@ sl: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1181,6 +1180,9 @@ sl: dependencies: "Odvisnosti" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Prikaži do" attachment: @@ -1555,6 +1557,7 @@ sl: not_available: "is not available due to a system configuration." not_deletable: "se ne da izbrisati." not_current_user: "ni trenutni uporabnik." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "ni veljaven datum" not_a_datetime: "ni veljaven datum." @@ -2154,6 +2157,7 @@ sl: role: "Vloga" roles: "Vloge" search: "Išči" + sprint: "Sprint" start_date: "Datum začetka" status: "Stanje" state: "State" @@ -2408,8 +2412,8 @@ sl: - "avgust" - "september" - "oktober" - - "november" - - "december" + - "November" + - "December" order: - :leto - :mesec @@ -3317,7 +3321,6 @@ sl: label_duplicate: "Podvoji" label_duplicates: "dvojniki" label_edit: "Uredi" - label_edit_attribute: "Edit attribute" label_edit_x: "Uredi: %{x}" label_enable_multi_select: "Preklopite na več selekcijo" label_enabled_project_custom_fields: "Omogočena polja po meri" @@ -4074,6 +4077,7 @@ sl: notice_successful_delete: "Uspešen izbris." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Posodobitev uspela." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4237,6 +4241,7 @@ sl: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4420,6 +4425,9 @@ sl: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/sr.yml b/config/locales/crowdin/sr.yml index 3718a768962..82cf3bb349c 100644 --- a/config/locales/crowdin/sr.yml +++ b/config/locales/crowdin/sr.yml @@ -1048,7 +1048,6 @@ sr: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1173,6 +1172,9 @@ sr: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1547,6 +1549,7 @@ sr: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2127,6 +2130,7 @@ sr: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3269,7 +3273,6 @@ sr: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -4025,6 +4028,7 @@ sr: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4187,6 +4191,7 @@ sr: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4371,6 +4376,9 @@ sr: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index 4750ddb4ee9..6be9ad903ac 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -1039,7 +1039,6 @@ sv: title: "Arbetsflöde saknas för delning av arbetspaket" message: "Inget arbetsflöde är konfigurerat för 'Work package editor'-rollen. Utan ett arbetsflöde kan den delade användaren inte ändra arbetspaketets status. Arbetsflöden kan kopieras. Välj en källtyp (t.ex. 'Task') och källroll (t.ex. 'Medlem'). Välj sedan måltyper. Till att börja med kan du välja alla typer som mål. Slutligen väljer du rollen \"Arbetspaket editor\" som mål och trycker på \"Kopiera\". Efter att ha skapat standardinställningarna, finjustera arbetsflödena som du gör för varje annan roll." link_message: "Konfigurera arbetsflödena i administrationen." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ sv: dependencies: "Beroenden" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Visa fram till" attachment: @@ -1538,6 +1540,7 @@ sv: not_available: "is not available due to a system configuration." not_deletable: "kan inte raderas." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "hittades inte." not_a_date: "är inte är ett giltigt datum." not_a_datetime: "är inte en giltig datumtid." @@ -2099,6 +2102,7 @@ sv: role: "Roll" roles: "Roll" search: "Sök" + sprint: "Sprint" start_date: "Startdatum" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ sv: label_duplicate: "dublett" label_duplicates: "dubblett av" label_edit: "Redigera" - label_edit_attribute: "Edit attribute" label_edit_x: "Redigera: %{x}" label_enable_multi_select: "Växla multival" label_enabled_project_custom_fields: "Aktiverade anpassade fält" @@ -3975,6 +3978,7 @@ sv: notice_successful_delete: "Raderades utan problem." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Uppdaterades utan problem." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Uppdateringen misslyckades." @@ -4136,6 +4140,7 @@ sv: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4318,6 +4323,9 @@ sv: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/th.yml b/config/locales/crowdin/th.yml index b52fad092a9..bbf146e432f 100644 --- a/config/locales/crowdin/th.yml +++ b/config/locales/crowdin/th.yml @@ -1030,7 +1030,6 @@ th: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1155,6 +1154,9 @@ th: dependencies: "ส่วนที่อ้างอิง" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1529,6 +1531,7 @@ th: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2071,6 +2074,7 @@ th: role: "บทบาท" roles: "บทบาท" search: "ค้นหา" + sprint: "Sprint" start_date: "วันเริ่มต้น" status: "สถานะ" state: "State" @@ -3171,7 +3175,6 @@ th: label_duplicate: "ทำซ้ำ" label_duplicates: "ซ้ำ" label_edit: "แก้ไข" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "สลับไปเลือกหลายค่า" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3925,6 +3928,7 @@ th: notice_successful_delete: "ลบเรียบร้อยแล้ว" notice_successful_cancel: "Successful cancellation." notice_successful_update: "ปรับปรุงข้อมูลเรียบร้อยแล้ว" + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4085,6 +4089,7 @@ th: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4269,6 +4274,9 @@ th: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index c2e986d52cb..30b15cdb999 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -1039,7 +1039,6 @@ tr: title: "İş paketi paylaşımı için iş akışı eksik" message: "'İş paketi düzenleyicisi' rolü için hiçbir iş akışı yapılandırılmamıştır. Bir iş akışı olmadan, paylaşılan kullanıcı iş paketinin durumunu değiştiremez. İş akışları kopyalanabilir. Bir kaynak türü (örn. 'Görev') ve kaynak rolü (örn. 'Üye') seçin. Ardından hedef türleri seçin. Başlangıç olarak, tüm türleri hedef olarak seçebilirsiniz. Son olarak, hedef olarak 'İş paketi düzenleyicisi' rolünü seçin ve 'Kopyala' düğmesine basın. Varsayılanları bu şekilde oluşturduktan sonra, diğer tüm roller için yaptığınız gibi iş akışlarında ince ayar yapın." link_message: "İş akışlarını yönetim alanından yapılandırın." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ tr: dependencies: "Bağımlılıklar" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Son yayın tarihi" attachment: @@ -1538,6 +1540,7 @@ tr: not_available: "Sistem yapılandırması nedeniyle kullanılamaz.\n" not_deletable: "kaldırılamadı." not_current_user: "mevcut kullanıcı değil." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "bulunamadı." not_a_date: "geçerli bir tarih değil." not_a_datetime: "geçerli bir zaman değil." @@ -2099,6 +2102,7 @@ tr: role: "Rol" roles: "Yetkiler" search: "Ara" + sprint: "Sprint" start_date: "Başlangıç tarihi" status: "Durum" state: "Durum" @@ -3220,7 +3224,6 @@ tr: label_duplicate: "kopya" label_duplicates: "kopyalayan" label_edit: "Düzenle" - label_edit_attribute: "Edit attribute" label_edit_x: "Düzenle: %{x}" label_enable_multi_select: "Çoklu seçimi etkinleştir" label_enabled_project_custom_fields: "Etkin özel alanlar" @@ -3974,6 +3977,7 @@ tr: notice_successful_delete: "Silme başarılı." notice_successful_cancel: "İptal işlemi başarılı." notice_successful_update: "Güncelleme başarılı." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Oluşturulamadı." notice_unsuccessful_create_with_reason: "Oluşturma başarısız oldu: %{reason}" notice_unsuccessful_update: "Güncellenemedi." @@ -4134,6 +4138,7 @@ tr: permission_edit_project_query: "Proje sorgusunu düzenleme" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4318,6 +4323,9 @@ tr: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "İlk giriş yönlendirmesi" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index d9b5db30ab3..0444e2e9e20 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -1052,7 +1052,6 @@ uk: title: "Відсутній робочий процес для надання доступу до пакета робіт" message: "Для ролі «Редактор пакета робіт» не налаштовано жодного робочого процесу. Без робочого процесу користувач, якому надано доступ, не може змінити статус пакета робіт. Робочі процеси можна копіювати. Виберіть вихідний тип (напр., «Завдання») і роль (напр., «Учасник»). Потім виберіть цільові типи. Для початку радимо вибирати всі типи як цільові. Нарешті, виберіть роль «Редактор пакета робіт» і натисніть «Копіювати». Після цього ви зможете налаштовувати робочі процеси для кожної ролі, як ви це зазвичай робите." link_message: "Налаштуйте робочі процеси на панелі адміністрування." - templated_subject_hint: Автоматично згенеровано з використанням типу %{type} summary: reports: category: @@ -1176,6 +1175,9 @@ uk: dependencies: "Залежності" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Показувати до" attachment: @@ -1550,6 +1552,7 @@ uk: not_available: "– недоступно через налаштування системи." not_deletable: "не можна видалити." not_current_user: "не поточний користувач." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "не знайдено." not_a_date: "не є дійсною датою." not_a_datetime: "не є дійсним датою." @@ -2149,6 +2152,7 @@ uk: role: "роль" roles: "Роль" search: "Пошук" + sprint: "Sprint" start_date: "Початок" status: "Статус" state: "Стан" @@ -3312,7 +3316,6 @@ uk: label_duplicate: "Дублювати" label_duplicates: "Дублікати" label_edit: "Редагувати" - label_edit_attribute: "Редагувати атрибут" label_edit_x: "Редагувати: %{x}" label_enable_multi_select: "Перемкнути мультиселекцію" label_enabled_project_custom_fields: "Увімкнено спеціальні поля" @@ -3396,7 +3399,7 @@ uk: label_index_by_title: "Індекс за назвою" label_information: "Інформація" label_information_plural: "Інформація" - label_installation_guides: "Інструкції із встановлення" + label_installation_guides: "Інструкції зі встановлення" label_integer: "Ціле число" label_interface: "Інтерфейс" label_internal: "Власне" @@ -4068,6 +4071,7 @@ uk: notice_successful_delete: "Видалення успішно завершене." notice_successful_cancel: "Успішно видалено." notice_successful_update: "Успішно оновлено." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Не вдалося створити." notice_unsuccessful_create_with_reason: "Не вдалося створити: %{reason}" notice_unsuccessful_update: "Не вдалось оновити." @@ -4230,6 +4234,7 @@ uk: permission_edit_project_query: "Редагування запиту проєктів" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 портфелів" @@ -4414,6 +4419,9 @@ uk: setting_capture_external_links: "Захоплення зовнішніх посилань" setting_capture_external_links_text: > Якщо ввімкнено, усі зовнішні посилання у відформатованому тексті переспрямовуватимуть на попереджувальну сторінку перед переходом із додатка. Це допомагає захистити користувачів від потенційно шкідливих зовнішніх вебсайтів. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Переспрямування після першого входу" setting_after_first_login_redirect_url_text_html: > Задайте шлях для переспрямування користувачів після першого входу. Якщо не задано, користувачі переспрямовуються на головну сторінку з ознайомленням.Наприклад:
/my/pagediff --git a/config/locales/crowdin/uz.yml b/config/locales/crowdin/uz.yml index 8ebfcff4640..e62769f5f6b 100644 --- a/config/locales/crowdin/uz.yml +++ b/config/locales/crowdin/uz.yml @@ -1039,7 +1039,6 @@ uz: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: category: @@ -1164,6 +1163,9 @@ uz: dependencies: "Dependencies" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1538,6 +1540,7 @@ uz: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2099,6 +2102,7 @@ uz: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3220,7 +3224,6 @@ uz: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -3975,6 +3978,7 @@ uz: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4136,6 +4140,7 @@ uz: permission_edit_project_query: "Edit project query" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 portfolios" @@ -4320,6 +4325,9 @@ uz: setting_capture_external_links: "Capture external links" setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour.Example:
/my/pagediff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index 1d7850c8dcc..ecb8a391886 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -1028,7 +1028,6 @@ vi: title: "Thiếu luồng công việc để chia sẻ gói công việc" message: "Không có quy trình làm việc nào được định cấu hình cho vai trò 'Trình chỉnh sửa gói công việc'. Nếu không có quy trình làm việc, nội dung được chia sẻ với người dùng không thể thay đổi trạng thái của gói công việc. Quy trình làm việc có thể được sao chép. Chọn loại nguồn (ví dụ: 'Nhiệm vụ') và vai trò nguồn (ví dụ: 'Thành viên'). Sau đó chọn loại mục tiêu. Để bắt đầu, bạn có thể chọn tất cả các loại làm mục tiêu. Cuối cùng, chọn vai trò 'Trình chỉnh sửa gói công việc' làm mục tiêu và nhấn 'Sao chép'. Sau khi đã tạo các giá trị mặc định, hãy tinh chỉnh quy trình làm việc như bạn thực hiện với mọi vai trò khác." link_message: "Cấu hình các quy trình công việc trong quản trị." - templated_subject_hint: Được tạo tự động thông qua loại %{type} summary: reports: category: @@ -1153,6 +1152,9 @@ vi: dependencies: "phụ thuộc" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Hiển thị cho đến khi" attachment: @@ -1527,6 +1529,7 @@ vi: not_available: "không khả dụng do cấu hình hệ thống." not_deletable: "không thể xóa được." not_current_user: "không phải là người dùng hiện tại." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "không tìm thấy." not_a_date: "không phải là ngày hợp lệ" not_a_datetime: "không phải là thời gian hợp lệ" @@ -2007,7 +2010,7 @@ vi: body: "cơ thể" blocks_ids: "ID của các work package bị chặn" category: "thể loại" - comment: "bình luận" + comment: "Nhận xét" comments: "bình luận" content: "Nội dung" color: "màu sắc" @@ -2069,6 +2072,7 @@ vi: role: "Vai trò" roles: "Vai trò" search: "tìm kiếm" + sprint: "Sprint" start_date: "Ngày bắt đầu" status: "Trạng thái" state: "tiểu bang" @@ -3169,7 +3173,6 @@ vi: label_duplicate: "Nhân đôi" label_duplicates: "Nhân đôi" label_edit: "Chỉnh sửa" - label_edit_attribute: "Chỉnh sửa thuộc tính" label_edit_x: "Chỉnh sửa: %{x}" label_enable_multi_select: "Bật/tắt đa lựa chọn" label_enabled_project_custom_fields: "Các trường tùy chỉnh đã bật" @@ -3587,7 +3590,7 @@ vi: label_used_by: "Được dùng bởi" label_used_by_types: "Được sử dụng bởi các loại" label_used_in_projects: "Được sử dụng trong các dự án" - label_user: "người dùng" + label_user: "Người dùng" label_user_and_permission: "Người dùng và quyền" label_user_named: "Người dùng %{name}" label_user_activity: "%{value} hoạt động" @@ -3923,6 +3926,7 @@ vi: notice_successful_delete: "Xóa thành công." notice_successful_cancel: "Hủy thành công." notice_successful_update: "Cập nhật thành công." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Tạo không thành công." notice_unsuccessful_create_with_reason: "Tạo không thành công: %{reason}" notice_unsuccessful_update: "Cập nhật không thành công." @@ -4083,6 +4087,7 @@ vi: permission_edit_project_query: "Chỉnh sửa truy vấn dự án" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 danh mục đầu tư" @@ -4267,6 +4272,9 @@ vi: setting_capture_external_links: "Chụp các liên kết bên ngoài" setting_capture_external_links_text: > Khi tính năng này được kích hoạt, tất cả các liên kết ngoài trong văn bản định dạng sẽ được chuyển hướng qua một trang cảnh báo trước khi rời khỏi ứng dụng. Điều này giúp bảo vệ người dùng khỏi các trang web bên ngoài có thể chứa mã độc hại. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "Chuyển hướng đăng nhập lần đầu" setting_after_first_login_redirect_url_text_html: > Đặt đường dẫn để chuyển hướng người dùng sau lần đăng nhập đầu tiên của họ. Nếu trống, hãy chuyển hướng đến trang chủ để xem chuyến tham quan giới thiệu.Ví dụ:
/my/pagediff --git a/config/locales/crowdin/zh-CN.seeders.yml b/config/locales/crowdin/zh-CN.seeders.yml index 1ee133e5684..336ac4fa27d 100644 --- a/config/locales/crowdin/zh-CN.seeders.yml +++ b/config/locales/crowdin/zh-CN.seeders.yml @@ -97,7 +97,7 @@ zh-CN: demo-project: name: 演示项目 status_explanation: 所有任务都按计划进行。相关人员均知晓各自任务。系统已完全建立。 - description: 这是对此演示 Scrum 项目目标的简短摘要。 + description: 这是对此演示项目目标的简短摘要。 news: item_0: title: 欢迎来到您的演示项目 @@ -216,7 +216,7 @@ zh-CN: scrum-project: name: Scrum 项目 status_explanation: 所有任务都按计划进行。相关人员均知晓各自任务。系统已完全建立。 - description: 这是对此演示 Scrum 项目目标的简短摘要。 + description: 这是对此演示Scrum项目目标的简短摘要。 news: item_0: title: 欢迎来到您的 Scrum 演示项目 diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml index 1b70fd456a6..d956b92104e 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -87,7 +87,7 @@ zh-CN: token_placeholder: "在此处粘贴您的企业版支持令牌" add_token: "上传企业版支持令牌" replace_token: "替换您当前的支持令牌" - order: "订购本地部署版的 Enterprise edition" + order: "订购本地部署的 Enterprise edition" paste: "粘贴您企业版的支持令牌" required_for_feature: "此功能仅限具激活的企业版支持令牌的订阅者使用。" enterprise_link: "如需了解详细信息,请单击此处。" @@ -1026,7 +1026,6 @@ zh-CN: title: "工作包共享缺少工作流" message: "没有为\"工作包编辑者\"角色配置工作流。没有工作流,共享用户就无法更改工作包的状态。工作流可以复制。选择一个源类型(例如\"任务\")和源角色(例如\"成员\")。然后选择目标类型。一开始,您可以将所有类型都选择为目标类型。最后,选择\"工作包编辑者\"角色作为目标,然后点击\"复制\"。在创建默认设置之后,像对其他角色一样进行微调,对工作流进行详细调整。" link_message: "在管理中配置工作流。" - templated_subject_hint: 通过类型 %{type}自动生成 summary: reports: category: @@ -1151,6 +1150,9 @@ zh-CN: dependencies: "依赖项" activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "显示截止日期" attachment: @@ -1228,7 +1230,7 @@ zh-CN: page: "页" row_count: "行数" column_count: "列数" - widgets: "微件" + widgets: "小部件" journal: notes: "备注" cause_type: "Cause 类型" @@ -1525,6 +1527,7 @@ zh-CN: not_available: "因系统配置而不可用。" not_deletable: "无法删除。" not_current_user: "不是当前用户。" + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "未找到" not_a_date: "不是有效的日期。" not_a_datetime: "不是有效的日期时间。" @@ -2067,6 +2070,7 @@ zh-CN: role: "角色" roles: "角色" search: "搜索" + sprint: "Sprint" start_date: "开始日期" status: "状态" state: "地区" @@ -3167,7 +3171,6 @@ zh-CN: label_duplicate: "重复" label_duplicates: "复制" label_edit: "编辑" - label_edit_attribute: "编辑属性" label_edit_x: "编辑:%{x}" label_enable_multi_select: "切换多选" label_enabled_project_custom_fields: "启用自定义字段" @@ -3505,7 +3508,7 @@ zh-CN: label_revision_id: "修订版本 %{value}" label_revision_plural: "修订" label_roadmap: "路线图" - label_roadmap_edit: "编辑路线图%{name}" + label_roadmap_edit: "编辑路线图 %{name}" label_roadmap_due_in: "%{value} 到期" label_roadmap_no_work_packages: "该版本没有工作包。" label_roadmap_overdue: "%{value} 超时" @@ -3919,6 +3922,7 @@ zh-CN: notice_successful_delete: "成功删除。" notice_successful_cancel: "取消成功" notice_successful_update: "成功更新。" + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "创建失败。" notice_unsuccessful_create_with_reason: "创建失败:%{reason}" notice_unsuccessful_update: "更新失败。" @@ -4078,6 +4082,7 @@ zh-CN: permission_edit_project_query: "编辑项目查询" placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: zero: "0 个项目组合" @@ -4226,7 +4231,7 @@ zh-CN: managed: "在 OpenProject 中创建新的存储库" storage: not_available: "磁盘存储开销不可用于此存储库。" - update_timeout: "在 N 分钟内保留存储库最后所需的磁盘空间信息。由于计算存储库所需的磁盘空间可能增加系统开销,增加该值可以减少性能影响。" + update_timeout: "在 N 分钟内保留存储库最后所需磁盘空间的信息。由于计算存储库所需的磁盘空间可能增加系统开销,增加该值可以减少性能影响。" oauth_application_details: "关闭此窗口后,将无法再次访问客户端密钥值。请将这些值复制到 Nextcloud OpenProject 集成设置中:" oauth_application_details_link_text: "转到设置页面" setup_documentation_details: "如果您在配置新文件存储方面需要帮助,请查看文档:" @@ -4260,6 +4265,9 @@ zh-CN: setting_capture_external_links: "捕获外部链接" setting_capture_external_links_text: > 启用后,格式化文本中的所有外部链接在离开应用程序前都会重定向至警告页面。这有助于保护用户免受潜在恶意外部网站的危害。 + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "首次登录重定向" setting_after_first_login_redirect_url_text_html: > 设置用户首次登录后的重定向路径。如果该路径为空,则重定向到主页以进行导览介绍。示例:
/my/page@@ -4435,7 +4443,7 @@ zh-CN: setting_session_ttl_hint: "当设置的值低于5时,其作用类似于禁用。" setting_session_ttl_enabled: "会话过期" setting_start_of_week: "一周起始日" - setting_sys_api_enabled: "启用存储库管理网页服务" + setting_sys_api_enabled: "启用版本库管理 web 服务" setting_sys_api_description: "存储库管理网页服务提供了集成的,用户授权的存储库访问。" setting_time_format: "时间" setting_total_percent_complete_mode: "计算 完成% 层次结构总数" @@ -4864,7 +4872,7 @@ zh-CN: warning_user_limit_reached_admin: > 添加额外的用户将超出当前限制。请升级您的计划,以确保外部用户能够访问此实例。 warning_user_limit_reached_instructions: > - 您已达到用户限制(%{current}/%{max} 活跃用户)。请联系 sales@openproject.com 升级您的企业版计划以添加额外用户。 + 您达到了用户限制(%{current}/%{max}活跃用户)。 请联系sales@openproject.com以升级您的Enterprise edition计划并添加其他用户。 warning_protocol_mismatch_html: > warning_bar: diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index b4d6a95b089..9cbbe93d339 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -1027,7 +1027,6 @@ zh-TW: title: "共用工作套件缺少工作流" message: "「工作套件編輯者」角色尚未設定工作流程。沒有工作流程時,共享給該角色的使用者無法變更工作套件的狀態。您可以複製其他工作流程:選擇來源類型(例如「任務」)和來源角色(例如「成員」)。接著選擇目標類型。初期建議選擇所有類型作為目標。最後,選擇「工作套件編輯者」角色作為目標,並按下「複製」。建立預設後,您可以像調整其他角色的工作流程一樣,進行細部調整。" link_message: "在管理中配置工作流。" - templated_subject_hint: 透過類型 %{type}自動產生 summary: reports: category: @@ -1151,6 +1150,9 @@ zh-TW: dependencies: "依賴套件" activerecord: attributes: + agile/sprint: + sharing: "分享" + finish_date: "結束日期" announcements: show_until: "只顯示到" attachment: @@ -1525,6 +1527,7 @@ zh-TW: not_available: "由於系統配置所以不可用" not_deletable: "無法刪除" not_current_user: "不是目前使用者。" + only_one_active_sprint_allowed: "每個專案只允許一個活動衝刺。" not_found: "未找到" not_a_date: "不是有效的日期。" not_a_datetime: "不是有效的日期時間。" @@ -2067,6 +2070,7 @@ zh-TW: role: "角色" roles: "角色" search: "搜尋" + sprint: "衝刺" start_date: "起始日期" status: "狀態" state: "狀態" @@ -3167,7 +3171,6 @@ zh-TW: label_duplicate: "重複" label_duplicates: "重複" label_edit: "編輯" - label_edit_attribute: "編輯屬性" label_edit_x: "編輯:%{x}" label_enable_multi_select: "取用複選" label_enabled_project_custom_fields: "開啟客製欄位" @@ -3208,7 +3211,7 @@ zh-TW: label_filter_add: "新增條件" label_filter_by: "篩選條件:" label_filter_any_name_attribute: "名稱屬性" - label_filter_plural: "篩選條件" + label_filter_plural: "篩選器" label_filters_toggle: "顯示/隱藏篩選條件" label_float: "浮點數" label_folder: "資料夾" @@ -3223,8 +3226,8 @@ zh-TW: label_global_modules: "全域模組" label_global_roles: "全域角色" label_git_path: ".git 目錄的路徑" - label_greater_or_equal: "之前" - label_group_by: "分類" + label_greater_or_equal: ">=" + label_group_by: "分組依據" label_group_new: "新增群組" label_group: "群組" label_group_named: "群組名稱 %{name}" @@ -3236,7 +3239,7 @@ zh-TW: label_hierarchy: "階層" label_hierarchy_leaf: "頁面結構頁" label_home: "Home" - label_subject_or_id: "名稱或 id" + label_subject_or_id: "主旨或 id" label_calendar_subscriptions: "訂閱行事曆" label_identifier: "識別碼" label_in: "在" @@ -3286,7 +3289,7 @@ zh-TW: label_latest_revision_plural: "最新版本" label_ldap_authentication: "LDAP 認證" label_learn_more: "了解更多" - label_less_or_equal: "之後" + label_less_or_equal: "<=" label_less_than_ago: "幾天內" label_link_url: "連結(URL)" label_list: "清單" @@ -3920,6 +3923,7 @@ zh-TW: notice_successful_delete: "刪除成功" notice_successful_cancel: "取消成功" notice_successful_update: "更新成功" + notice_successful_move: "成功從 %{from} 移至 %{to}." notice_unsuccessful_create: "建立失敗。" notice_unsuccessful_create_with_reason: "建立失敗:%{reason}" notice_unsuccessful_update: "更新失敗。" @@ -4079,6 +4083,7 @@ zh-TW: permission_edit_project_query: "編輯專案查詢" placeholders: default: "-" + templated_hint: 透過類型 %{type}自動產生 portfolio: count: zero: "0 個組合" @@ -4263,6 +4268,9 @@ zh-TW: setting_capture_external_links: "擷取外部連結" setting_capture_external_links_text: > 啟用後,格式化文字中的所有外部連結都會在離開應用程式前透過警告頁重定向。這有助於保護使用者遠離潛在的惡意外部網站。 + setting_capture_external_links_require_login: "要求使用者登入" + setting_capture_external_links_require_login_text: > + 啟用後,想要按一下外部連結的使用者必須先登入才能繼續。 setting_after_first_login_redirect_url: "首次登入重新導向" setting_after_first_login_redirect_url_text_html: > 設定使用者首次登入後的重新導向路徑。如果為空,則會重定向到上線導覽的首頁。範例:
/my/pagediff --git a/config/locales/en.yml b/config/locales/en.yml index 7e3b962b210..24696a3adf2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -1123,7 +1123,6 @@ en: title: "Workflow missing for work package sharing" message: "No workflow is configured for the 'Work package editor' role. Without a workflow, the shared with user cannot alter the status of the work package. Workflows can be copied. Select a source type (e.g. 'Task') and source role (e.g. 'Member'). Then select the target types. To start with, you could select all the types as targets. Finally, select the 'Work package editor' role as the target and press 'Copy'. After having thus created the defaults, fine tune the workflows as you do for every other role." link_message: "Configure the workflows in the administration." - templated_subject_hint: Automatically generated through type %{type} summary: reports: @@ -1259,6 +1258,9 @@ en: activerecord: attributes: + agile/sprint: + sharing: "Sharing" + finish_date: "End date" announcements: show_until: "Display until" attachment: @@ -1634,6 +1636,7 @@ en: not_available: "is not available due to a system configuration." not_deletable: "cannot be deleted." not_current_user: "is not the current user." + only_one_active_sprint_allowed: "only one active sprint is allowed per project." not_found: "not found." not_a_date: "is not a valid date." not_a_datetime: "is not a valid date time." @@ -2201,6 +2204,7 @@ en: role: "Role" roles: "Roles" search: "Search" + sprint: "Sprint" start_date: "Start date" status: "Status" state: "State" @@ -3372,7 +3376,6 @@ en: label_duplicate: "duplicate" label_duplicates: "duplicates" label_edit: "Edit" - label_edit_attribute: "Edit attribute" label_edit_x: "Edit: %{x}" label_enable_multi_select: "Toggle multiselect" label_enabled_project_custom_fields: "Enabled custom fields" @@ -4135,6 +4138,7 @@ en: notice_successful_delete: "Successful deletion." notice_successful_cancel: "Successful cancellation." notice_successful_update: "Successful update." + notice_successful_move: "Successful move from %{from} to %{to}." notice_unsuccessful_create: "Creation failed." notice_unsuccessful_create_with_reason: "Creation failed: %{reason}" notice_unsuccessful_update: "Update failed." @@ -4303,6 +4307,7 @@ en: placeholders: default: "-" + templated_hint: Automatically generated through type %{type} portfolio: count: @@ -4504,6 +4509,9 @@ en: setting_capture_external_links_text: > When enabled, all external links in formatted text will redirect through a warning page before leaving the application. This helps protect users from potentially malicious external websites. + setting_capture_external_links_require_login: "Require users to be logged in" + setting_capture_external_links_require_login_text: > + When enabled, users wanting to click on external links need to be logged in before being able to continue. setting_after_first_login_redirect_url: "First login redirect" setting_after_first_login_redirect_url_text_html: > Set a path to redirect users after their first login. If empty, redirects to the home page for the onboarding tour. @@ -4563,6 +4571,10 @@ en: setting_smtp_password: "SMTP password" setting_smtp_domain: "SMTP HELO domain" setting_activity_days_default: "Days displayed on project activity" + setting_api_tokens_enabled: "Enable API tokens" + setting_api_tokens_enabled_caption: > + Decide whether users can create personal API tokens in their account settings. These tokens can be used to access the different + APIs of OpenProject, such as APIv3 and MCP. setting_app_subtitle: "Application subtitle" setting_app_title: "Application title" setting_attachment_max_size: "Attachment max. size" @@ -4677,7 +4689,6 @@ en: setting_repository_checkout_text: "Checkout instruction text" setting_repository_log_display_limit: "Maximum number of revisions displayed on file log" setting_repository_truncate_at: "Maximum number of files displayed in the repository browser" - setting_rest_api_enabled: "Enable REST web service" setting_self_registration: "Self-registration" setting_self_registration_caption: > Choose the self-registration mechanism for users. Be careful with the setting you choose, as some diff --git a/config/routes.rb b/config/routes.rb index 9d6891ed2ad..5373a93f1f3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -385,7 +385,9 @@ Rails.application.routes.draw do # this could probably be rewritten with a resource as: 'roadmap' get "/roadmap" => "versions#index" - resources :news, only: %i[index new create] + resources :news do + resources :comments, controller: "news/comments", only: %i[create destroy] + end # Match everything to be the ID of the wiki page except the part that # is reserved for the format. This assumes that we have only two formats: @@ -456,6 +458,13 @@ Rails.application.routes.draw do end resources :forums do + resources :topics, controller: "messages", except: [:index] do + member do + get :quote + post :reply, as: "reply_to" + end + end + member do get :confirm_destroy get :move @@ -465,18 +474,15 @@ Rails.application.routes.draw do resources :categories, except: %i[index show], shallow: true - resources :members, only: %i[index create update], shallow: true do + resources :members, only: %i[index create update] do collection do delete "by_principal/:principal_id", action: :destroy_by_principal get :autocomplete_for_member + get :menu, to: "members/menus#show" end end - namespace :members do - resource :menu, only: %[show] - end - resource :repository, controller: "repositories", except: [:new] do # Destroy uses a get request to prompt the user before the actual DELETE request get :destroy_info @@ -905,18 +911,7 @@ Rails.application.routes.draw do # The show page of groups is public and thus moved out of the admin scope resources :groups, only: %i[show], as: :show_group - resources :forums, only: [] do - resources :topics, controller: "messages", except: [:index], shallow: true do - member do - get :quote - post :reply, as: "reply_to" - end - end - end - - resources :news, only: %i[index destroy update edit show] do - resources :comments, controller: "news/comments", only: %i[create destroy], shallow: true - end + resources :news, only: %i[index show] # redirect for backwards compatibility scope "attachments", @@ -1063,6 +1058,13 @@ Rails.application.routes.draw do delete "Groups/:id", to: "groups#destroy" end + scope "inplace_edit_fields/:model/:id/:attribute", as: "inplace_edit_field" do + post :update, controller: "inplace_edit_fields", action: :update + patch :update, controller: "inplace_edit_fields", action: :update + get :reset, controller: "inplace_edit_fields", action: :reset + get :edit, controller: "inplace_edit_fields", action: :edit + end + if OpenProject::Configuration.lookbook_enabled? mount Primer::ViewComponents::Engine, at: "/" mount Lookbook::Engine, at: "/lookbook" diff --git a/db/migrate/20260203171223_remove_default_from_work_packages_subject.rb b/db/migrate/20260203171223_remove_default_from_work_packages_subject.rb new file mode 100644 index 00000000000..98c6748854f --- /dev/null +++ b/db/migrate/20260203171223_remove_default_from_work_packages_subject.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class RemoveDefaultFromWorkPackagesSubject < ActiveRecord::Migration[8.0] + def change + change_column_default :work_packages, :subject, from: "", to: nil + end +end diff --git a/db/migrate/20260212133700_rename_setting_rest_api_enabled.rb b/db/migrate/20260212133700_rename_setting_rest_api_enabled.rb new file mode 100644 index 00000000000..914068abf2b --- /dev/null +++ b/db/migrate/20260212133700_rename_setting_rest_api_enabled.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +require_relative "migration_utils/setting_renamer" + +class RenameSettingRestAPIEnabled < ActiveRecord::Migration[8.0] + def up + ::Migration::MigrationUtils::SettingRenamer.rename(:rest_api_enabled, :api_tokens_enabled) + end + + def down + ::Migration::MigrationUtils::SettingRenamer.rename(:api_tokens_enabled, :rest_api_enabled) + end +end diff --git a/docker-compose.yml b/docker-compose.yml index 12ef9b8fb3a..f530e2fe00d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -87,7 +87,7 @@ services: networks: - network environment: - __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: "openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD}" + __VITE_ADDITIONAL_SERVER_ALLOWED_HOSTS: "openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD:-local}" ports: - "${FE_PORT:-4200}:4200" diff --git a/docker/dev/gitlab/.env b/docker/dev/gitlab/.env deleted file mode 100644 index c962deb2abe..00000000000 --- a/docker/dev/gitlab/.env +++ /dev/null @@ -1 +0,0 @@ -OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/gitlab/.gitignore b/docker/dev/gitlab/.gitignore index 7376571d14b..b8865edfb36 100644 --- a/docker/dev/gitlab/.gitignore +++ b/docker/dev/gitlab/.gitignore @@ -1 +1,2 @@ +.env docker-compose.override.yml diff --git a/docker/dev/gitlab/docker-compose.yml b/docker/dev/gitlab/docker-compose.yml index 42ad0f3eb6a..7cf56fd9101 100644 --- a/docker/dev/gitlab/docker-compose.yml +++ b/docker/dev/gitlab/docker-compose.yml @@ -19,10 +19,10 @@ services: networks: - external extra_hosts: - - "openproject.${OPENPROJECT_DOCKER_DEV_TLD}:host-gateway" + - "openproject.${OPENPROJECT_DOCKER_DEV_TLD:-local}:host-gateway" labels: - "traefik.enable=true" - - "traefik.http.routers.gitlab.rule=Host(`gitlab.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.gitlab.rule=Host(`gitlab.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" - "traefik.http.routers.gitlab.entrypoints=websecure" - "traefik.http.services.gitlab.loadbalancer.server.port=80" diff --git a/docker/dev/hocuspocus/.env b/docker/dev/hocuspocus/.env deleted file mode 100644 index c962deb2abe..00000000000 --- a/docker/dev/hocuspocus/.env +++ /dev/null @@ -1 +0,0 @@ -OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/hocuspocus/.gitignore b/docker/dev/hocuspocus/.gitignore index 7376571d14b..b8865edfb36 100644 --- a/docker/dev/hocuspocus/.gitignore +++ b/docker/dev/hocuspocus/.gitignore @@ -1 +1,2 @@ +.env docker-compose.override.yml diff --git a/docker/dev/hocuspocus/docker-compose.yml b/docker/dev/hocuspocus/docker-compose.yml index 4419c6e4365..6ff6d5fbda9 100644 --- a/docker/dev/hocuspocus/docker-compose.yml +++ b/docker/dev/hocuspocus/docker-compose.yml @@ -5,7 +5,7 @@ services: image: openproject/hocuspocus:latest labels: - "traefik.enable=true" - - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" - "traefik.http.routers.hocuspocus.service=hocuspocus-service" - "traefik.http.routers.hocuspocus.tls=true" - "traefik.http.services.hocuspocus-service.loadbalancer.server.port=1234" @@ -14,7 +14,7 @@ services: networks: - gateway environment: - - ALLOWED_DOMAINS=openproject.${OPENPROJECT_DOCKER_DEV_TLD},localhost + - ALLOWED_DOMAINS=openproject.${OPENPROJECT_DOCKER_DEV_TLD:-local},localhost - NODE_TLS_REJECT_UNAUTHORIZED=0 - SECRET=secret12345 networks: diff --git a/docker/dev/keycloak/.env b/docker/dev/keycloak/.env deleted file mode 100644 index c962deb2abe..00000000000 --- a/docker/dev/keycloak/.env +++ /dev/null @@ -1 +0,0 @@ -OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/keycloak/.gitignore b/docker/dev/keycloak/.gitignore index 7376571d14b..b8865edfb36 100644 --- a/docker/dev/keycloak/.gitignore +++ b/docker/dev/keycloak/.gitignore @@ -1 +1,2 @@ +.env docker-compose.override.yml diff --git a/docker/dev/keycloak/docker-compose.yml b/docker/dev/keycloak/docker-compose.yml index 4c5bf556edc..9c2f933ea8b 100644 --- a/docker/dev/keycloak/docker-compose.yml +++ b/docker/dev/keycloak/docker-compose.yml @@ -35,7 +35,7 @@ services: - KEYCLOAK_ADMIN=admin - KEYCLOAK_ADMIN_PASSWORD=admin - KC_DB_SCHEMA=public - - KC_HOSTNAME=keycloak.${OPENPROJECT_DOCKER_DEV_TLD} + - KC_HOSTNAME=keycloak.${OPENPROJECT_DOCKER_DEV_TLD:-local} - KC_TRANSACTION_XA_ENABLED=false volumes: - /etc/ssl/certs/ca-certificates.crt:/etc/ssl/certs/ca-certificates.crt:ro @@ -43,7 +43,7 @@ services: - ./themes/:/opt/keycloak/themes/ labels: - "traefik.enable=true" - - "traefik.http.routers.keycloak-sub-secure.rule=Host(`keycloak.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.keycloak-sub-secure.rule=Host(`keycloak.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" - "traefik.http.routers.keycloak-sub-secure.entrypoints=websecure" depends_on: - db-keycloak diff --git a/docker/dev/minio/.env b/docker/dev/minio/.env deleted file mode 100644 index c962deb2abe..00000000000 --- a/docker/dev/minio/.env +++ /dev/null @@ -1 +0,0 @@ -OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/minio/.gitignore b/docker/dev/minio/.gitignore index 7376571d14b..b8865edfb36 100644 --- a/docker/dev/minio/.gitignore +++ b/docker/dev/minio/.gitignore @@ -1 +1,2 @@ +.env docker-compose.override.yml diff --git a/docker/dev/minio/docker-compose.yml b/docker/dev/minio/docker-compose.yml index cecc8ee8df9..7255785f13c 100644 --- a/docker/dev/minio/docker-compose.yml +++ b/docker/dev/minio/docker-compose.yml @@ -19,13 +19,13 @@ services: - "traefik.enable=true" # MinIO API - "traefik.http.routers.minio.entrypoints=websecure" - - "traefik.http.routers.minio.rule=Host(`minio.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.minio.rule=Host(`minio.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" - "traefik.http.routers.minio.service=minio" - "traefik.http.routers.minio.tls.certresolver=step" - "traefik.http.services.minio.loadbalancer.server.port=9000" # MinIO Admin Console (Management UI) - "traefik.http.routers.minioadmin.entrypoints=websecure" - - "traefik.http.routers.minioadmin.rule=Host(`minioadmin.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.minioadmin.rule=Host(`minioadmin.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" - "traefik.http.routers.minioadmin.service=minioadmin" - "traefik.http.routers.minioadmin.tls.certresolver=step" - "traefik.http.services.minioadmin.loadbalancer.server.port=9001" diff --git a/docker/dev/nextcloud/.env b/docker/dev/nextcloud/.env deleted file mode 100644 index c962deb2abe..00000000000 --- a/docker/dev/nextcloud/.env +++ /dev/null @@ -1 +0,0 @@ -OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/nextcloud/.gitignore b/docker/dev/nextcloud/.gitignore index 7376571d14b..b8865edfb36 100644 --- a/docker/dev/nextcloud/.gitignore +++ b/docker/dev/nextcloud/.gitignore @@ -1 +1,2 @@ +.env docker-compose.override.yml diff --git a/docker/dev/nextcloud/docker-compose.yml b/docker/dev/nextcloud/docker-compose.yml index 100a37dbc49..19667ff8b2f 100644 --- a/docker/dev/nextcloud/docker-compose.yml +++ b/docker/dev/nextcloud/docker-compose.yml @@ -11,7 +11,7 @@ services: # - ../nextcloud_apps:/var/www/html/custom_apps labels: - "traefik.enable=true" - - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${OPENPROJECT_DOCKER_DEV_TLD}`)" + - "traefik.http.routers.nextcloud.rule=Host(`nextcloud.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" - "traefik.http.routers.nextcloud.entrypoints=websecure" cron: diff --git a/docker/dev/tls/.env b/docker/dev/tls/.env deleted file mode 100644 index c962deb2abe..00000000000 --- a/docker/dev/tls/.env +++ /dev/null @@ -1 +0,0 @@ -OPENPROJECT_DOCKER_DEV_TLD=local diff --git a/docker/dev/tls/.gitignore b/docker/dev/tls/.gitignore index 5502086abc8..3211e03f17c 100644 --- a/docker/dev/tls/.gitignore +++ b/docker/dev/tls/.gitignore @@ -1,3 +1,3 @@ +.env acme.json - docker-compose.override.yml diff --git a/docker/dev/tls/docker-compose.core-override.example.yml b/docker/dev/tls/docker-compose.core-override.example.yml index d45a9be091b..70c0d67a745 100644 --- a/docker/dev/tls/docker-compose.core-override.example.yml +++ b/docker/dev/tls/docker-compose.core-override.example.yml @@ -6,30 +6,30 @@ x-op-env-override: &environment SSL_CERT_FILE: /etc/ssl/certs/ca-certificates.crt # uncomment and set all the envs below to integrate keycloak with OpenProject # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_DISPLAY__NAME: Keycloak - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: keycloak.${OPENPROJECT_DOCKER_DEV_TLD} - # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: https://openproject.${OPENPROJECT_DOCKER_DEV_TLD} + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_HOST: keycloak.${OPENPROJECT_DOCKER_DEV_TLD:-local} + # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_IDENTIFIER: https://openproject.${OPENPROJECT_DOCKER_DEV_TLD:-local} # OPENPROJECT_OPENID__CONNECT_KEYCLOAK_SECRET:Note: This setting can only be set through ENV/settings | | SMTP Timeout | smtp_timeout | `OPENPROJECT_SMTP__TIMEOUT` | This optional setting allows you to specify the number of seconds to wait for SMTP connections to be opened and read.
If the value is set too low, a `Net::OpenTimeout` or `Net::ReadTimeout` might be raised. | -## Package-based installation (DEB/RPM) +### Package-based installation (DEB/RPM) If you installed OpenProject with the package-based installation, you can configure the above settings using the config:set helper. Please note that this will disable the settings in the administration UI. @@ -64,7 +65,7 @@ openproject config:set OPENPROJECT_SMTP__USER__NAME="apikey" openproject config:set OPENPROJECT_SMTP__PASSWORD="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" ``` -## Docker installation +### Docker installation If you installed OpenProject with Docker, here is how you would enable outbound emails through the use of the SMTP environment variables (with SendGrid, the `SMTP_USER_NAME` is always `apikey`. Just replace `SMTP_PASSWORD` with the API key you've generated and you should be good to go). Please note that this will disable the settings in the administration UI. @@ -81,3 +82,30 @@ docker run -d \ -e OPENPROJECT_SMTP__PASSWORD="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" \ ... ``` +## Sendmail + +### Requirements + +You need to have Sendmail configured on your server. +For information about how to configure Sendmail, please refer to the [Sendmail docs](https://www.sendmail.org/~ca/email/doc8.12/cf/m4/index.html) + + +### Configuration through the Admin UI + +OpenProject allows you to configure your Sendmail through the administration UI. Using the default admin account created when you first installed OpenProject, go to Administration > Emails and notifications. +Here, you need to change the `Email delivery method` to `sendmail`. + + + +If you want to override the path where Sendmail is installed or change the command-line arguments, you can’t do this through the web frontend. +In this case, please use the variable shown in the next section to configure the path or arguments. + +### Sendmail Options + +These are the options that are available. Please see the [Configuration guide](../) and [Environment variables guide](../environment) on how to set these values from the command line. + +| Option | Setting | ENV name | Description | +| -------------------------- | ------------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Email delivery method | email_delivery_method | `OPENPROJECT_EMAIL__DELIVERY__METHOD` | email delivery method to be used (smtp, sendmail) | +| Sendmail location | sendmail_location | `OPENPROJECT_SENDMAIL__LOCATION` | Location of sendmail to call if it is configured as outgoing email setup. Default value: `/usr/sbin/sendmail` | +| Sendmail arguments | sendmail_arguments | `OPENPROJECT_SENDMAIL__ARGUMENTS` | Arguments to call sendmail with in case it is configured as outgoing email setup. Default value: `-i` | diff --git a/docs/installation-and-operations/configuration/outbound-emails/sendmail.png b/docs/installation-and-operations/configuration/outbound-emails/sendmail.png new file mode 100644 index 00000000000..254ddc14e2c Binary files /dev/null and b/docs/installation-and-operations/configuration/outbound-emails/sendmail.png differ diff --git a/docs/installation-and-operations/system-requirements/README.md b/docs/installation-and-operations/system-requirements/README.md index 12611d1fb7b..ed3c7c27d2c 100644 --- a/docs/installation-and-operations/system-requirements/README.md +++ b/docs/installation-and-operations/system-requirements/README.md @@ -34,7 +34,7 @@ OpenProject currently requires some bundled extensions, that should be available - [btree_gist: GiST operator classes with B-tree behavior](https://www.postgresql.org/docs/current/btree-gist.html) - [unaccent: a text search dictionary which removes diacritics](https://www.postgresql.org/docs/current/unaccent.html) -Additionally, OpenProject will try to create a [custom collation](https://www.postgresql.org/docs/current/collation.html) for version sorting that depends on `und-u-kn-true` ICU collation. +Additionally, OpenProject will try to create a [custom collation](https://www.postgresql.org/docs/current/collation.html) for version sorting that depends on `und-u-kn-true` ICU collation. ## Scaling requirements @@ -214,6 +214,7 @@ OpenProject supports the latest versions of the major browsers. * [Nextcloud 30](https://nextcloud.com/changelog/#latest30) * [Nextcloud 31](https://nextcloud.com/changelog/#latest31) +* [Nextcloud 32](https://nextcloud.com/changelog/#latest32) > [!TIP] > @@ -228,13 +229,13 @@ OpenProject supports the latest versions of the major browsers. ##### OpenProject integration -* [OpenProject Integration 2.10.0](https://github.com/nextcloud/integration_openproject/releases/tag/v2.10.0) +* [OpenProject Integration 2.11.1](https://github.com/nextcloud/integration_openproject/releases/tag/v2.11.1) ##### Team folders If you want to use the feature of [automatically managed project folders](../../system-admin-guide/integrations/nextcloud/#4-automatically-managed-project-folders) you need to install the [Team folders](https://apps.nextcloud.com/apps/groupfolders) app in Nextcloud (formerly Group folders). -* [Team folders 19.1.7](https://github.com/nextcloud/groupfolders/releases/tag/v19.1.7) +* [Team folders 19.1.14](https://github.com/nextcloud/groupfolders/releases/tag/v19.1.14) ### Keycloak token exchange diff --git a/docs/release-notes/14/14-4-0/README.md b/docs/release-notes/14/14-4-0/README.md index 97690decef1..8ab1ce4d934 100644 --- a/docs/release-notes/14/14-4-0/README.md +++ b/docs/release-notes/14/14-4-0/README.md @@ -10,7 +10,7 @@ release_date: 2024-08-14 Release date: 2024-08-14 -We released [OpenProject 14.4.0](https://community.openproject.org/versions/2063). The release contains several bug fixes and we recommend updating to the newest version. +We released [OpenProject 14.4.0](https://community.openproject.org/versions/2063). The release contains several bug fixes and we recommend updating to the newest version. In these Release Notes, we will give an overview of important technical updates as well as important feature changes. At the end, you will find a complete list of all changes and bug fixes. @@ -22,7 +22,7 @@ OpenProject 14.4 introduces a new feature that allows OpenID clients, such as Ne With this feature, the OpenProject API will validate access tokens issued by the OpenID provider (Keycloak) by checking the token's signature and authenticating the user using the sub claim value. This integration ensures secure and efficient API authentication for OpenID clients. -For more details, take a look at our [API documentation](../../../api/introduction/#oidc-provider-generated-jwt-as-a-bearer-token). +For more details, take a look at our [API documentation](../../../api/introduction/#oauth-20-using-an-external-authorization-server). ### Improve error messages and logs of automatically managed project folders synchronization services/jobs @@ -38,7 +38,7 @@ For more details, see this [work package](https://community.openproject.org/wp/5 ### Personal settings: Dark mode -Dark mode for OpenProject is finally here! In the '[My account](../../../user-guide/account-settings/#look-and-feel)' section under 'Interface', there is an **option labeled 'Mode' where users can now select 'Dark (Beta).'** – as an alternative to the light mode. When the dark mode is selected, the change applies only to that user, not to the entire instance. +Dark mode for OpenProject is finally here! In the '[My account](../../../user-guide/account-settings/#look-and-feel)' section under 'Interface', there is an **option labeled 'Mode' where users can now select 'Dark (Beta).'** – as an alternative to the light mode. When the dark mode is selected, the change applies only to that user, not to the entire instance.  @@ -222,12 +222,12 @@ Clicking on the "Details" link will take the user to the diff view, which is als ## Contributions -A very special thank you goes to the City of Cologne again for sponsoring features on project attributes and project lists. +A very special thank you goes to the City of Cologne again for sponsoring features on project attributes and project lists. Also a big thanks to our Community members for reporting bugs and helping us identify and provide fixes. Special thanks for reporting and finding bugs go to Johan Bouduin, Sven Kunze and Marcel Carvalho. -Last but not least, we are very grateful for our very engaged translation contributors on Crowdin, who translated quite a few OpenProject strings! This release we would like to highlight the three following users: +Last but not least, we are very grateful for our very engaged translation contributors on Crowdin, who translated quite a few OpenProject strings! This release we would like to highlight the three following users: - [Jeff Li](https://crowdin.com/profile/jeff_li) for translations to Chinese Simplified, - [Adam Siemienski](https://crowdin.com/profile/siemienas) for translations to Polish, diff --git a/docs/system-admin-guide/api-and-webhooks/README.md b/docs/system-admin-guide/api-and-webhooks/README.md index 74df21f2dcf..e4c3d9ab0c7 100644 --- a/docs/system-admin-guide/api-and-webhooks/README.md +++ b/docs/system-admin-guide/api-and-webhooks/README.md @@ -13,9 +13,12 @@ Navigate to **Administration → API and webhooks**. ## API +  -Here, you can manage the **REST web service** to selectively control whether foreign applications may access your OpenProject API endpoints from within the browser. This setting allows users to access the OpenProject API using an API token created from the users "Account settings" page. You can set the **maximum page size** the API will respond with. It will not be possible to perform API requests that return more values on a single page. You can also enable **write access to read-only attributes**, which will allow administrators to write static read-only attributes during creation, such as *createdAt* and *author*. +Here, you can manage whether users can create personal API tokens, this setting allows users to access the OpenProject APIs using an API token created from the user's "Account settings" page. +You can set the **maximum page size** the API will respond with. It will not be possible to perform API requests that return more values on a single page. +You can also enable **write access to read-only attributes**, which will allow administrators to write static read-only attributes during creation, such as *createdAt* and *author*. This can be useful during data imports. ### Documentation diff --git a/docs/system-admin-guide/documents/README.md b/docs/system-admin-guide/documents/README.md index 889a0ca7306..5506fab3d7f 100644 --- a/docs/system-admin-guide/documents/README.md +++ b/docs/system-admin-guide/documents/README.md @@ -80,11 +80,78 @@ From a technical perspective, real-time collaboration relies on a running [Hocus ### Enable real-time collaboration for packaged installations -To enable real-time collaboration in packaged installations, follow these steps: -1. Download and install [op-blocknote-hocuspocus](https://github.com/opf/op-blocknote-hocuspocus) -2. Set up the server by following the instructions in the GitHub repository -3. Manually configure the server URL & secret in the *Documents* administration settings in OpenProject. -> [!NOTE] +#### 1. Install hocuspocus + +The easiest way to install hocuspocus is by using the Docker container. +You can do so by using the following steps. + +Create a hocuspocus directory: + +```shell +mkdir hocuspocus +cd hocuspocus +``` +Then you can create a `docker-compose.yml` file with the following content inside the `hocuspocus` directory: + +```yaml +services: + hocuspocus: + image:
+ +
Debug
+++{{maxValue() }}+{{lineChartData() | json}}+