diff --git a/.env.example b/.env.example index 1d40114a28a..22069e05a6c 100644 --- a/.env.example +++ b/.env.example @@ -48,10 +48,15 @@ FE_PORT=4200 OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__URL=ws://localhost:1234 OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__SECRET=secret12345 +# Hocuspocus (collaborative editing) - docker dev development +# OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__URL=wss://hocuspocus.local +# OPENPROJECT_COLLABORATIVE__EDITING__HOCUSPOCUS__SECRET=secret12345 + # Default TLD for docker dev stack (e.g. when set to "local", services will be openproject.local, nextcloud.local, etc.) OPENPROJECT_DOCKER_DEV_TLD=local # Use this variables to configure hostnames for frontend and backend, e.g. to enable HTTPS in docker development setup +# For docker development: openproject.local and localhost for "local" development OPENPROJECT_DEV_HOST=localhost OPENPROJECT_DEV_URL=http://${OPENPROJECT_DEV_HOST}:${FE_PORT} diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 84c9f0eebe9..9f8fb33d8d7 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -40,6 +40,8 @@ updates: update-types: ["version-update:semver-major"] - dependency-name: "@openproject/octicons" - dependency-name: "@openproject/primer-view-components" + - dependency-name: "@primer/primitives" + - dependency-name: "@primer/css" - package-ecosystem: "bundler" directory: "/" schedule: diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index b1920ad916c..dfce7ca123d 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -41,6 +41,7 @@ jobs: NobodysNightmare, RobinWagner, akabiru, + anna-mohn, as-op, astock2, ba1ash, @@ -53,10 +54,12 @@ jobs: corinnaguenther, crohr, dependabot[bot], + dfriquet, dombesz, dominic-braeunlein, dsteiner, dtohmucu, + fereshtehnm, gleone-art, ihor-khomenko, judithroth, @@ -80,8 +83,8 @@ jobs: vspielau, wielinde, yanzubrytskyi, - ehassan01 - + ehassan01, + thykel # the followings are the optional inputs - If the optional inputs are not given, then default values will be taken remote-organization-name: opf remote-repository-name: legal diff --git a/.github/workflows/create-merge-release-into-dev-pr.yml b/.github/workflows/create-merge-release-into-dev-pr.yml index c2bd973bf78..853e71493be 100644 --- a/.github/workflows/create-merge-release-into-dev-pr.yml +++ b/.github/workflows/create-merge-release-into-dev-pr.yml @@ -57,8 +57,14 @@ jobs: - name: Resolve bug in git https://stackoverflow.com/a/63879454/96823 run: git repack -d - - name: Get one commit more to connect the history of dev and release branches - run: git fetch --deepen 1 origin "$BASE_BRANCH" "$RELEASE_BRANCH" + - name: Get more commits to connect the history of dev and release branches + run: | + for i in $(seq 1 10); do + git fetch --deepen 10 origin "$BASE_BRANCH" "$RELEASE_BRANCH" + git merge-base --all HEAD origin/"$RELEASE_BRANCH" && exit 0 + done + echo "Failed to fetch merge base" >&2 + exit 1 - name: Create branch without checkout just to have shorter automatic commit message run: git branch "$RELEASE_BRANCH" origin/"$RELEASE_BRANCH" diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 27904f8b1fa..a5d38e38ead 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -113,7 +113,7 @@ jobs: ./docker/prod/setup/precompile-assets.sh # public/assets will be saved as artifact, so temporarily copying config file there as well cp config/frontend_assets.manifest.json public/assets/frontend_assets.manifest.json - - uses: actions/upload-artifact@v6 + - uses: actions/upload-artifact@v7 with: path: public/ name: public-assets-${{ inputs.tag }}-${{ github.sha }} @@ -168,7 +168,7 @@ jobs: run: | cp ./docker/prod/Dockerfile ./Dockerfile - name: Download precompiled public assets - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: public-assets-${{ inputs.tag }}-${{ github.sha }} path: public/ @@ -187,18 +187,18 @@ jobs: echo "https://github.com/opf/openproject/commits/${{ inputs.branch }}" > PRODUCT_URL date -u +"%Y-%m-%dT%H:%M:%SZ" > RELEASE_DATE - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx id: buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: context: git labels: | @@ -223,7 +223,7 @@ jobs: sed -i 's/vendor\/bundle//g' .dockerignore - name: Build image id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . platforms: ${{ matrix.platform }} @@ -267,7 +267,7 @@ jobs: --platform "${{ matrix.platform }}" - name: Push image id: push - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . platforms: ${{ matrix.platform }} @@ -284,7 +284,7 @@ jobs: digest="${{ steps.push.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@v7 with: name: digests-${{ inputs.tag }}-${{ matrix.target }}--${{ matrix.digest }} path: /tmp/digests/* @@ -300,13 +300,13 @@ jobs: - build steps: - name: Merge digests - uses: actions/upload-artifact/merge@v6 + uses: actions/upload-artifact/merge@v7 with: pattern: "digests-${{ inputs.tag }}-${{ matrix.target }}--*" overwrite: true name: "merged-digests-${{ inputs.tag }}-${{ matrix.target }}-${{ github.run_number }}-${{ github.run_attempt }}" - name: Download digests - uses: actions/download-artifact@v7 + uses: actions/download-artifact@v8 with: name: "merged-digests-${{ inputs.tag }}-${{ matrix.target }}-${{ github.run_number }}-${{ github.run_attempt }}" path: /tmp/digests @@ -317,10 +317,10 @@ jobs: if [ "$suffix" = "-all-in-one" ]; then suffix="" ; fi echo "suffix=$suffix" >> "$GITHUB_OUTPUT" - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: ${{ needs.setup.outputs.registry_image }} labels: | @@ -335,7 +335,7 @@ jobs: tags: | ${{ needs.setup.outputs.docker_tags }} - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} diff --git a/.github/workflows/email-notification.yml b/.github/workflows/email-notification.yml index dda751a5c28..b4c532c89f6 100644 --- a/.github/workflows/email-notification.yml +++ b/.github/workflows/email-notification.yml @@ -25,7 +25,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Send mail - uses: dawidd6/action-send-mail@v7 + uses: dawidd6/action-send-mail@v15 with: subject: ${{ inputs.subject }} body: ${{ inputs.body }} diff --git a/.github/workflows/hocuspocus-docker.yml b/.github/workflows/hocuspocus-docker.yml index 680f1ae61c1..27b93b9d085 100644 --- a/.github/workflows/hocuspocus-docker.yml +++ b/.github/workflows/hocuspocus-docker.yml @@ -83,20 +83,20 @@ jobs: echo $REGISTRY:$LATEST_TAG >> $GITHUB_OUTPUT echo 'EOF' >> $GITHUB_OUTPUT - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Build and push id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: extensions/op-blocknote-hocuspocus push: true @@ -112,7 +112,7 @@ jobs: - name: Generate GHA token id: generate-gha-token - uses: actions/create-github-app-token@v2 + uses: actions/create-github-app-token@v3 with: app-id: ${{ vars.DEPLOY_APP_ID }} private-key: ${{ secrets.DEPLOY_APP_PRIVATE_KEY }} diff --git a/.github/workflows/hocuspocus-test.yml b/.github/workflows/hocuspocus-test.yml index 0839e51f825..17af080cb10 100644 --- a/.github/workflows/hocuspocus-test.yml +++ b/.github/workflows/hocuspocus-test.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 diff --git a/.github/workflows/sync-internal-fork.yml b/.github/workflows/sync-internal-fork.yml index a3e21021e18..2d91bd10284 100644 --- a/.github/workflows/sync-internal-fork.yml +++ b/.github/workflows/sync-internal-fork.yml @@ -13,7 +13,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v6 with: fetch-depth: 0 token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.rubocop.yml b/.rubocop.yml index 47896294dfe..1815a2c11d1 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -226,6 +226,10 @@ Rails/SkipsModelValidations: RSpecRails/HttpStatus: Enabled: false +# The block form of `travel_to` is often the tighter and safer option in our Rails specs. +RSpecRails/TravelAround: + Enabled: false + # expect not_to change is not working as expected # if you chain it with multiple expected changes RSpec/ChangeByZero: diff --git a/.ruby-version b/.ruby-version index 2aa51319921..1454f6ed4b7 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -3.4.7 +4.0.1 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b0484f3ae6f..cb9ad130e7a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -139,6 +139,7 @@ version 1.0.0, available at ## Contributors license agreement -Contributors have to sign a CLA before contributing to OpenProject. -The [CLA can be found here](https://www.openproject.org/legal/contributor-license-agreement/) -and has to be filled out and sent to info@openproject.org. +If you want to contribute to OpenProject, please make sure to accept our Contributor License Agreement (CLA). The CLA documents the rights granted by contributors to OpenProject. +The [CLA can be found here](https://www.openproject.org/legal/contributor-license-agreement/). + +A GitHub action will enforce the CLA has been read and accepted by every new contributor. You will only be asked once, the document is short, and signing is easy. diff --git a/Gemfile b/Gemfile index dd4bb56ea2b..78d1a139ecd 100644 --- a/Gemfile +++ b/Gemfile @@ -50,7 +50,7 @@ gem "connection_pool", "~> 3.0.2" gem "rdoc", ">= 2.4.2" -gem "doorkeeper", "~> 5.8.0" +gem "doorkeeper", "~> 5.9.0" # Maintain our own omniauth due to relative URL root issues # see upstream PR: https://github.com/omniauth/omniauth/pull/903 gem "omniauth", git: "https://github.com/opf/omniauth", ref: "7eb21563ba047ef86d71f099975587b5ec88f9c9" @@ -69,16 +69,16 @@ gem "scimitar", "~> 2.13" gem "acts_as_list", "~> 1.2.6" gem "acts_as_tree", "~> 2.9.0" gem "awesome_nested_set", "~> 3.9.0" -gem "closure_tree", "~> 9.5.0" +gem "closure_tree", "~> 9.6.1" gem "rubytree", "~> 2.2.0" -gem "addressable", "~> 2.8.0" +gem "addressable", "~> 2.8.9" # Remove whitespace from model input gem "auto_strip_attributes", "~> 2.5" # Provide timezone info for TZInfo used by AR -gem "tzinfo-data", "~> 1.2025.1" +gem "tzinfo-data", "~> 1.2026.1" # to generate html-diffs (e.g. for wiki comparison) gem "htmldiff" @@ -124,10 +124,10 @@ gem "sys-filesystem", "~> 1.5.0", require: false gem "bcrypt", "~> 3.1.6" gem "multi_json", "~> 1.19.0" -gem "oj", "~> 3.16.12" +gem "oj", "~> 3.16.16" gem "daemons" -gem "good_job", "~> 4.12.0" # update should be done manually in sync with saas-openproject version. +gem "good_job", "~> 4.13.3" # update should be done manually in sync with saas-openproject version. gem "rack-protection", "~> 3.2.0" @@ -161,7 +161,7 @@ gem "ttfunk", "~> 1.7.0" # remove after https://github.com/prawnpdf/prawn/issues # prawn implicitly depends on matrix gem no longer in ruby core with 3.1 gem "matrix", "~> 0.4.3" -gem "mcp", "~> 0.7.0" +gem "mcp", "~> 0.8.0" gem "meta-tags", "~> 2.22.3" @@ -199,8 +199,9 @@ gem "rack-timeout", "~> 0.7.0", require: "rack/timeout/base" gem "nokogiri", "~> 1.19.1" -gem "carrierwave", "~> 1.3.4" -gem "carrierwave_direct", "~> 2.1.0" +gem "carrierwave", "~> 2.2.6" +gem "carrierwave_direct", "~> 3.0.0" +gem "ssrf_filter", "~> 1.3" gem "fog-aws" gem "aws-sdk-core", "~> 3.241" @@ -239,7 +240,7 @@ gem "opentelemetry-exporter-otlp", "~> 0.31.0", require: false gem "opentelemetry-instrumentation-all", "~> 0.90.0", require: false gem "opentelemetry-sdk", "~> 1.10", require: false -gem "view_component", "~> 4.4.0" +gem "view_component", "~> 4.5.0" # Lookbook gem "lookbook", "2.3.14" @@ -255,7 +256,7 @@ gem "turbo-rails", "~> 2.0.20" # There is a problem with version 1.4.0. Do not update until you're sure there is no infinite hang # happenning in failing tests when WebMock or VCR stub cannot be found. -gem "httpx", "~> 1.6.3" +gem "httpx", "~> 1.7.3" # Brings actual deep-freezing to most ruby objects gem "ice_nine" @@ -273,10 +274,10 @@ group :test do gem "rack_session_access" gem "rspec", "~> 3.13.2" # also add to development group, so 'spec' rake task gets loaded - gem "rspec-rails", "~> 8.0.3", group: :development + gem "rspec-rails", "~> 8.0.4", group: :development # Retry failures within the same environment - gem "retriable", "~> 3.1.1" + gem "retriable", "~> 3.2.1" gem "rspec-retry", "~> 0.6.1" # Accessibility tests @@ -388,7 +389,7 @@ end gem "bootsnap", "~> 1.23.0", require: false # API gems -gem "grape", "~> 2.4.0" +gem "grape", "~> 3.1.1" gem "grape_logging", "~> 3.0.0" gem "roar", "~> 1.2.0" diff --git a/Gemfile.lock b/Gemfile.lock index 44fd5a7f313..238a837bd6f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -213,6 +213,11 @@ PATH specs: openproject-webhooks (1.0.0) +PATH + remote: modules/wikis + specs: + openproject-wikis (1.0.0) + PATH remote: modules/xls_export specs: @@ -223,7 +228,7 @@ GEM remote: https://rubygems.org/ specs: Ascii85 (2.0.1) - action_text-trix (2.1.16) + action_text-trix (2.1.17) railties actioncable (8.1.2) actionpack (= 8.1.2) @@ -321,7 +326,7 @@ GEM activesupport (>= 6.1) acts_as_tree (2.9.1) activerecord (>= 3.0.0) - addressable (2.8.8) + addressable (2.8.9) public_suffix (>= 2.0.2, < 8.0) aes_key_wrap (1.1.0) afm (1.0.0) @@ -332,7 +337,7 @@ GEM android_key_attestation (0.3.0) anyway_config (2.8.0) ruby-next-core (~> 1.0) - appsignal (4.8.2) + appsignal (4.8.3) logger rack (>= 2.0.0) ast (2.4.3) @@ -342,7 +347,7 @@ GEM awesome_nested_set (3.9.0) activerecord (>= 4.0.0, < 8.2) aws-eventstream (1.4.0) - aws-partitions (1.1213.0) + aws-partitions (1.1221.0) aws-sdk-core (3.242.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -351,10 +356,10 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.121.0) + aws-sdk-kms (1.122.0) aws-sdk-core (~> 3, >= 3.241.4) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.213.0) + aws-sdk-s3 (1.214.0) aws-sdk-core (~> 3, >= 3.241.4) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -390,7 +395,7 @@ GEM bindata (2.5.1) bootsnap (1.23.0) msgpack (~> 1.2) - brakeman (8.0.2) + brakeman (8.0.4) racc browser (6.2.0) builder (3.3.0) @@ -405,23 +410,26 @@ GEM rack-test (>= 0.6.3) regexp_parser (>= 1.5, < 3.0) xpath (~> 3.2) - capybara-screenshot (1.0.26) + capybara-screenshot (1.0.27) capybara (>= 1.0, < 4) launchy - carrierwave (1.3.4) - activemodel (>= 4.0.0) - activesupport (>= 4.0.0) - mime-types (>= 1.16) - ssrf_filter (~> 1.0, < 1.1.0) - carrierwave_direct (2.1.0) - carrierwave (>= 1.0.0) + carrierwave (2.2.6) + activemodel (>= 5.0.0) + activesupport (>= 5.0.0) + addressable (~> 2.6) + image_processing (~> 1.1) + marcel (~> 1.0.0) + mini_mime (>= 0.1.3) + ssrf_filter (~> 1.0) + carrierwave_direct (3.0.0) + carrierwave (>= 2.2.0) fog-aws cbor (0.5.10.1) cgi (0.5.1) childprocess (5.1.0) logger (~> 1.5) climate_control (1.2.0) - closure_tree (9.5.0) + closure_tree (9.6.1) activerecord (>= 7.2.0) with_advisory_lock (>= 7.5.0) zeitwerk (~> 2.7) @@ -444,21 +452,21 @@ GEM cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - counter_culture (3.12.1) + counter_culture (3.12.2) activerecord (>= 4.2) activesupport (>= 4.2) crack (1.0.1) bigdecimal rexml crass (1.0.6) - css_parser (1.21.1) + css_parser (2.0.0) addressable csv (3.3.5) cuprite (0.17) capybara (~> 3.0) ferrum (~> 0.17.0) daemons (1.4.1) - dalli (5.0.0) + dalli (5.0.2) logger date (3.5.1) date_validator (0.12.0) @@ -476,7 +484,7 @@ GEM disposable (0.6.3) declarative (>= 0.0.9, < 1.0.0) representable (>= 3.1.1, < 4) - doorkeeper (5.8.2) + doorkeeper (5.9.0) railties (>= 5) dotenv (3.2.0) dotenv-rails (3.2.0) @@ -503,13 +511,13 @@ GEM concurrent-ruby (~> 1.0) dry-core (~> 1.1) zeitwerk (~> 2.6) - dry-schema (1.15.0) + dry-schema (1.16.0) concurrent-ruby (~> 1.0) dry-configurable (~> 1.0, >= 1.0.1) dry-core (~> 1.1) dry-initializer (~> 3.2) dry-logic (~> 1.6) - dry-types (~> 1.8) + dry-types (~> 1.9, >= 1.9.1) zeitwerk (~> 2.6) dry-types (1.9.1) bigdecimal (>= 3.0) @@ -555,7 +563,7 @@ GEM tzinfo eventmachine (1.2.7) eventmachine_httpserver (0.2.1) - excon (1.3.2) + excon (1.4.0) logger factory_bot (6.5.6) activesupport (>= 6.1.0) @@ -570,7 +578,7 @@ GEM faraday (>= 1, < 3) faraday-net_http (3.4.2) net-http (~> 0.5) - fastimage (2.4.0) + fastimage (2.4.1) ferrum (0.17.1) addressable (~> 2.5) base64 (~> 0.2) @@ -616,7 +624,7 @@ GEM glob (0.4.0) globalid (1.3.0) activesupport (>= 6.1) - good_job (4.12.1) + good_job (4.13.3) activejob (>= 6.1.0) activerecord (>= 6.1.0) concurrent-ruby (>= 1.3.1) @@ -637,30 +645,30 @@ GEM base64 (~> 0.2) faraday (>= 1.0, < 3.a) google-logging-utils (0.2.0) - google-protobuf (4.33.5) + google-protobuf (4.34.0) bigdecimal - rake (>= 13) - google-protobuf (4.33.5-aarch64-linux-gnu) + rake (~> 13.3) + google-protobuf (4.34.0-aarch64-linux-gnu) bigdecimal - rake (>= 13) - google-protobuf (4.33.5-aarch64-linux-musl) + rake (~> 13.3) + google-protobuf (4.34.0-aarch64-linux-musl) bigdecimal - rake (>= 13) - google-protobuf (4.33.5-arm64-darwin) + rake (~> 13.3) + google-protobuf (4.34.0-arm64-darwin) bigdecimal - rake (>= 13) - google-protobuf (4.33.5-x86_64-darwin) + rake (~> 13.3) + google-protobuf (4.34.0-x86_64-darwin) bigdecimal - rake (>= 13) - google-protobuf (4.33.5-x86_64-linux-gnu) + rake (~> 13.3) + google-protobuf (4.34.0-x86_64-linux-gnu) bigdecimal - rake (>= 13) - google-protobuf (4.33.5-x86_64-linux-musl) + rake (~> 13.3) + google-protobuf (4.34.0-x86_64-linux-musl) bigdecimal - rake (>= 13) + rake (~> 13.3) googleapis-common-protos-types (1.22.0) google-protobuf (~> 4.26) - googleauth (1.16.1) + googleauth (1.16.2) faraday (>= 1.0, < 3.a) google-cloud-env (~> 2.2) google-logging-utils (~> 0.1) @@ -668,8 +676,9 @@ GEM multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) - grape (2.4.0) - activesupport (>= 6.1) + grape (3.1.1) + activesupport (>= 7.1) + dry-configurable dry-types (>= 1.1) mustermann-grape (~> 1.1.0) rack (>= 2) @@ -691,10 +700,10 @@ GEM htmlbeautifier (1.4.3) htmldiff (0.0.1) htmlentities (4.3.4) - http-2 (1.1.2) + http-2 (1.1.3) http_parser.rb (0.8.1) - httpx (1.6.3) - http-2 (>= 1.0.0) + httpx (1.7.3) + http-2 (>= 1.1.3) i18n (1.14.8) concurrent-ruby (~> 1.0) i18n-js (4.2.4) @@ -719,6 +728,9 @@ GEM ostruct ice_cube (0.17.0) ice_nine (0.11.2) + image_processing (1.14.0) + mini_magick (>= 4.9.5, < 6) + ruby-vips (>= 2.0.17, < 3) inline_svg (1.10.0) activesupport (>= 3.0) nokogiri (>= 1.6) @@ -760,7 +772,7 @@ GEM addressable (~> 2.8) childprocess (~> 5.0) logger (~> 1.6) - lefthook (2.1.1) + lefthook (2.1.4) letter_opener (1.10.0) launchy (>= 2.2, < 4) letter_opener_web (3.0.0) @@ -804,10 +816,10 @@ GEM net-imap net-pop net-smtp - marcel (1.1.0) + marcel (1.0.4) markly (0.15.2) matrix (0.4.3) - mcp (0.7.1) + mcp (0.8.0) json-schema (>= 4.1) messagebird-rest (5.0.0) jwt (< 4) @@ -817,7 +829,7 @@ GEM mime-types (3.7.0) logger mime-types-data (~> 3.2025, >= 3.2025.0507) - mime-types-data (3.2026.0203) + mime-types-data (3.2026.0303) mini_magick (5.3.1) logger mini_mime (1.1.5) @@ -861,7 +873,7 @@ GEM racc (~> 1.4) nokogiri (1.19.1-x86_64-linux-musl) racc (~> 1.4) - oj (3.16.15) + oj (3.16.16) bigdecimal (>= 3.0) ostruct (>= 0.2) okcomputer (1.19.1) @@ -895,7 +907,7 @@ GEM view_component (>= 3.1, < 5.0) openproject-token (8.8.2) activemodel - openssl (4.0.0) + openssl (4.0.1) openssl-signature_algorithm (1.3.0) openssl (> 2.0) opentelemetry-api (1.7.0) @@ -984,7 +996,7 @@ GEM opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-concurrent_ruby (0.24.0) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-dalli (0.29.0) + opentelemetry-instrumentation-dalli (0.29.2) opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-delayed_job (0.25.1) opentelemetry-instrumentation-base (~> 0.25) @@ -994,7 +1006,7 @@ GEM opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-faraday (0.31.0) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-grape (0.5.0) + opentelemetry-instrumentation-grape (0.5.1) opentelemetry-instrumentation-rack (~> 0.29) opentelemetry-instrumentation-graphql (0.31.2) opentelemetry-instrumentation-base (~> 0.25) @@ -1006,7 +1018,7 @@ GEM opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-http_client (0.27.0) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-httpx (0.6.0) + opentelemetry-instrumentation-httpx (0.6.1) opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-koala (0.23.0) opentelemetry-instrumentation-base (~> 0.25) @@ -1076,8 +1088,9 @@ GEM ostruct (0.6.3) ox (2.14.23) bigdecimal (>= 3.0) - pagy (43.2.9) + pagy (43.3.2) json + uri yaml paper_trail (17.0.0) activerecord (>= 7.1) @@ -1085,7 +1098,7 @@ GEM parallel (1.27.0) parallel_tests (4.10.1) parallel - parser (3.3.10.1) + parser (3.3.10.2) ast (~> 2.4.1) racc pdf-core (0.9.0) @@ -1167,7 +1180,7 @@ GEM psych (5.3.1) date stringio - public_suffix (7.0.2) + public_suffix (7.0.5) puffing-billy (4.0.3) addressable (~> 2.5) cgi @@ -1283,7 +1296,7 @@ GEM responders (3.2.0) actionpack (>= 7.0) railties (>= 7.0) - retriable (3.1.2) + retriable (3.2.1) rexml (3.4.4) rinku (2.0.6) roar (1.2.0) @@ -1299,26 +1312,27 @@ GEM rspec-expectations (3.13.5) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.7) + rspec-mocks (3.13.8) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-rails (8.0.3) + rspec-rails (8.0.4) actionpack (>= 7.2) activesupport (>= 7.2) railties (>= 7.2) - rspec-core (~> 3.13) - rspec-expectations (~> 3.13) - rspec-mocks (~> 3.13) - rspec-support (~> 3.13) + rspec-core (>= 3.13.0, < 5.0.0) + rspec-expectations (>= 3.13.0, < 5.0.0) + rspec-mocks (>= 3.13.0, < 5.0.0) + rspec-support (>= 3.13.0, < 5.0.0) rspec-retry (0.6.2) rspec-core (> 3.3) rspec-support (3.13.7) rspec-wait (1.0.2) rspec (>= 3.4) - rubocop (1.84.2) + rubocop (1.85.1) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) + mcp (~> 0.6) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) @@ -1360,13 +1374,17 @@ GEM iso8601 ruby-next-core (1.2.0) ruby-ole (1.2.13.1) - ruby-prof (1.7.2) + ruby-prof (2.0.4) base64 + ostruct ruby-progressbar (1.13.0) ruby-rc4 (0.1.5) ruby-saml (1.18.1) nokogiri (>= 1.13.10) rexml + ruby-vips (2.3.0) + ffi (~> 1.12) + logger ruby2_keywords (0.0.5) rubytree (2.2.0) json (~> 2.0, > 2.9) @@ -1381,7 +1399,7 @@ GEM securerandom (0.4.1) selenium-devtools (0.143.0) selenium-webdriver (~> 4.2) - selenium-webdriver (4.40.0) + selenium-webdriver (4.41.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -1415,7 +1433,7 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - ssrf_filter (1.0.8) + ssrf_filter (1.3.0) stackprof (0.2.28) statesman (13.1.0) store_attribute (2.1.1) @@ -1439,7 +1457,7 @@ GEM thor (1.5.0) thread_safe (0.3.6) timecop (0.9.10) - timeout (0.6.0) + timeout (0.6.1) tpm-key_attestation (0.14.1) bindata (~> 2.4) openssl (> 2.0) @@ -1454,7 +1472,7 @@ GEM turbo-rails (>= 1.3.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - tzinfo-data (1.2025.3) + tzinfo-data (1.2026.1) tzinfo (>= 1.0.0) uber (0.1.0) unicode-display_width (3.2.0) @@ -1469,8 +1487,8 @@ GEM activemodel (>= 3.0.0) public_suffix vcr (6.4.0) - vernier (1.9.0) - view_component (4.4.0) + vernier (1.10.0) + view_component (4.5.0) actionview (>= 7.1.0) activesupport (>= 7.1.0) concurrent-ruby (~> 1) @@ -1510,7 +1528,7 @@ GEM zeitwerk (>= 2.7) xpath (3.2.0) nokogiri (~> 1.8) - yabeda (0.14.0) + yabeda (0.15.0) anyway_config (>= 1.0, < 3) concurrent-ruby dry-initializer @@ -1555,7 +1573,7 @@ DEPENDENCIES activerecord-session_store (~> 2.2.0) acts_as_list (~> 1.2.6) acts_as_tree (~> 2.9.0) - addressable (~> 2.8.0) + addressable (~> 2.8.9) airbrake (~> 13.0.0) appsignal (~> 4.7) auto_strip_attributes (~> 2.5) @@ -1572,10 +1590,10 @@ DEPENDENCIES capybara (~> 3.40.0) capybara-screenshot (~> 1.0.17) capybara_accessible_selectors! - carrierwave (~> 1.3.4) - carrierwave_direct (~> 2.1.0) + carrierwave (~> 2.2.6) + carrierwave_direct (~> 3.0.0) climate_control - closure_tree (~> 9.5.0) + closure_tree (~> 9.6.1) colored2 commonmarker (~> 2.6.0) compare-xml (~> 0.66) @@ -1590,7 +1608,7 @@ DEPENDENCIES deckar01-task_list (~> 2.3.1) dentaku (~> 3.5) disposable (~> 0.6.2) - doorkeeper (~> 5.8.0) + doorkeeper (~> 5.9.0) dotenv-rails dry-container dry-monads @@ -1608,15 +1626,15 @@ DEPENDENCIES friendly_id (~> 5.6.0) fuubar (~> 2.5.0) globalid (~> 1.3) - good_job (~> 4.12.0) + good_job (~> 4.13.3) google-apis-gmail_v1 googleauth - grape (~> 2.4.0) + grape (~> 3.1.1) grape_logging (~> 3.0.0) grids! html-pipeline (~> 2.14.0) htmldiff - httpx (~> 1.6.3) + httpx (~> 1.7.3) i18n-js (~> 4.2.4) i18n-tasks (~> 1.1.0) ice_cube (~> 0.17.0) @@ -1635,7 +1653,7 @@ DEPENDENCIES mail (= 2.9.0) markly (~> 0.15) matrix (~> 0.4.3) - mcp (~> 0.7.0) + mcp (~> 0.8.0) md_to_pdf! meta-tags (~> 2.22.3) mini_magick (~> 5.3.0) @@ -1643,7 +1661,7 @@ DEPENDENCIES my_page! net-ldap (~> 0.20.0) nokogiri (~> 1.19.1) - oj (~> 3.16.12) + oj (~> 3.16.16) okcomputer (~> 1.19.1) omniauth! omniauth-openid-connect! @@ -1674,6 +1692,7 @@ DEPENDENCIES openproject-token (~> 8.8.2) openproject-two_factor_authentication! openproject-webhooks! + openproject-wikis! openproject-xls_export! opentelemetry-exporter-otlp (~> 0.31.0) opentelemetry-instrumentation-all (~> 0.90.0) @@ -1708,12 +1727,12 @@ DEPENDENCIES redis (~> 5.4.0) request_store (~> 1.7.0) responders (~> 3.2) - retriable (~> 3.1.1) + retriable (~> 3.2.1) rinku (~> 2.0.4) roar (~> 1.2.0) rouge (~> 4.7.0) rspec (~> 3.13.2) - rspec-rails (~> 8.0.3) + rspec-rails (~> 8.0.4) rspec-retry (~> 0.6.1) rspec-wait rubocop @@ -1740,6 +1759,7 @@ DEPENDENCIES spring-commands-rubocop sprockets (~> 3.7.2) sprockets-rails (~> 3.5.1) + ssrf_filter (~> 1.3) stackprof statesman (~> 13.1.0) store_attribute (~> 2.0) @@ -1754,11 +1774,11 @@ DEPENDENCIES turbo-rails (~> 2.0.20) turbo_power (~> 0.7.0) turbo_tests! - tzinfo-data (~> 1.2025.1) + tzinfo-data (~> 1.2026.1) validate_url vcr vernier - view_component (~> 4.4.0) + view_component (~> 4.5.0) warden (~> 1.2) warden-basic_auth (~> 0.2.1) webmock (~> 3.26) @@ -1771,7 +1791,7 @@ DEPENDENCIES CHECKSUMS Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a - action_text-trix (2.1.16) sha256=f645a2c21821b8449fd1d6770708f4031c91a2eedf9ef476e9be93c64e703a8a + action_text-trix (2.1.17) sha256=b44691639d77e67169dc054ceacd1edc04d44dc3e4c6a427aa155a2beb4cc951 actioncable (8.1.2) sha256=dc31efc34cca9cdefc5c691ddb8b4b214c0ea5cd1372108cbc1377767fb91969 actionmailbox (8.1.2) sha256=058b2fb1980e5d5a894f675475fcfa45c62631103d5a2596d9610ec81581889b actionmailer (8.1.2) sha256=f4c1d2060f653bfe908aa7fdc5a61c0e5279670de992146582f2e36f8b9175e9 @@ -1791,23 +1811,23 @@ CHECKSUMS 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 + addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485 aes_key_wrap (1.1.0) sha256=b935f4756b37375895db45669e79dfcdc0f7901e12d4e08974d5540c8e0776a5 afm (1.0.0) sha256=5bd4d6f6241e7014ef090985ec6f4c3e9745f6de0828ddd58bc1efdd138f4545 airbrake (13.0.5) sha256=901f5074c25d5ef77ed87f5bde7a28400a7324f5d7013a8a12d07e0099cc31b6 airbrake-ruby (6.2.2) sha256=293e34fb36e763e1b6d67ab584cce7c5b6fe9eea1a70c26d8c13c0f5d7de2fbc android_key_attestation (0.3.0) sha256=467eb01a99d2bb48ef9cf24cc13712669d7056cba5a52d009554ff037560570b anyway_config (2.8.0) sha256=f6797a7231f81202dcd3d0c07284e836e45713e761d320180348b13a5c7c9306 - appsignal (4.8.2) sha256=88cf3229a451a6501da55e221367991788f5621d319823b471f271e07cc92ca5 + appsignal (4.8.3) sha256=aa0ea5ffd39fe7530c56a6eb6efda60825ab061ef31376126cae93b009844dd7 ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 attr_required (1.0.2) sha256=f0ebfc56b35e874f4d0ae799066dbc1f81efefe2364ca3803dc9ea6a4de6cb99 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.1213.0) sha256=5ec132d91d44ef2702125b8f71f0e4fc2cd7de040e02c5d0aefb87219fd2e05e + aws-partitions (1.1221.0) sha256=f09304480191f5ff03f8994705067779bc8fe5b4731183ce45f092afb706e8eb aws-sdk-core (3.242.0) sha256=c17b3003acc78d80c1a8437b285a1cfc5e4d7749ce7821cf3071e847535a29a0 - aws-sdk-kms (1.121.0) sha256=d563c1cfb4b5754efbc671216c8eca875338748adad0f42518c28dfa0a2d01e0 - aws-sdk-s3 (1.213.0) sha256=af596ccf544582406db610e95cc9099276eaf03142f57a2f30f76940e598e50d + aws-sdk-kms (1.122.0) sha256=47ce3f51b26bd7d76f1270cfdfca17b40073ecd3219c8c9400788712abfb4eb8 + aws-sdk-s3 (1.214.0) sha256=923135327634c873ecedbd8f396ea9939874d524b14fa65eced766660c8dd62e aws-sdk-sns (1.112.0) sha256=aff1b1b5bbcb4229599221c558a41790c1cd1a1fed47ac3d27d27512ad24b254 aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 axe-core-api (4.11.1) sha256=a6460506449a692030620a0574fee7afa6cd38cfbbf6620d20bf4d53d33a80cc @@ -1820,21 +1840,21 @@ CHECKSUMS bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218 bindata (2.5.1) sha256=53186a1ec2da943d4cb413583d680644eb810aacbf8902497aac8f191fad9e58 bootsnap (1.23.0) sha256=c1254f458d58558b58be0f8eb8f6eec2821456785b7cdd1e16248e2020d3f214 - brakeman (8.0.2) sha256=7b02065ce8b1de93949cefd3f2ad78e8eb370e644b95c8556a32a912a782426a + brakeman (8.0.4) sha256=7bf921fa9638544835df9aa7b3e720a9a72c0267f34f92135955edd80d4dcf6f browser (6.2.0) sha256=281d5295788825c9396427c292c2d2be0a5c91875c93c390fde6e5d61a5ace2d budgets (1.0.0) builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f byebug (13.0.0) sha256=d2263efe751941ca520fa29744b71972d39cbc41839496706f5d9b22e92ae05d capybara (3.40.0) sha256=42dba720578ea1ca65fd7a41d163dd368502c191804558f6e0f71b391054aeef - capybara-screenshot (1.0.26) sha256=816b9370a07752097c82a05f568aaf5d3b7f45c3db5d3aab2014071e1b3c0c77 + capybara-screenshot (1.0.27) sha256=afa1896cc23df77be1774e8d3b3ce3953bf060aeaa04ff87607b5daf689174f2 capybara_accessible_selectors (0.15.0) - carrierwave (1.3.4) sha256=81772dabd1830edbd7f4526d2ae2c79f974f1d48900c3f03f7ecb7c657463a21 - carrierwave_direct (2.1.0) sha256=b0d5c19c1d17a05940e488cff644a3b2946422d6d494b2174b48f6a90c5dddbf + carrierwave (2.2.6) sha256=cd9b6108fc7544e97e7fbcc561bd319a09f23c96816fdd0df8f2f45ffdc0dac3 + carrierwave_direct (3.0.0) sha256=da4105ec7beea2687817b95ad95181be3d657248ec1cb5a0e5c35a45b176fc2f cbor (0.5.10.1) sha256=79cdf79f18dcd9ee97e0b849c6d573e5a2e3ddc1954d180f384d6ed2612b6df0 cgi (0.5.1) sha256=e93fcafc69b8a934fe1e6146121fa35430efa8b4a4047c4893764067036f18e9 childprocess (5.1.0) sha256=9a8d484be2fd4096a0e90a0cd3e449a05bc3aa33f8ac9e4d6dcef6ac1455b6ec climate_control (1.2.0) sha256=36b21896193fa8c8536fa1cd843a07cf8ddbd03aaba43665e26c53ec1bd70aa5 - closure_tree (9.5.0) sha256=1ae7d32f1a03ac13dee152bd1185a0f509c26dca7f2e5a234867576de5a96b5b + closure_tree (9.6.1) sha256=f6af11243dea13d888788ffb0fd28014bd1077abe3a4233ea1e7044e52fc6377 coderay (1.1.3) sha256=dc530018a4684512f8f38143cd2a096c9f02a1fc2459edcfe534787a7fc77d4b coercible (1.0.0) sha256=5081ad24352cc8435ce5472bc2faa30260c7ea7f2102cc6a9f167c4d9bffaadc color_conversion (0.1.2) sha256=99bea5fa412e1527a11389975aa6ad445ff8528ebae202c11d08c45ea2b94c96 @@ -1851,14 +1871,14 @@ CHECKSUMS cookiejar (0.3.4) sha256=11b16acfc4baf7a0f463c21a6212005e04e25f5554d4d9f24d97f3492dfda0df cose (1.3.1) sha256=d5d4dbcd6b035d513edc4e1ab9bc10e9ce13b4011c96e3d1b8fe5e6413fd6de5 costs (1.0.0) - counter_culture (3.12.1) sha256=8a880092321941732bfa639c449a133ff08c5197c29e853371cc03c486e43980 + counter_culture (3.12.2) sha256=26358dbb15e2f1f7e60f2c11976e37517a74ffd34eb5ef8d7269862b9ff8aadd crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d - css_parser (1.21.1) sha256=6cfd3ffc0a97333b39d2b1b49c95397b05e0e3b684d68f77ec471ba4ec2ef7c7 + css_parser (2.0.0) sha256=af5c759a127b125b635006a6c6c2e05b96a1ebdeec21b3c415fd5f09ec714a0a csv (3.3.5) sha256=6e5134ac3383ef728b7f02725d9872934f523cb40b961479f69cf3afa6c8e73f cuprite (0.17) sha256=b140d5dc70d08b97ad54bcf45cd95d0bd430e291e9dffe76fff851fddd57c12b daemons (1.4.1) sha256=8fc76d76faec669feb5e455d72f35bd4c46dc6735e28c420afb822fac1fa9a1d - dalli (5.0.0) sha256=7d5ae821da3ef98cad68e77f09ae0e563244dc0f3995f5c8999c5a379fa3d468 + dalli (5.0.2) sha256=818469227b9acdd9da3fc65ec5ae75d4020115545879e5e8f95634085e9a8749 date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0 date_validator (0.12.0) sha256=68c9834da240347b9c17441c553a183572508617ebfbe8c020020f3192ce3058 deckar01-task_list (2.3.4) sha256=66abdc7e009ea759732bb53867e1ea42de550e2aa03ac30a015cbf42a04c1667 @@ -1867,7 +1887,7 @@ CHECKSUMS descendants_tracker (0.0.4) sha256=e9c41dd4cfbb85829a9301ea7e7c48c2a03b26f09319db230e6479ccdc780897 diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962 disposable (0.6.3) sha256=7f2a3fb251bff6cd83f25b164043d4ec3531209b51b066ed476a9df9c2d384cc - doorkeeper (5.8.2) sha256=a73d07aeaf590b1e7e2a35390446f23131c9f37bc0561653e514d3973f4d50d3 + doorkeeper (5.9.0) sha256=edad053b3dcb6bf51e3181fc423333e7fba28863beb2122379643ce6463b6336 dotenv (3.2.0) sha256=e375b83121ea7ca4ce20f214740076129ab8514cd81378161f11c03853fe619d dotenv-rails (3.2.0) sha256=657e25554ba622ffc95d8c4f1670286510f47f2edda9f68293c3f661b303beab drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373 @@ -1878,7 +1898,7 @@ CHECKSUMS dry-initializer (3.2.0) sha256=37d59798f912dc0a1efe14a4db4a9306989007b302dcd5f25d0a2a20c166c4e3 dry-logic (1.6.0) sha256=da6fedbc0f90fc41f9b0cc7e6f05f5d529d1efaef6c8dcc8e0733f685745cea2 dry-monads (1.9.0) sha256=9348a67b5c862c7a876342dbd94737fdf3fb3c17978382cf6801a85b27215816 - dry-schema (1.15.0) sha256=0f2a34adba4206bd6d46ec1b6b7691b402e198eecaff1d8349a7d48a77d82cd2 + dry-schema (1.16.0) sha256=cd3aaeabc0f1af66ec82a29096d4c4fb92a0a58b9dae29a22b1bbceb78985727 dry-types (1.9.1) sha256=baebeecdb9f8395d6c9d227b62011279440943e3ef2468fe8ccc1ba11467f178 dry-validation (1.11.1) sha256=70900bb5a2d911c8aab566d3e360c6bff389b8bf92ea8e04885ce51c41ff8085 dumb_delegator (1.1.0) sha256=1ad255e5b095a2206a574c62b40c678f3d5c9151f1b3d0bae1b0463f7e40188e @@ -1895,13 +1915,13 @@ CHECKSUMS et-orbi (1.4.0) sha256=6c7e3c90779821f9e3b324c5e96fda9767f72995d6ae435b96678a4f3e2de8bc eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972 eventmachine_httpserver (0.2.1) sha256=5db5e8a23754204d43592e5fcc2160457c57c870babe6307c4e61fc95019b809 - excon (1.3.2) sha256=a089babe98638e58042a7d542b2bbd183304527e33d612b6dde22fa491a544a5 + excon (1.4.0) sha256=5d2bc9d2c79511a562e7fcac77cc7a40acd9cebcc55b80e537975ad8187f2924 factory_bot (6.5.6) sha256=12beb373214dccc086a7a63763d6718c49769d5606f0501e0a4442676917e077 factory_bot_rails (6.5.1) sha256=d3cc4851eae4dea8a665ec4a4516895045e710554d2b5ac9e68b94d351bc6d68 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 + fastimage (2.4.1) sha256=c64bebd46b6fd8943ab70c1e6e85ff728f970f2e48f92ecd249b6bc3a540ad20 ferrum (0.17.1) sha256=51d591120fc593e5a13b5d9d6474389f5145bb92a91e36eab147b5d096c8cbe7 ffi (1.17.3-aarch64-linux-gnu) sha256=28ad573df26560f0aedd8a90c3371279a0b2bd0b4e834b16a2baa10bd7a97068 ffi (1.17.3-aarch64-linux-musl) sha256=020b33b76775b1abacc3b7d86b287cef3251f66d747092deec592c7f5df764b2 @@ -1923,21 +1943,21 @@ CHECKSUMS fuubar (2.5.1) sha256=b272a7804b282661c7fab583a3764f92543cb482c365ae39c685cd218fdd4880 glob (0.4.0) sha256=893dc9e2d24abe13dda907ce0cda576f680ff382f2a6cf9e543f98ecbe29238c globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11 - good_job (4.12.1) sha256=a8a981baf2ac9c40a85412d92ce2bf7a5e6d35d9dadb0900db57780583eafebb + good_job (4.13.3) sha256=37478710dddd2630ed055f2159111ce6f7f14482d8ccb412142ee04674151e2e google-apis-core (1.0.2) sha256=ba4579aaadc902d6cc7bc8db88f566ab00f5e31ea87ab41e9f9a032c470f2629 google-apis-gmail_v1 (0.47.0) sha256=3064434b6da55b85e2828ce4bb0f4d04e8cfd187a4ab262ceb1dcb01f98e49ef google-cloud-env (2.3.1) sha256=0faac01eb27be78c2591d64433663b1a114f8f7af55a4f819755426cac9178e7 google-logging-utils (0.2.0) sha256=675462b4ea5affa825a3442694ca2d75d0069455a1d0956127207498fca3df7b - google-protobuf (4.33.5) sha256=1b64fb774c101b23ac3f6923eca24be04fd971635d235c4cd4cfe0d752620da0 - google-protobuf (4.33.5-aarch64-linux-gnu) sha256=f70ca066e37a7ac60b4f34a836bb48ca3fc41a9371310052e484d8c9f925ff39 - google-protobuf (4.33.5-aarch64-linux-musl) sha256=d9ae90025f05db642e5603de5dbb2390cd1215bac7507fa575cc20b0db7e11a1 - google-protobuf (4.33.5-arm64-darwin) sha256=996d4e93c4232cc42f0facd821a92b4f4a926c3c9c1a768e7d768b33d9ef72f9 - google-protobuf (4.33.5-x86_64-darwin) sha256=173d1d6c9f0de93fd9ee25fde172d6fb6376099dca8844e19bc5782bbc7b93b0 - google-protobuf (4.33.5-x86_64-linux-gnu) sha256=a782adf86bfba207740b49d7bb9ccdc25c4fb8f800fe222af62bce951149338a - google-protobuf (4.33.5-x86_64-linux-musl) sha256=d14feec9118f44cfdc3ee4a1d1baa4e6dd77fa418967ccf22ecbe76b8c1bacbf + google-protobuf (4.34.0) sha256=bffaea30fbe2807c80667a78953b15645b3bef62b25c10ca187e4418119be531 + google-protobuf (4.34.0-aarch64-linux-gnu) sha256=0ab8a8a97976a2265d647e69b3ff1980c89184abdaf06d36091856c5ab37cc55 + google-protobuf (4.34.0-aarch64-linux-musl) sha256=0632a86df6d320eac3b335bd779499d43ad8ee6d1f8c8494b773ed5d3d5c6ab4 + google-protobuf (4.34.0-arm64-darwin) sha256=f83967a8095a9da676b79ba372c58fef2ca3878428bd40febfce65b3752c90d1 + google-protobuf (4.34.0-x86_64-darwin) sha256=4a5b67281993345adca54bb32947f25a289597eafaa240e5b714d0a740f99321 + google-protobuf (4.34.0-x86_64-linux-gnu) sha256=bbb333fbe79c16f35a2e2154cf29f3ce26f60390dba286b339861206d5435ef9 + google-protobuf (4.34.0-x86_64-linux-musl) sha256=0b75858a388b17e73aa4176df2e722762dbc92551b7075fdc562d33c1c6de0b0 googleapis-common-protos-types (1.22.0) sha256=f97492b77bd6da0018c860d5004f512fe7cd165554d7019a8f4df6a56fbfc4c7 - googleauth (1.16.1) sha256=36776bce9d55d8c1a0c6638c939b000dcee5954ca5b728f06ec4c2df4a46709c - grape (2.4.0) sha256=3d59673e80f11d49ba86270b78344e5348dc057b318c2bbc1c01f3532f9b6aec + googleauth (1.16.2) sha256=15009502e2e38af71948cda918f230e27d327f6882a1e47967a5a4664930a638 + grape (3.1.1) sha256=774f16782d917a90e69de0499dfaab571e5ad967569ac066a2b0b918af12de69 grape_logging (3.0.0) sha256=7b62d984ce96df15d120508668debe307e6a59ac1c511f1d9b5f3b4bea793e13 gravatar_image_tag (1.2.0) sha256=eb5630fea846b711e713b934a0178fb9785f02f4eb9ced8d6faa4d537c40fdcf grids (1.0.0) @@ -1950,15 +1970,16 @@ CHECKSUMS htmlbeautifier (1.4.3) sha256=b43d08f7e2aa6ae1b5a6f0607b4ed8954c8d4a8e85fd2336f975dda1e4db385b htmldiff (0.0.1) sha256=a96d068c8b8ea96cca3154a61785365d83129f47f1df964c6b1666924ce113a1 htmlentities (4.3.4) sha256=125a73c6c9f2d1b62100b7c3c401e3624441b663762afa7fe428476435a673da - http-2 (1.1.2) sha256=8c1cb40203fc60b521c491509fee96c7b08092975eb30036465992433715ed40 + http-2 (1.1.3) sha256=1b2f379d35a11dbae94f8a1a52c053d8c161eb4a0c98b5d1605ff1b2bf171c9c http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a - httpx (1.6.3) sha256=1b4a11b9572b78839f649ad6ebbe09b5f467cc458e0ee408bddc902b273a2e8a + httpx (1.7.3) sha256=126914109a58350e5ad0c13786092f35e2419857dad15745f31fc81c371f93a8 i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 i18n-js (4.2.4) sha256=61390d372f8fa68c495c5907d577657e8cc3a7031f4945db1e91f935e1391355 i18n-tasks (1.1.2) sha256=4dcfba49e52a623f30661cb316cb80d84fbba5cb8c6d88ef5e02545fffa3637a icalendar (2.12.1) sha256=ecff56c550aed551f29ad1faad0da54bf62362dfaf22a428bd7ad782938fe764 ice_cube (0.17.0) sha256=32deb45dda4b4acc53505c2f581f6d32b5afc04d29b9004769944a0df5a5fcbe ice_nine (0.11.2) sha256=5d506a7d2723d5592dc121b9928e4931742730131f22a1a37649df1c1e2e63db + image_processing (1.14.0) sha256=754cc169c9c262980889bec6bfd325ed1dafad34f85242b5a07b60af004742fb inline_svg (1.10.0) sha256=5b652934236fd9f8adc61f3fd6e208b7ca3282698b19f28659971da84bf9a10f interception (0.5) sha256=a53818d636752a8df90d8c1bb2f7b6e13a7b828543cb02b50fbde98b849d7907 io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc @@ -1975,7 +1996,7 @@ CHECKSUMS ladle (1.0.1) sha256=e8586964108c798d48bf57d2a65bd5602e8e5223a176b6602a0fb36c0bda90dc language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc launchy (3.1.1) sha256=72b847b5cc961589dde2c395af0108c86ff0119f42d4648d25b5440ebb10059e - lefthook (2.1.1) sha256=1b4ce49fbadb3f6584c07daaa9164e560e27bb5aba18446bb9de2378fcf3f5b6 + lefthook (2.1.4) sha256=b3c5bba86911e85b239fea3861ba8c74740fc084ba9ac79dba3fe79267572d6a letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2 letter_opener_web (3.0.0) sha256=3f391efe0e8b9b24becfab5537dfb17a5cf5eb532038f947daab58cb4b749860 lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 @@ -1986,16 +2007,16 @@ CHECKSUMS loofah (2.25.0) sha256=df5ed7ac3bac6a4ec802df3877ee5cc86d027299f8952e6243b3dac446b060e6 lookbook (2.3.14) sha256=c11a693bde9915b553c4463440ad5e750829f90bff08abdb6b8610373864cd7c mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941 - marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee + marcel (1.0.4) sha256=0d5649feb64b8f19f3d3468b96c680bae9746335d02194270287868a661516a4 markly (0.15.2) sha256=65dae965d4dd4ecd997fba43b93acc0fe7dadfec6f07a748640c7a9299a8551e matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b - mcp (0.7.1) sha256=fa967895d6952bad0d981ea907731d8528d2c246d2079d56a9c8bae83d14f1c7 + mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb md_to_pdf (0.2.5) messagebird-rest (5.0.0) sha256=da4cc1efba3d5e4aa021fad07426c2cb6b326ce5670da5104bb8f6056a39d59c 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.2026.0203) sha256=54353d693af028847391c28361c07d4b8b689cad78c3e1cc272fb1205c6d2a2f + mime-types-data (3.2026.0303) sha256=164af1de5824c5195d4b503b0a62062383b65c08671c792412450cd22d3bc224 mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d @@ -2019,7 +2040,7 @@ CHECKSUMS nokogiri (1.19.1-x86_64-darwin) sha256=7093896778cc03efb74b85f915a775862730e887f2e58d6921e3fa3d981e68bf nokogiri (1.19.1-x86_64-linux-gnu) sha256=1a4902842a186b4f901078e692d12257678e6133858d0566152fe29cdb98456a nokogiri (1.19.1-x86_64-linux-musl) sha256=4267f38ad4fc7e52a2e7ee28ed494e8f9d8eb4f4b3320901d55981c7b995fc23 - oj (3.16.15) sha256=4d3324cac3e8fef54c0fa250b2af26a16dadd9f9788a1d6b1b2098b793a1b2cd + oj (3.16.16) sha256=3635b36128991796434f55da8decc0de236a323535adcb36fc04e6d0253c013d okcomputer (1.19.1) sha256=7df770e768434816d228407f0786563827cbf34cb379933578829720cb4f1e77 omniauth (1.9.2) omniauth-openid-connect (0.5.0) @@ -2053,8 +2074,9 @@ CHECKSUMS openproject-token (8.8.2) sha256=081cbff7269d92a82fa1d63e9e09c87b70d47d7aefadcbb80d1e7368bc2cf096 openproject-two_factor_authentication (1.0.0) openproject-webhooks (1.0.0) + openproject-wikis (1.0.0) openproject-xls_export (1.0.0) - openssl (4.0.0) sha256=185711ed93d4e9c9a9db6efea7edb202dfe04f7d3692fbab988e3d84e498ee91 + openssl (4.0.1) sha256=e27974136b7b02894a1bce46c5397ee889afafe704a839446b54dc81cb9c5f7d openssl-signature_algorithm (1.3.0) sha256=a3b40b5e8276162d4a6e50c7c97cdaf1446f9b2c3946a6fa2c14628e0c957e80 opentelemetry-api (1.7.0) sha256=ccfd264ea6f2db5bf4185e3c07a1297977b44a944e2ce65457c4fe63a697214f opentelemetry-common (0.23.0) sha256=da721190479d57bae0ad2207468f47f3e2c3b9a91024b5bc32c9d280183eb32c @@ -2077,18 +2099,18 @@ CHECKSUMS opentelemetry-instrumentation-base (0.25.0) sha256=642a3a7f08354e6e969423327a4fa67ed2cca7ac6fe5ee09e55b17d1c576da27 opentelemetry-instrumentation-bunny (0.24.0) sha256=1ec484e48a5f42a1d0c33e8e6bc7e9e78dd80f3ed9d63520b8a22ba564aa2585 opentelemetry-instrumentation-concurrent_ruby (0.24.0) sha256=229bd8b72000c59de693609bb637b8a9114992f5e0ab03730d7fd7ef91f7d1d2 - opentelemetry-instrumentation-dalli (0.29.0) sha256=a2686650545609e8d7e281c9fd1aef529ab578ef2dcf9a6258737e4ba214bc2f + opentelemetry-instrumentation-dalli (0.29.2) sha256=21b82772ced1529288c7f08285d44d5690de11f3d275e24558a062f39a270f4f opentelemetry-instrumentation-delayed_job (0.25.1) sha256=47f35b10d2bfd9ac7c2bbbe10dea095a2e25db2a84f5351860ead969d180c3ec opentelemetry-instrumentation-ethon (0.27.0) sha256=bfd2e34a5f34c7114727b0e0c9d441a1c6c7a4cceb8374d90ae9332f009f3968 opentelemetry-instrumentation-excon (0.27.0) sha256=3d7e6e160f0328e1136646aabb23efefdb125854637d5bd57b849720f783b5bb opentelemetry-instrumentation-faraday (0.31.0) sha256=1c00dc96d4c18890a34a20eef27eae536bf6558965e03e254bb7b84a4f09840b - opentelemetry-instrumentation-grape (0.5.0) sha256=b9fcbe13b015b663577b8bde5b419c297da2588d0a022f4ce40f9ffc49df7624 + opentelemetry-instrumentation-grape (0.5.1) sha256=a623608ef10e96c413f4d50b840082bf1ab9700126185d89ddbc8a29b49ec0ef opentelemetry-instrumentation-graphql (0.31.2) sha256=a4455f225427f8f9058247c8c0b351b8932567913c35ef049f7958801d401b1f opentelemetry-instrumentation-grpc (0.4.1) sha256=5ffa2bb1d5ec69bcd1fe23e1d8c1a563a00351ce052fe9d76885cc43f21ebc87 opentelemetry-instrumentation-gruf (0.5.0) sha256=ee21be36e312e71b847c9a87168225625890121140a364b68d3668e0df58dacd opentelemetry-instrumentation-http (0.28.0) sha256=0946a9593d64740780d16e041f9441945c4970b17566ce98e1a63bd3b276cacd opentelemetry-instrumentation-http_client (0.27.0) sha256=6a639a21296d3b6d3934b2d5465bee5d5f9180208d0329a5bbe34b374030690e - opentelemetry-instrumentation-httpx (0.6.0) sha256=a4ee129cb7d5ea690565585b6a5c7a1d181a1e239c5360ec4554c15ce2b3fd6d + opentelemetry-instrumentation-httpx (0.6.1) sha256=9050801826bb0f148f603b39b119c4f49591955ee53d3d0b2a8fb1eb8f558022 opentelemetry-instrumentation-koala (0.23.0) sha256=8f324b50a2a64fd4994bb2b105a4cb0c80b64ec05cf5487d2daa906c650bc6f9 opentelemetry-instrumentation-lmdb (0.25.0) sha256=1e4d66d583ea242d4f72051062971f5af1ea353484d224abbd0aabdd1ce5f5cb opentelemetry-instrumentation-mongo (0.25.0) sha256=d04585669f928ea82e7c469f996061d39d8ff184278d57cf4fc77a6d607f9c7a @@ -2116,11 +2138,11 @@ CHECKSUMS ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 overviews (1.0.0) ox (2.14.23) sha256=4a9aedb4d6c78c5ebac1d7287dc7cc6808e14a8831d7adb727438f6a1b461b66 - pagy (43.2.9) sha256=6a167c8da72388883e8f4c8e29dc8f5577f9f934d8d0b704cb2f39d2cebd276e + pagy (43.3.2) sha256=105683fccb86c1011a96c76da610c35c64aaf4cf412165606aadcb39e733e830 paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85 parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 parallel_tests (4.10.1) sha256=df05458c691462b210f7a41fc2651d4e4e8a881e8190e6d1e122c92c07735d70 - parser (3.3.10.1) sha256=06f6a725d2cd91e5e7f2b7c32ba143631e1f7c8ae2fb918fc4cebec187e6a688 + parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357 pdf-core (0.9.0) sha256=4f368b2f12b57ec979872d4bf4bd1a67e8648e0c81ab89801431d2fc89f4e0bb pdf-inspector (1.3.0) sha256=fc107579d6f29b636e2da3d6743479b2624d9e390bf2d84beef8fd4ebe1a05bd pdf-reader (2.15.1) sha256=18c6a986a84a3117fa49f4279fc2de51f5d2399b71833df5d2bccd595c7068ce @@ -2149,7 +2171,7 @@ CHECKSUMS pry-rails (0.3.11) sha256=a69e28e24a34d75d1f60bcf241192a54253f8f7ef8a62cba1e75750a9653593d pry-rescue (1.6.0) sha256=985bfd506d9866b587fd86790cf8445266a41b7f92c627fc5b21ec7d92aba6db psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974 - public_suffix (7.0.2) sha256=9114090c8e4e7135c1fd0e7acfea33afaab38101884320c65aaa0ffb8e26a857 + public_suffix (7.0.5) sha256=1a8bb08f1bbea19228d3bed6e5ed908d1cb4f7c2726d18bd9cadf60bc676f623 puffing-billy (4.0.3) sha256=376fe2e2cc3ff9d48814a15153db80970cf0539ba026ac5108c971c2e160883c puma (7.2.0) sha256=bf8ef4ab514a4e6d4554cb4326b2004eba5036ae05cf765cfe51aba9706a72a8 puma-plugin-statsd (2.7.0) sha256=04f243a7233f4d06ec0e26f1a3522bce18a5910ae711763fabff22681bdad08b @@ -2190,7 +2212,7 @@ CHECKSUMS representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb responders (3.2.0) sha256=89c2d6ac0ae16f6458a11524cae4a8efdceba1a3baea164d28ee9046bd3df55a - retriable (3.1.2) sha256=0a5a5d0ca4ba61a76fb31a17ab8f7f80281beb040c329d34dfc137a1398688e0 + retriable (3.2.1) sha256=26e87a33391fae4c382d4750f1e135e4dda7e5aa32b6b71f1992265981f9b991 rexml (3.4.4) sha256=19e0a2c3425dfbf2d4fc1189747bdb2f849b6c5e74180401b15734bc97b5d142 rinku (2.0.6) sha256=8b60670e3143f3db2b37efa262971ce3619ec23092045498ef9f077d82828d7d roar (1.2.0) sha256=8db4d1ca79c57a5fb746c16c0d5661d7c3e0de3d9553dc016a88d2dba2929d08 @@ -2199,12 +2221,12 @@ CHECKSUMS rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587 rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836 - rspec-mocks (3.13.7) sha256=0979034e64b1d7a838aaaddf12bf065ea4dc40ef3d4c39f01f93ae2c66c62b1c - rspec-rails (8.0.3) sha256=b0a440e7a10700317d898a014852e26660867298c4076dbc3baa99c768b79dc1 + rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47 + rspec-rails (8.0.4) sha256=06235692fc0892683d3d34977e081db867434b3a24ae0dd0c6f3516bad4e22df rspec-retry (0.6.2) sha256=6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858 rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c rspec-wait (1.0.2) sha256=865f921239325d3d26fc10ded4bdd485d8b58bcaaad1a28dd85ed15266b5a912 - rubocop (1.84.2) sha256=5692cea54168f3dc8cb79a6fe95c5424b7ea893c707ad7a4307b0585e88dbf5f + rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77 rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c rubocop-factory_bot (2.28.0) sha256=4b17fc02124444173317e131759d195b0d762844a71a29fe8139c1105d92f0cb @@ -2216,10 +2238,11 @@ CHECKSUMS ruby-duration (3.2.3) sha256=eb3d13b1df85067a015a8fb2ed8f1eec842a3b721e47c9b6fd74d2f356069784 ruby-next-core (1.2.0) sha256=f6a7d00bb5186cecbb02f7f1845a0f3a2c9788d35b6ccff5c9be3f0d46799b86 ruby-ole (1.2.13.1) sha256=578d10dd2a797a2b35a1286c6fb2c9525f67c24791346fc8015d39f0ffa3cb72 - ruby-prof (1.7.2) sha256=270424fcac37e611f2d15a55226c4628e234f8434e1d7c25ca8a2155b9fc4340 + ruby-prof (2.0.4) sha256=42190ff6870e055a9d58b39df63e83442113ef7169c0e27b599942c3f15d1435 ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33 ruby-rc4 (0.1.5) sha256=00cc40a39d20b53f5459e7ea006a92cf584e9bc275e2a6f7aa1515510e896c03 ruby-saml (1.18.1) sha256=1b0e7a44aef150b4197955f5e015d593672e242cfdc5d06aa7554ec2350b9107 + ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374 ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef rubytree (2.2.0) sha256=e312dc1ed814153583b57d9662e6caac1fd60830886a571b48bb06daef906eb6 rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615 @@ -2228,7 +2251,7 @@ CHECKSUMS scimitar (2.14.0) sha256=93b29132ebd50d78e61d56c8a17406b1c01857448f2d075560fd6c7660ac9aa9 securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 selenium-devtools (0.143.0) sha256=23e8b364e1074a93a56ea0365ff739022a23a72e9033ad69832400c884417dc4 - selenium-webdriver (4.40.0) sha256=16ef7aa9853c1d4b9d52eac45aafa916e3934c5c83cb4facb03f250adfd15e5b + selenium-webdriver (4.41.0) sha256=cdc1173cd55cf186022cea83156cc2d0bec06d337e039b02ad25d94e41bedd22 semantic (1.6.1) sha256=3cdbb48f59198ebb782a3fdfb87b559e0822a311610db153bae22777a7d0c163 shoulda-context (2.0.0) sha256=7adf45342cd800f507d2a053658cb1cce2884b616b26004d39684b912ea32c34 shoulda-matchers (7.0.1) sha256=b4bfd8744c10e0a36c8ac1a687f921ee7e25ed529e50488d61b79a8688749c77 @@ -2241,7 +2264,7 @@ CHECKSUMS spring-commands-rubocop (0.4.0) sha256=3e677a2c8a27ae8a986f04bfb69e66d5d55b017541e8be93bf0dc48a7f5690c1 sprockets (3.7.5) sha256=72c20f256548f8a37fe7db41d96be86c3262fddaf4ebe9d69ec8317394fed383 sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e - ssrf_filter (1.0.8) sha256=03f49f54837e407d43ee93ec733a8a94dc1bcf8185647ac61606e63aaedaa0db + ssrf_filter (1.3.0) sha256=66882d7de7d09c019098d6d7372412950ae184ebbc7c51478002058307aba6f2 stackprof (0.2.28) sha256=4ec2ace02f386012b40ca20ef80c030ad711831f59511da12e83b34efb0f9a04 statesman (13.1.0) sha256=3ecb78466dfd2682e433f335a7722aa0e5b8c6853d72d83e460151b8af17a84e store_attribute (2.1.1) sha256=de53611c01d3fb4c91e0d7af4208861bd18d017c4cee682ed0473d0e4496f5b8 @@ -2258,7 +2281,7 @@ CHECKSUMS 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 + timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb tpm-key_attestation (0.14.1) sha256=7fd4e4653a7afd0a386632ddfb05d10ecfdd47678299c5e69165bc9ae111193f trailblazer-option (0.1.2) sha256=20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f @@ -2267,7 +2290,7 @@ CHECKSUMS turbo_power (0.7.0) sha256=ad95d147e0fa761d0023ad9ca00528c7b7ddf6bba8ca2e23755d5b21b290d967 turbo_tests (2.2.0) tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b - tzinfo-data (1.2025.3) sha256=b546e2f1e5e5c40a0c619aafb24e30d3d6a128c2f689278f721b7286dd499562 + tzinfo-data (1.2026.1) sha256=4ea36519ae5ae2cf0fad471207a519be006daf42e3b2359ee9e9c53f113609fd uber (0.1.0) sha256=5beeb407ff807b5db994f82fa9ee07cfceaa561dad8af20be880bc67eba935dc unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42 unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f @@ -2276,8 +2299,8 @@ CHECKSUMS validate_email (0.1.6) sha256=9dfe9016d527b17a8d3a6e95e4dc50a125400eef899d13d4cc2a254393f82ee4 validate_url (1.0.15) sha256=72fe164c0713d63a9970bd6700bea948babbfbdcec392f2342b6704042f57451 vcr (6.4.0) sha256=077ac92cc16efc5904eb90492a18153b5e6ca5398046d8a249a7c96a9ea24ae6 - vernier (1.9.0) sha256=3aa6169804144ef27744123525f8f884541185a2eeb53c77afb28ad33c4be3cd - view_component (4.4.0) sha256=ecccecb495ee4b4a5d9f93382992dc32c11a0708fde5ec261150a0f5223393d0 + vernier (1.10.0) sha256=5b1dc57012e08ed23e14f4d2943540140d454aa8434c7c35e7eb97befd4969bf + view_component (4.5.0) sha256=0d951360d830752da4d1daa5f6cb643f17c30f295fb779fd4de90a051537d9c1 virtus (2.0.0) sha256=8841dae4eb7fcc097320ba5ea516bf1839e5d056c61ee27138aa4bddd6e3d1c2 warden (1.2.9) sha256=46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0 warden-basic_auth (0.2.1) sha256=bfc752e0109c0182c3e69e930284c5e1e81e7b4a354aeb2b5914ead1391f3c6e @@ -2291,7 +2314,7 @@ CHECKSUMS will_paginate (4.0.1) sha256=107b226ebe1d393d274575956a7c472e1eefdd97d8828e01b72d425d15a875b9 with_advisory_lock (7.5.0) sha256=dadb2f1ed35a10ed7b9649a6769e6848bc64f735a85eb8a6e162a81d383a15bf xpath (3.2.0) sha256=6dfda79d91bb3b949b947ecc5919f042ef2f399b904013eb3ef6d20dd3a4082e - yabeda (0.14.0) sha256=bc517bf22d692ebd80a29fc9fd2246c257aaf92d10b2735a775e2419351a43bf + yabeda (0.15.0) sha256=81ce61c6e89d42ac3c5855aa2dcddfc2347c33d1595372d59d79e7dda4e72238 yabeda-activerecord (0.1.2) sha256=1dd281a64e5742445a6718aa05e799ea08a397e9ab9c0d254ece447635a3e0e2 yabeda-prometheus-mmap (0.4.0) sha256=1a66120756d6f931f03a7784e08e79060d71681ff83a9f5287df2ff756e9e2c9 yabeda-puma-plugin (0.9.0) sha256=b78673ecc7ee30bc50691ddc41b7022c1c1801843900d5101418f4a14b550bc8 @@ -2301,7 +2324,7 @@ CHECKSUMS zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd RUBY VERSION - ruby 3.4.7p58 + ruby 4.0.1 BUNDLED WITH - 2.7.2 + 4.0.6 diff --git a/Gemfile.modules b/Gemfile.modules index bf1f2f7b5b9..a548efe06a1 100644 --- a/Gemfile.modules +++ b/Gemfile.modules @@ -40,6 +40,7 @@ group :opf_plugins do gem 'openproject-gantt', path: 'modules/gantt' gem 'openproject-calendar', path: 'modules/calendar' gem 'openproject-storages', path: 'modules/storages' + gem 'openproject-wikis', path: 'modules/wikis' gem 'openproject-documents', path: 'modules/documents' gem 'openproject-bim', path: 'modules/bim' diff --git a/app/components/op_primer/copy_to_clipboard_component.rb b/app/components/op_primer/copy_to_clipboard_component.rb index fde52372f4c..18e0153b5f2 100644 --- a/app/components/op_primer/copy_to_clipboard_component.rb +++ b/app/components/op_primer/copy_to_clipboard_component.rb @@ -32,14 +32,25 @@ module OpPrimer class CopyToClipboardComponent < ApplicationComponent include OpPrimer::ComponentHelpers + SCHEME_OPTIONS = %i[value link].freeze + alias_method :value, :model def initialize(value = nil, scheme: :value, **system_arguments) super(value) - @scheme = scheme + @scheme = validate_scheme!(scheme) @system_arguments = system_arguments @id = SecureRandom.hex(8) end + + private + + def validate_scheme!(scheme) + scheme = scheme.to_sym + raise ArgumentError, "scheme must be one of #{SCHEME_OPTIONS}" unless SCHEME_OPTIONS.include?(scheme) + + scheme + end end end diff --git a/app/contracts/work_packages/base_contract.rb b/app/contracts/work_packages/base_contract.rb index b26b1d1c309..21c973d7a51 100644 --- a/app/contracts/work_packages/base_contract.rb +++ b/app/contracts/work_packages/base_contract.rb @@ -46,8 +46,9 @@ module WorkPackages attribute :type_id attribute :priority_id attribute :category_id + # TODO: manage_sprint_items can be removed once the sprint_id is in place. attribute :version_id, - permission: :assign_versions do + permission: %i(assign_versions manage_sprint_items) do validate_version_is_assignable end diff --git a/app/contracts/work_packages/update_contract.rb b/app/contracts/work_packages/update_contract.rb index 5e9a8777548..562bf9eba87 100644 --- a/app/contracts/work_packages/update_contract.rb +++ b/app/contracts/work_packages/update_contract.rb @@ -33,7 +33,8 @@ module WorkPackages include UnchangedProject attribute :lock_version, - permission: %i[edit_work_packages change_work_package_status assign_versions manage_subtasks move_work_packages] do + permission: %i[edit_work_packages change_work_package_status assign_versions manage_sprint_items manage_subtasks + move_work_packages] do if model.lock_version.nil? || model.lock_version_changed? errors.add :base, :error_conflict end @@ -60,6 +61,7 @@ module WorkPackages with_unchanged_project_id do next if allowed_in_work_package?(:edit_work_packages) || allowed_in_project?(:assign_versions) || + allowed_in_project?(:manage_sprint_items) || allowed_in_project?(:change_work_package_status) || allowed_in_project?(:manage_subtasks) || allowed_in_project?(:move_work_packages) diff --git a/app/controllers/custom_styles_controller.rb b/app/controllers/custom_styles_controller.rb index e35c1880fd0..301fbca287f 100644 --- a/app/controllers/custom_styles_controller.rb +++ b/app/controllers/custom_styles_controller.rb @@ -249,7 +249,7 @@ class CustomStylesController < ApplicationController def file_download(path_method) @custom_style = CustomStyle.current - if @custom_style && @custom_style.send(path_method) + if @custom_style&.send(path_method) expires_in 1.year, public: true, must_revalidate: false send_file(@custom_style.send(path_method)) else @@ -263,7 +263,7 @@ class CustomStylesController < ApplicationController return render_404 end - @custom_style.send(remove_method) + @custom_style.send("#{remove_method}!") redirect_to custom_style_path, status: :see_other end end diff --git a/app/controllers/projects/creation_wizard_controller.rb b/app/controllers/projects/creation_wizard_controller.rb index b5c9f669b27..f444065596b 100644 --- a/app/controllers/projects/creation_wizard_controller.rb +++ b/app/controllers/projects/creation_wizard_controller.rb @@ -74,8 +74,8 @@ class Projects::CreationWizardController < ApplicationController def create_work_package_artifact # rubocop:disable Metrics/AbcSize creation_call = User.execute_as_admin(current_user) do - Projects::CreationWizard::CreateArtifactWorkPackageService - .new(user: current_user, model: @project) + Projects::CreationWizard::SubmitArtifactService + .new(user: current_user, project: @project) .call end diff --git a/app/controllers/wiki_menu_items_controller.rb b/app/controllers/wiki_menu_items_controller.rb index bb7351753e8..ad27f786029 100644 --- a/app/controllers/wiki_menu_items_controller.rb +++ b/app/controllers/wiki_menu_items_controller.rb @@ -67,12 +67,11 @@ class WikiMenuItemsController < ApplicationController def update # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity wiki_menu_setting = wiki_menu_item_params[:setting] - parent_wiki_menu_item = params[:parent_wiki_menu_item] get_data_from_params(params) if wiki_menu_setting == "no_item" - unless @wiki_menu_item.nil? + if @wiki_menu_item if @wiki_menu_item.is_only_main_item? if @page.only_wiki_page? flash.now[:error] = t(:wiki_menu_item_delete_not_permitted) @@ -89,12 +88,8 @@ class WikiMenuItemsController < ApplicationController @wiki_menu_item.name = @page.slug @wiki_menu_item.title = wiki_menu_item_params[:title] || @page_title - if wiki_menu_setting == "sub_item" - @wiki_menu_item.parent_id = parent_wiki_menu_item - elsif wiki_menu_setting == "main_item" - @wiki_menu_item.parent_id = nil - assign_wiki_menu_item_params @wiki_menu_item - end + @wiki_menu_item.parent_id = nil + assign_wiki_menu_item_params @wiki_menu_item end if @wiki_menu_item.destroyed? || @wiki_menu_item.save diff --git a/app/forms/admin/settings/aggregation_settings_form.rb b/app/forms/admin/settings/aggregation_settings_form.rb new file mode 100644 index 00000000000..b3466f9b7cc --- /dev/null +++ b/app/forms/admin/settings/aggregation_settings_form.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 Admin + module Settings + class AggregationSettingsForm < ApplicationForm + include Redmine::I18n + + settings_form do |f| + f.text_field name: :journal_aggregation_time_minutes, + type: :number, + min: 0, + input_width: :medium, + trailing_visual: { text: { text: I18n.t("datetime.units.minute_abbreviated", count: 2) } } + + f.submit + end + end + end +end diff --git a/app/forms/admin/settings/aggregation_settings_form/journal_aggregation_time_minutes_caption.html.erb b/app/forms/admin/settings/aggregation_settings_form/journal_aggregation_time_minutes_caption.html.erb new file mode 100644 index 00000000000..986e2aa67fd --- /dev/null +++ b/app/forms/admin/settings/aggregation_settings_form/journal_aggregation_time_minutes_caption.html.erb @@ -0,0 +1,8 @@ +<%= render(Primer::Beta::Text.new(tag: :p)) do %> + <%= link_translate("admin.journal_aggregation.caption", links: { webhook_link: url_helpers.admin_outgoing_webhooks_path }) %> +<% end %> +<%= render(Primer::OpenProject::InlineMessage.new(scheme: :warning, size: :small)) do %> + <%= render(Primer::Beta::Text.new(tag: :p)) do %> + <%= I18n.t(:text_hint_disable_with_0) %> + <% end %> +<% end %> diff --git a/app/forms/admin/settings/mail_notifications_setting_form.rb b/app/forms/admin/settings/mail_notifications_setting_form.rb new file mode 100644 index 00000000000..a944711699d --- /dev/null +++ b/app/forms/admin/settings/mail_notifications_setting_form.rb @@ -0,0 +1,113 @@ +# 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 Admin + module Settings + class MailNotificationsSettingForm < ApplicationForm + include ::Settings::FormHelper + + settings_form do |f| + if @deliveries + f.text_field name: :mail_from, input_width: :medium + f.check_box name: :bcc_recipients + f.check_box name: :plain_text_mail + f.select_list name: :emails_salutation, + values: [ + [User.human_attribute_name(:firstname), :firstname], + [I18n.t("mail.salutation_full_name"), :name] + ], + input_width: :medium + + f.fieldset_group(title: "#{I18n.t(:setting_emails_header)} & #{I18n.t(:setting_emails_footer)}", mt: 4) do |fg| + fg.multi_language_text_select(name: :emails_header) + fg.multi_language_text_select(name: :emails_footer) + end + end + + unless OpenProject::Configuration["email_delivery_configuration"] == "legacy" + email_methods = %i[smtp sendmail] + email_methods += [:letter_opener] if Rails.env.development? + + f.fieldset_group(title: I18n.t(:text_setup_mail_configuration), mt: 4) do |fg| + fg.select_list( + name: :email_delivery_method, + values: email_methods.map { |m| [m.to_s, m] }, + input_width: :small, + data: { + show_when_value_selected_target: "cause", + target_name: "email_delivery_method_settings" + } + ) + end + + f.group( + hidden: { true => Setting.email_delivery_method != :smtp }, + data: { + show_when_value_selected_target: "effect", + target_name: "email_delivery_method_settings", + value: "smtp" + } + ) do |smtp| + smtp.text_field(name: :smtp_address, input_width: :medium) + smtp.text_field(name: :smtp_port, type: :number, input_width: :xsmall) + smtp.text_field(name: :smtp_domain, input_width: :medium) + smtp.select_list(name: :smtp_authentication, + values: %i[none plain login cram_md5].map { |m| [m.to_s, m] }, + input_width: :small) + smtp.text_field(name: :smtp_user_name, input_width: :medium) + smtp.text_field(name: :smtp_password, input_width: :medium) + smtp.check_box(name: :smtp_enable_starttls_auto) + smtp.check_box(name: :smtp_ssl) + end + + f.group( + hidden: { true => Setting.email_delivery_method != :sendmail }, + data: { + show_when_value_selected_target: "effect", + target_name: "email_delivery_method_settings", + value: "sendmail" + } + ) do |sendmail| + sendmail.text_field(name: :sendmail_location) + sendmail.text_field(name: :sendmail_arguments) + end + end + + f.submit + end + + def initialize(deliveries:) + super() + + @deliveries = deliveries + end + end + end +end diff --git a/app/forms/versions/form.rb b/app/forms/versions/form.rb index 233a8423776..d370e14c2eb 100644 --- a/app/forms/versions/form.rb +++ b/app/forms/versions/form.rb @@ -113,14 +113,44 @@ module Versions if backlogs_enabled? setting = version_setting_for_project - f.select_list( - name: "version[version_settings_attributes][][display]", - scope_name_to_model: false, - label: I18n.t(:label_column_in_backlog), - input_width: :small - ) do |list| - position_display_options.each do |label, value| - list.option(label:, value:, selected: setting.display == value) + if OpenProject::FeatureDecisions.scrum_projects_active? + # We originally planned to render a check_box here. But since this changes the way Rails will submit the parameters, + # this would require changing the controller or services, too. With a feature flag in place, this adds quite a + # lot of complexity. + # To circumvent this, we will use a select list with two options for now. This will not require any changes to + # controllers or services. We can fix this once the feature flag has been removed. + f.select_list( + name: "version[version_settings_attributes][][display]", + scope_name_to_model: false, + label: I18n.t(:label_used_as_backlog), + input_width: :small + ) do |list| + # Maintain the current setting for the sake of migrating sprints to versions later on + current_display_setting = setting.display + value_for_no = if current_display_setting == VersionSetting::DISPLAY_RIGHT || current_display_setting.nil? + VersionSetting::DISPLAY_NONE + else + current_display_setting + end + + list.option(label: I18n.t(:general_text_no), + value: value_for_no, + selected: setting.display != VersionSetting::DISPLAY_RIGHT) + + list.option(label: I18n.t(:general_text_yes), + value: VersionSetting::DISPLAY_RIGHT, + selected: setting.display == VersionSetting::DISPLAY_RIGHT) + end + else + f.select_list( + name: "version[version_settings_attributes][][display]", + scope_name_to_model: false, + label: I18n.t(:label_column_in_backlog), + input_width: :small + ) do |list| + position_display_options.each do |label, value| + list.option(label:, value:, selected: setting.display == value) + end end end diff --git a/app/models/custom_style.rb b/app/models/custom_style.rb index 58f4111f9fa..ac23d2c8bff 100644 --- a/app/models/custom_style.rb +++ b/app/models/custom_style.rb @@ -70,15 +70,9 @@ class CustomStyle < ApplicationRecord end end - define_method :"remove_#{name}" do - attachment = send(name) - attachment&.remove! - - if new_record? - send(:"#{name}=", nil) - else - update_columns(name => nil, updated_at: Time.zone.now) - end + define_method :"remove_#{name}!" do + super() + save! end end end diff --git a/app/models/menu_item.rb b/app/models/menu_item.rb index 548248343aa..991d1fd1486 100644 --- a/app/models/menu_item.rb +++ b/app/models/menu_item.rb @@ -50,7 +50,9 @@ class MenuItem < ApplicationRecord elsif is_main_item? :main_item else - :sub_item + # backwards compatibility for removed configuration option + # sub items are not offered anymore and are effectively not visible + :no_item end end diff --git a/app/models/permitted_params.rb b/app/models/permitted_params.rb index 6562883140e..51dec7954b7 100644 --- a/app/models/permitted_params.rb +++ b/app/models/permitted_params.rb @@ -557,6 +557,7 @@ class PermittedParams :priority_id, :remaining_hours, :responsible_id, + :sprint_id, :start_date, :status_id, :type_id, diff --git a/app/models/project.rb b/app/models/project.rb index 41fb0312f92..5a9479b5c6b 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -77,7 +77,7 @@ class Project < ApplicationRecord has_many :principals, through: :member_principals, source: :principal has_many :calculated_value_errors, dependent: :delete_all, as: :customized - has_many :enabled_modules, dependent: :delete_all + has_many :enabled_modules, dependent: :delete_all, after_remove: :module_disabled has_and_belongs_to_many :types, -> { order("#{::Type.table_name}.position") } @@ -221,7 +221,8 @@ class Project < ApplicationRecord :assignable_parents, :available_custom_fields, :available_templates, - :visible + :visible, + :with_settings scope :has_module, ->(mod) { where(["#{Project.table_name}.id IN (SELECT em.project_id FROM #{EnabledModule.table_name} em WHERE em.name=?)", mod.to_s]) @@ -349,4 +350,10 @@ class Project < ApplicationRecord OpenProject::AccessControl.allowed_actions(permission) end end + + def module_disabled(disabled_module) + OpenProject::Notifications.send( + OpenProject::Events::MODULE_DISABLED, disabled_module: + ) + end end diff --git a/app/models/projects/scopes/with_settings.rb b/app/models/projects/scopes/with_settings.rb new file mode 100644 index 00000000000..461821c873e --- /dev/null +++ b/app/models/projects/scopes/with_settings.rb @@ -0,0 +1,49 @@ +# 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 Projects::Scopes + module WithSettings + extend ActiveSupport::Concern + + class_methods do + def with_settings(**kwargs) + raise ArgumentError, "Provide at least one setting" if kwargs.empty? + + kwargs.reduce(all) do |scope, (key, value)| + if value.nil? + scope.where("settings->>? IS NULL", key) + else + scope.where("settings->>? = ?", key, value) + end + end + end + end + end +end diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 0e1d169a9e0..3636607cfda 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -161,7 +161,7 @@ class WikiPage < ApplicationRecord content_to = journals.find_by(version: version_to) content_from = journals.find_by(version: version_from) - content_to && content_from ? Wikis::Diff.new(content_to, content_from) : nil + content_to && content_from ? WikiPage::Diff.new(content_to, content_from) : nil end def version @@ -171,7 +171,7 @@ class WikiPage < ApplicationRecord def annotate(compare_version = nil) compare_version = compare_version ? compare_version.to_i : version c = journals.find_by(version: compare_version) - c ? Wikis::Annotate.new(c) : nil + c ? WikiPage::Annotate.new(c) : nil end # Returns true if usr is allowed to edit the page, otherwise false diff --git a/app/models/wikis/annotate.rb b/app/models/wiki_page/annotate.rb similarity index 98% rename from app/models/wikis/annotate.rb rename to app/models/wiki_page/annotate.rb index 5a9ea79240f..49bb99494b5 100644 --- a/app/models/wikis/annotate.rb +++ b/app/models/wiki_page/annotate.rb @@ -28,7 +28,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -class Wikis::Annotate +class WikiPage::Annotate attr_reader :lines, :content def initialize(content) diff --git a/app/models/wikis/diff.rb b/app/models/wiki_page/diff.rb similarity index 96% rename from app/models/wikis/diff.rb rename to app/models/wiki_page/diff.rb index aa186cd88b5..b4f001e5ce7 100644 --- a/app/models/wikis/diff.rb +++ b/app/models/wiki_page/diff.rb @@ -28,7 +28,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -class Wikis::Diff < Redmine::Helpers::Diff +class WikiPage::Diff < Redmine::Helpers::Diff attr_reader :content_to, :content_from def initialize(content_to, content_from) diff --git a/app/seeders/env_data/custom_design_seeder.rb b/app/seeders/env_data/custom_design_seeder.rb index 82bd3fa0720..d0d41302b6c 100644 --- a/app/seeders/env_data/custom_design_seeder.rb +++ b/app/seeders/env_data/custom_design_seeder.rb @@ -54,7 +54,7 @@ module EnvData data = Setting.seed_design[key.to_s] if data.blank? - custom_style.public_send(:"remove_#{key}") + custom_style.public_send(:"remove_#{key}!") elsif data.match?(/^https?:\/\//) seed_remote_url(custom_style, key, data) else diff --git a/app/services/project_custom_fields/load_service.rb b/app/services/project_custom_fields/load_service.rb index 50e3cbc4cdc..7f15d4c2c2b 100644 --- a/app/services/project_custom_fields/load_service.rb +++ b/app/services/project_custom_fields/load_service.rb @@ -31,26 +31,19 @@ module ProjectCustomFields class LoadService def initialize(project:, project_custom_fields:) - super() - @project = project - @project_custom_fields = project_custom_fields - eager_load_project_custom_field_values + @values_by_custom_field_id = + CustomValue + .includes(custom_field: :custom_options) + .where( + custom_field: project_custom_fields, + customized: project + ) + .order(:id) + .group_by(&:custom_field_id) end def get_eager_loaded_project_custom_field_values_for(custom_field_id) - @eager_loaded_project_custom_field_values.select { |pcfv| pcfv.custom_field_id == custom_field_id } - end - - private - - def eager_load_project_custom_field_values - @eager_loaded_project_custom_field_values = CustomValue - .includes(custom_field: :custom_options) - .where( - custom_field_id: @project_custom_fields.pluck(:id), - customized_id: @project.id - ) - .to_a + @values_by_custom_field_id[custom_field_id] || [] end end 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 b2640ea2cac..eabe5c94bbc 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 @@ -39,7 +39,6 @@ module Projects::CreationWizard def initialize(user:, model:, contract_class: Projects::CreateArtifactWorkPackageContract) super(user:, contract_class:) self.model = model - @skip_creation = false end def project = model @@ -48,26 +47,7 @@ module Projects::CreationWizard attr_accessor :artifact_work_package - def skip_creation? = @skip_creation - def skip_creation! = @skip_creation = true - - def before_perform(service_call) - if WorkPackage.exists?(project.project_creation_wizard_artifact_work_package_id) - skip_creation! - end - - service_call - end - - def validate_contract(service_call) - return service_call if skip_creation? - - super - end - def persist(service_call) - return service_call if skip_creation? - creation_call = create_artifact_work_package creation_call.on_success do @@ -86,7 +66,7 @@ module Projects::CreationWizard def after_perform(service_call) send_notification_email - return service_call if store_attachment_locally? || skip_creation? + return service_call if store_attachment_locally? if project_storage.nil? service_call.errors.add(:base, I18n.t("projects.wizard.create_artifact_storage_error")) diff --git a/app/services/projects/creation_wizard/reupload_artifact_on_status_changes_service.rb b/app/services/projects/creation_wizard/reupload_artifact_on_status_changes_service.rb index 687f6e7f836..878602be3ca 100644 --- a/app/services/projects/creation_wizard/reupload_artifact_on_status_changes_service.rb +++ b/app/services/projects/creation_wizard/reupload_artifact_on_status_changes_service.rb @@ -30,10 +30,6 @@ module Projects::CreationWizard class ReuploadArtifactOnStatusChangesService - include Contracted - include ProjectsHelper - include ArtifactExporter - include Rails.application.routes.url_helpers prepend Projects::Concerns::UpdateDemoData attr_reader :current_user, :artifact_work_package @@ -61,45 +57,15 @@ module Projects::CreationWizard private def update_artifact - call = store_artifact + call = UploadArtifactService + .new(user: current_user, project:, work_package: artifact_work_package) + .call + if call.success? Rails.logger.debug { "Updated artifact for creation wizard in ##{artifact_work_package.id}" } else Rails.logger.error("Failed to process artifact change for ##{artifact_work_package.id}: ##{call.message}") end end - - def store_artifact - if store_attachment_locally? - return add_attachment_locally - end - - if project_storage.nil? - return ServiceResult.failure(message: I18n.t("projects.wizard.create_artifact_storage_error")) - end - - upload_artifact_to_storage - end - - def add_attachment_locally - export = create_pdf_export! - file = OpenProject::Files.create_uploaded_file( - name: export.title, - content_type: export.mime_type, - content: export.content, - binary: true - ) - - attachment = artifact_work_package.attachments.create( - author: current_user, - file: - ) - - if attachment.persisted? - ServiceResult.success(result: attachment) - else - ServiceResult.failure(result: attachment, errors: attachment.errors) - end - end end end diff --git a/app/services/projects/creation_wizard/submit_artifact_service.rb b/app/services/projects/creation_wizard/submit_artifact_service.rb new file mode 100644 index 00000000000..fead3b302a7 --- /dev/null +++ b/app/services/projects/creation_wizard/submit_artifact_service.rb @@ -0,0 +1,73 @@ +# 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 Projects::CreationWizard + class SubmitArtifactService + attr_reader :user, :project + + def initialize(user:, project:) + @user = user + @project = project + end + + def call + if artifact_work_package_exists? + upload_artifact + else + create_artifact_work_package + end + end + + private + + def artifact_work_package_exists? + WorkPackage.exists?(project.project_creation_wizard_artifact_work_package_id) + end + + def create_artifact_work_package + CreateArtifactWorkPackageService + .new(user:, model: project) + .call + end + + def upload_artifact + work_package = WorkPackage.find(project.project_creation_wizard_artifact_work_package_id) + upload_call = UploadArtifactService + .new(user:, project:, work_package:) + .call + + service_call = ServiceResult.success(result: project) + upload_call.on_failure do + service_call.merge!(upload_call, without_success: true) + end + service_call + end + end +end diff --git a/app/services/projects/creation_wizard/upload_artifact_service.rb b/app/services/projects/creation_wizard/upload_artifact_service.rb new file mode 100644 index 00000000000..8f24cd3de2e --- /dev/null +++ b/app/services/projects/creation_wizard/upload_artifact_service.rb @@ -0,0 +1,78 @@ +# 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 Projects::CreationWizard + class UploadArtifactService + include ArtifactExporter + + attr_reader :user, :project, :artifact_work_package + + def initialize(user:, project:, work_package:) + @user = user + @project = project + @artifact_work_package = work_package + end + + def call + if store_attachment_locally? + return add_attachment_locally + end + + if project_storage.nil? + return ServiceResult.failure(message: I18n.t("projects.wizard.create_artifact_storage_error")) + end + + upload_artifact_to_storage + end + + private + + def add_attachment_locally + export = create_pdf_export! + file = OpenProject::Files.create_uploaded_file( + name: export.title, + content_type: export.mime_type, + content: export.content, + binary: true + ) + + attachment = artifact_work_package.attachments.create( + author: user, + file: + ) + + if attachment.persisted? + ServiceResult.success(result: attachment) + else + ServiceResult.failure(result: attachment, errors: attachment.errors) + end + end + end +end diff --git a/app/views/admin/settings/aggregation_settings/show.html.erb b/app/views/admin/settings/aggregation_settings/show.html.erb index 14308e9ff81..18ebddc5d73 100644 --- a/app/views/admin/settings/aggregation_settings/show.html.erb +++ b/app/views/admin/settings/aggregation_settings/show.html.erb @@ -40,25 +40,13 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= styled_form_tag(admin_settings_aggregation_path, method: :patch) do %> -
- <%= setting_number_field :journal_aggregation_time_minutes, - unit: t(:label_minute_plural), - min: 0, - container_class: "-xslim" %> - - <%= t( - :"admin.journal_aggregation.explanation.text", - webhook_link: link_to( - t(:"admin.journal_aggregation.explanation.link"), - admin_outgoing_webhooks_path, - target: "_blank" - ) - ).html_safe %> -
- <%= t(:text_hint_disable_with_0) %> -
-
- - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> -<% end %> +<%= + settings_primer_form_with( + url: admin_settings_aggregation_path, + scope: :settings, + method: :patch, + data: { turbo: false } + ) do |form| + render Admin::Settings::AggregationSettingsForm.new(form) + end +%> diff --git a/app/views/admin/settings/date_format_settings/show.html.erb b/app/views/admin/settings/date_format_settings/show.html.erb index 66a3ed5395b..5866ade0b77 100644 --- a/app/views/admin/settings/date_format_settings/show.html.erb +++ b/app/views/admin/settings/date_format_settings/show.html.erb @@ -40,40 +40,38 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= styled_form_tag( - admin_settings_date_format_path, - method: :patch, - class: "op-date-format-admin-settings" - ) do %> +<%= + settings_primer_form_with( + url: admin_settings_date_format_path, + scope: :settings, + method: :patch, + class: "op-date-format-admin-settings", + data: { turbo: false } + ) do |form| + render_inline_settings_form(form) do |f| + f.select_list name: :date_format, + values: Settings::Definition[:date_format].allowed.collect { |f| [Date.today.strftime(f), f] }, + input_width: :medium, + include_blank: t(:label_language_based) -
+ f.select_list name: :time_format, + values: Settings::Definition[:time_format].allowed.collect { |f| [Time.now.strftime(f), f] }, + input_width: :medium, + include_blank: t(:label_language_based) -
- <%= setting_select :date_format, - Settings::Definition[:date_format].allowed.collect { |f| [Date.today.strftime(f), f] }, - blank: :label_language_based, - container_class: "-wide" %> -
+ f.select_list name: :start_of_week, + values: [[day_name(1), 1], [day_name(6), 6], [day_name(7), 7]], + input_width: :medium, + include_blank: t(:label_language_based) -
- <%= setting_select :time_format, - Settings::Definition[:time_format].allowed.collect { |f| [Time.now.strftime(f), f] }, - blank: :label_language_based, - container_class: "-wide" %> -
+ f.select_list name: :first_week_of_year, + values: [[day_name(1), 1], [day_name(4), 4]], + input_width: :medium, + include_blank: t(:label_language_based), + caption: t("settings.date_format.first_week_of_year_text_html", + link: OpenProject::Static::Links.url_for(:date_format_settings_documentation)) -
- <%= setting_select :start_of_week, [[day_name(1), "1"], [day_name(6), "6"], [day_name(7), "7"]], blank: :label_language_based, container_class: "-wide" %> -
- -
- <%= setting_select :first_week_of_year, [[day_name(1), "1"], [day_name(4), "4"]], blank: :label_language_based, container_class: "-wide" %> -
-

<%= t("settings.date_format.first_week_of_year_text_html", link: OpenProject::Static::Links.url_for(:date_format_settings_documentation)) %>

-
-
- -
- - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> -<% end %> + f.submit + end + end +%> diff --git a/app/views/admin/settings/icalendar_settings/show.html.erb b/app/views/admin/settings/icalendar_settings/show.html.erb index ac1885f1b22..e1459f83415 100644 --- a/app/views/admin/settings/icalendar_settings/show.html.erb +++ b/app/views/admin/settings/icalendar_settings/show.html.erb @@ -40,20 +40,20 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= styled_form_tag( - admin_settings_icalendar_path, - method: :patch, - class: "op-icalendar-admin-settings" - ) do %> +<%= + settings_primer_form_with( + url: admin_settings_icalendar_path, + scope: :settings, + method: :patch, + class: "op-icalendar-admin-settings", + data: { turbo: false } + ) do |form| + render_inline_settings_form(form) do |f| + f.check_box name: :ical_enabled, + caption: t("settings.icalendar.enable_subscriptions_text_html", + link: OpenProject::Static::Links.url_for(:ical_docs)) -
-
- <%= setting_check_box :ical_enabled, size: 6 %> -
-

<%= t("settings.icalendar.enable_subscriptions_text_html", link: OpenProject::Static::Links.url_for(:ical_docs)) %>

-
-
-
- - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> -<% end %> + f.submit + end + end +%> diff --git a/app/views/admin/settings/incoming_mails_settings/show.html.erb b/app/views/admin/settings/incoming_mails_settings/show.html.erb index 13df0d25774..5676f233229 100644 --- a/app/views/admin/settings/incoming_mails_settings/show.html.erb +++ b/app/views/admin/settings/incoming_mails_settings/show.html.erb @@ -40,21 +40,26 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= styled_form_tag(admin_settings_incoming_mails_path, method: :patch) do %> -
-
- <%= setting_text_area :mail_handler_body_delimiters, rows: 5, container_class: "-wide" %> -
<%= t(:text_line_separated) %>
-
-
- <%= setting_text_field :mail_handler_body_delimiter_regex, container_class: "-wide" %> -
<%= t(:text_regexp_multiline) %>
-
-
- <%= setting_text_area :mail_handler_ignore_filenames, rows: 5, container_class: "-wide" %> -
<%= t("incoming_mails.ignore_filenames") %>
-
-
+<%= + settings_primer_form_with( + url: admin_settings_incoming_mails_path, + scope: :settings, + method: :patch, + data: { turbo: false } + ) do |form| + render_inline_settings_form(form) do |f| + f.text_area name: :mail_handler_body_delimiters, + rows: 5, + caption: t(:text_line_separated) - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> -<% end %> + f.text_field name: :mail_handler_body_delimiter_regex, + caption: t(:text_regexp_multiline) + + f.text_area name: :mail_handler_ignore_filenames, + rows: 5, + caption: t("incoming_mails.ignore_filenames") + + f.submit + end + end +%> diff --git a/app/views/admin/settings/mail_notifications_settings/show.html.erb b/app/views/admin/settings/mail_notifications_settings/show.html.erb index 8ec62de0be2..31c2968dbf1 100644 --- a/app/views/admin/settings/mail_notifications_settings/show.html.erb +++ b/app/views/admin/settings/mail_notifications_settings/show.html.erb @@ -40,98 +40,34 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= - styled_form_tag( - admin_settings_mail_notifications_path, - method: :patch, - data: { controller: "show-when-value-selected" } - ) do -%> - <% if @deliveries %> -
-
<%= setting_text_field :mail_from, size: 60, container_class: "-middle" %>
-
<%= setting_check_box :bcc_recipients %>
-
<%= setting_check_box :plain_text_mail %>
-
- <%= setting_select :emails_salutation, - [ - [User.human_attribute_name(:firstname), :firstname], - [t("mail.salutation_full_name"), :name] - ], - container_class: "-middle" %> -
-
- -
- <%= t(:setting_emails_header) %> - & <%= I18n.t(:setting_emails_footer) %> - <%= render Settings::TextSettingComponent.new(I18n.locale, name: "emails_header") %> - <%= render Settings::TextSettingComponent.new(I18n.locale, name: "emails_footer") %> -
- <% else %> -
- <%= simple_format(t(:text_email_delivery_not_configured)) %> -
- <% end %> - - <% - email_methods = %i[smtp sendmail] - email_methods << :letter_opener if Rails.env.development? +<% unless @deliveries %> + <%= + render Primer::Alpha::Banner.new(scheme: :default) do + t(:text_email_delivery_not_configured) + end %> - <%= content_tag :fieldset, class: "form--fieldset" do %> - <%= t(:text_setup_mail_configuration) %> -
- <%= - setting_select( - :email_delivery_method, - email_methods, - container_class: "-slim", - data: { - show_when_value_selected_target: "cause", - target_name: "email_delivery_method_settings" - } - ) - %> -
-
> -
<%= setting_text_field :smtp_address, container_class: "-middle" %>
-
<%= setting_text_field :smtp_port, size: 6, container_class: "-xslim" %>
-
<%= setting_text_field :smtp_domain, container_class: "-middle" %>
-
<%= setting_select(:smtp_authentication, %i[none plain login cram_md5], container_class: "-slim") %>
-
<%= setting_text_field :smtp_user_name, container_class: "-middle" %>
-
<%= setting_password :smtp_password, container_class: "-middle" %>
-
<%= setting_check_box :smtp_enable_starttls_auto %>
-
<%= setting_check_box :smtp_ssl %>
-
-
> -
<%= setting_text_field :sendmail_location %>
-
<%= setting_text_field :sendmail_arguments %>
-
-
> -

Letter opener is used to render emails as a file in your Rails tmp folder. Mails will automatically open in - your browser if supported.

-
- <% end unless OpenProject::Configuration['email_delivery_configuration'] == 'legacy' %> - - <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> - - <% if @deliveries %> -
- <%= link_to t(:label_send_test_email), - { controller: "/admin", action: "test_email" }, - data: { turbo_method: :post } %> -
- <% end %> +<% end %> + +<%= + settings_primer_form_with( + url: admin_settings_mail_notifications_path, + scope: :settings, + method: :patch, + data: { turbo: false, + controller: "show-when-value-selected" } + ) do |form| + render Admin::Settings::MailNotificationsSettingForm.new(form, deliveries: @deliveries) + end +%> + +<% if @deliveries %> + <%= render Primer::Beta::Link.new( + href: test_email_admin_index_path, + data: { turbo_method: :post }, + font_weight: :bold, + display: :block, + mt: 3 + ) do + t(:label_send_test_email) + end %> <% end %> diff --git a/app/views/layouts/base.html.erb b/app/views/layouts/base.html.erb index 96f97626981..4ccba595b04 100644 --- a/app/views/layouts/base.html.erb +++ b/app/views/layouts/base.html.erb @@ -74,7 +74,7 @@ See COPYRIGHT and LICENSE files for more details. <%= render_top_menu_left %> -
+
<%= render_top_menu_center %>
diff --git a/app/views/layouts/only_logo.html.erb b/app/views/layouts/only_logo.html.erb index 315db4ec378..311c4ebf6be 100644 --- a/app/views/layouts/only_logo.html.erb +++ b/app/views/layouts/only_logo.html.erb @@ -39,8 +39,13 @@ See COPYRIGHT and LICENSE files for more details. data: body_data_attributes(local_assigns) do %>
"> -
diff --git a/app/views/wiki_menu_items/edit.html.erb b/app/views/wiki_menu_items/edit.html.erb index b7041d88f1b..2624bcbed04 100644 --- a/app/views/wiki_menu_items/edit.html.erb +++ b/app/views/wiki_menu_items/edit.html.erb @@ -57,12 +57,6 @@ See COPYRIGHT and LICENSE files for more details. <%= form.radio_button "setting", :main_item %> <%= form.label "setting_main_item", t(:label_wiki_show_menu_item) %>

-

- <% disabled = @parent_menu_item_options.empty? %> - <%= form.radio_button "setting", :sub_item, disabled: disabled %> - <%= form.label "setting_sub_item", t(:label_wiki_show_submenu_item), { id: "with-select" } %> - <%= select_tag "parent_wiki_menu_item", options_for_select(@parent_menu_item_options, @selected_parent_menu_item_id), disabled: disabled %> -

<%= styled_button_tag t(:button_save), method: :post, diff --git a/app/workers/import/jira_import_projects_job.rb b/app/workers/import/jira_import_projects_job.rb index 0b9f0f6ca02..eb431d59bf1 100644 --- a/app/workers/import/jira_import_projects_job.rb +++ b/app/workers/import/jira_import_projects_job.rb @@ -68,11 +68,12 @@ module Import Import::JiraProject.where(jira_id:, jira_project_id: project_ids).find_each do |jira_project| ### PROJECT + identifier = jira_project.payload.fetch("key").downcase service_call = Projects::CreateService .new(user:) .call( name: jira_project.payload.fetch("name"), - identifier: jira_project.payload.fetch("key").downcase, + identifier:, description: jira_project.payload.fetch("description"), active: true, public: false, @@ -231,6 +232,11 @@ module Import raise service_call.message end end + elsif (error = service_call.errors.find { |e| e.attribute == :identifier && e.type == :taken }) && error.present? + taken_identifier = error.options[:value] + project = Project.find_by!(identifier: taken_identifier) + raise "You are trying to import a project with already used " \ + "identifier: #{taken_identifier}. Existing project: #{project}." else raise service_call.message end diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb index 78862f69417..e6e4b85f366 100644 --- a/config/initializers/carrierwave.rb +++ b/config/initializers/carrierwave.rb @@ -56,6 +56,12 @@ module CarrierWave end end +# CW 2.0 changed the default cache_storage from :file to nil. +# Restore :file to keep Attachment.clean_cached_files! working. +CarrierWave.configure do |config| + config.cache_storage = :file +end + unless OpenProject::Configuration.fog_credentials.empty? CarrierWave::Configuration.configure_fog! end diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index ad78036edd4..28b6fb2eb55 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -42,9 +42,10 @@ # These inflection rules are supported but not enabled by default: ActiveSupport::Inflector.inflections(:en) do |inflect| + inflect.acronym "AI" inflect.acronym "API" + inflect.acronym "ClamAV" + inflect.acronym "ICal" inflect.acronym "OAuth" inflect.acronym "OpenID" - inflect.acronym "ICal" - inflect.acronym "ClamAV" end diff --git a/config/initializers/permissions.rb b/config/initializers/permissions.rb index 777e2247c47..beee0e35280 100644 --- a/config/initializers/permissions.rb +++ b/config/initializers/permissions.rb @@ -399,7 +399,8 @@ Rails.application.reloader.to_prepare do wpt.permission :add_work_package_attachments, {}, permissible_on: %i[work_package project], - dependencies: :view_work_packages + dependencies: :view_work_packages, + contract_actions: { work_package_attachments: %i[create] } # WorkPackage categories wpt.permission :manage_categories, diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index a0b70d27cf2..4ecc5bc38c9 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -36,6 +36,7 @@ relative_url_root = config["rails_relative_url_root"].presence session_options = { key: config["session_cookie_name"], httponly: true, + same_site: :lax, secure: config.https?, path: relative_url_root } diff --git a/config/initializers/store_attribute.rb b/config/initializers/store_attribute.rb index 37ded4b606b..1fb782ad849 100644 --- a/config/initializers/store_attribute.rb +++ b/config/initializers/store_attribute.rb @@ -31,4 +31,9 @@ # From v1.0 to v2.0 of store_attribute, the value for store_attribute_unset_values_fallback_to_default changed from # false to true. This initializer sets it back to false to keep the behavior consistent with the previous version. +# Keeping this false also avoids a subtle dirty-tracking issue with the `default:` option: assigning the +# default value to an attribute that has never been persisted is a no-op from dirty-tracking's perspective, +# so the store column is never written. Concretely, `create(:project, sprint_sharing: "no_sharing")` leaves +# `project.settings` as `{}` because "no_sharing" equals the declared default and is never saved. + StoreAttribute.store_attribute_unset_values_fallback_to_default = false diff --git a/config/locales/crowdin/af.yml b/config/locales/crowdin/af.yml index 163922a2908..31462553e6a 100644 --- a/config/locales/crowdin/af.yml +++ b/config/locales/crowdin/af.yml @@ -107,9 +107,8 @@ af: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ af: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ af: label_float: "Dryf" label_folder: "Folder" label_follows: "volg" - label_force_user_language_to_default: "Stel taal van gebruikers wat 'n nie-toegelate taal gebruik na verstektaal" label_form_configuration: "Vorm konfigurasie" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ af: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anoniem" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Net vir die dinge wat ek dophou of betrokke in is" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ af: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Begin bladsy" label_work: "Work" label_work_package: "Werkspakket" @@ -5011,7 +4999,7 @@ af: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Skrap" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Wat wil jy doen?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index 9f2a881444f..8e09304a361 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -107,9 +107,8 @@ ar: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1435,9 +1434,6 @@ ar: dependencies: "الاعتماديات" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3739,7 +3735,6 @@ ar: label_float: "عدد عشري" label_folder: "مجلد" label_follows: "تتبع" - label_force_user_language_to_default: "إعطاء لغة للمستخدمين غير اللغة المسموح بها بشكل افتراضي" label_form_configuration: "تشكيل النموذج" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -4118,12 +4113,6 @@ ar: label_user_named: "User %{name}" label_user_activity: "نشاط %{value}'s" label_user_anonymous: "مجهول" - label_user_mail_option_all: "من أجل أي حدث في كافة المشاريع" - label_user_mail_option_none: "لا يوجد أحداث" - label_user_mail_option_only_assigned: "فقط لأشياء موكلة لي" - label_user_mail_option_only_my_events: "فقط لأشياء أشاهدها أو أشارك فيها" - label_user_mail_option_only_owner: "فقط لأشياء أملكها" - label_user_mail_option_selected: "من أجل أي حدث في المشاريع المحددة فقط" label_user_menu: "User menu" label_user_new: "مستخدم جديد" label_user_plural: "المستخدمين" @@ -4163,7 +4152,6 @@ ar: label_wiki_show_index_page_link: "إظهار عنصر القائمة الفرعية ’جدول المحتويات’" label_wiki_show_menu_item: "إظهار كعنصر قائمة في مشروع الملاحة" label_wiki_show_new_page_link: "إظهار عنصر القائمة الفرعية 'إنشاء صفحة فرعية جديدة'" - label_wiki_show_submenu_item: "إظهار كعنصر قائمة فرعية من " label_wiki_start: "صفحة البداية" label_work: "Work" label_work_package: "مجموعة العمل" @@ -5241,7 +5229,7 @@ ar: text_default_administrator_account_changed: "تغيير حساب المسؤول الافتراضي" text_default_encoding: "الافتراضي: UTF-8" text_destroy: "احذف" - text_destroy_with_associated: "وهناك كائنات إضافية مرتبطة مع الحزمة (الحزم) العمل الموجودة المراد حذفها. هذه الكائنات من الأنواع التالية: " + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "ماذا تريد أن تفعل؟" text_diff_truncated: "... تم اقتطاعه لأنه يتجاوز الحد الأقصى للحجم التي يمكن عرضها." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/az.yml b/config/locales/crowdin/az.yml index c0e2d31c54a..49b97ec5e7b 100644 --- a/config/locales/crowdin/az.yml +++ b/config/locales/crowdin/az.yml @@ -107,9 +107,8 @@ az: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ az: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ az: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ az: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ az: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ az: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Sil" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/be.yml b/config/locales/crowdin/be.yml index 342bdda8035..4a8565f774f 100644 --- a/config/locales/crowdin/be.yml +++ b/config/locales/crowdin/be.yml @@ -107,9 +107,8 @@ be: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1405,9 +1404,6 @@ be: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3627,7 +3623,6 @@ be: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -4006,12 +4001,6 @@ be: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -4051,7 +4040,6 @@ be: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5127,7 +5115,7 @@ be: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Выдаліць" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 46f71a5a182..c40eadfd279 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -107,9 +107,8 @@ bg: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ bg: dependencies: "Зависимости" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ bg: label_float: "Плаващ" label_folder: "Папка" label_follows: "следва" - label_force_user_language_to_default: "Задаване на език по подразбиране за потребителите с липсващи в системата езици" label_form_configuration: "Конфигурация на формата" label_formula: "Formula" label_gantt_chart: "Диаграма на Гант" @@ -3894,12 +3889,6 @@ bg: label_user_named: "Потребител %{name}" label_user_activity: "дейност на %{value}" label_user_anonymous: "Анонимен" - label_user_mail_option_all: "За всяко събитие на всичките ми проекти" - label_user_mail_option_none: "Няма събития" - label_user_mail_option_only_assigned: "Само за неща, възложени на мен" - label_user_mail_option_only_my_events: "Само за неща, които наблюдавам или съм включен в тях" - label_user_mail_option_only_owner: "Само за неща, на които аз съм собственик" - label_user_mail_option_selected: "За всяко събитие само в избраните проекти" label_user_menu: "User menu" label_user_new: "Нов потребител" label_user_plural: "Потребители" @@ -3939,7 +3928,6 @@ bg: label_wiki_show_index_page_link: "Показване на елемент от подменюто \"Съдържание\"" label_wiki_show_menu_item: "Покажи като меню в проектната навигация" label_wiki_show_new_page_link: "Показване на елемент от подменю \"Създай нова подстраница\"" - label_wiki_show_submenu_item: "Покажи като елемент от подменюто на " label_wiki_start: "Начална страница" label_work: "Работа" label_work_package: "Работен пакет" @@ -5011,7 +4999,7 @@ bg: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Изтрий" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Какво искате да правите?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index 4e6707ec15a..a3840f9b274 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -107,9 +107,8 @@ ca: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Les accions individuals d'un sol usuari (per exemple actualitzar dos cops un paquet de treball) seran agregades en una sola acció si la diferència temporal és menor a l'especificada. Aquests seran exposats com una acció individual dins l'aplicació. Això, també reduïra el número d'emails enviats i el retràs en el %{webhook_link} ja que les notificacións també seran retrasades." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1372,9 +1371,6 @@ ca: dependencies: "Dependències" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3512,7 +3508,6 @@ ca: label_float: "Flotant" label_folder: "Carpeta" label_follows: "segueix" - label_force_user_language_to_default: "Definir la llengua pels usuaris que tenen una llengua per defecte no permesa" label_form_configuration: "Configuració del formulari" label_formula: "Formula" label_gantt_chart: "Diagrama de Gantt" @@ -3891,12 +3886,6 @@ ca: label_user_named: "Usuari %{name}" label_user_activity: "Activitat de %{value}" label_user_anonymous: "Anònim" - label_user_mail_option_all: "Per qualsevol esdeveniment en tots els meus projectes" - label_user_mail_option_none: "No hi ha esdeveniments" - label_user_mail_option_only_assigned: "Només pels objectes on estic assignat" - label_user_mail_option_only_my_events: "Només pels objectes que estic en observant o involucrat" - label_user_mail_option_only_owner: "Només pels objectes dels que en sóc propietari" - label_user_mail_option_selected: "Només per qualsevol esdeveniment dels projectes seleccionats" label_user_menu: "User menu" label_user_new: "Nou usuari" label_user_plural: "Usuaris" @@ -3936,7 +3925,6 @@ ca: label_wiki_show_index_page_link: "Mostra l'element del submenú 'Taula de continguts'" label_wiki_show_menu_item: "Mostrar com a element de menú en la navegació del projecte" label_wiki_show_new_page_link: "Mostra l'element submenú 'Crea nova pàgina fill'" - label_wiki_show_submenu_item: "Mostrar com element de submenú de " label_wiki_start: "Pàgina d'inici" label_work: "Treball" label_work_package: "Paquet de treball" @@ -5002,7 +4990,7 @@ ca: text_default_administrator_account_changed: "S'ha canviat el compte d'administrador predeterminat" text_default_encoding: "Per defecte: UTF-8" text_destroy: "Esborrar" - text_destroy_with_associated: "Hi ha objectes addicionals associats amb el(s) paquet(s) de treball que es suprimiran. Aquests objectes són dels tipus següents:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Què vols fer?" text_diff_truncated: "... Aquestes diferències s'han truncat perquè excedeixen la mida màxima que es pot mostrar." text_email_delivery_not_configured: "L'entrega de correu electrònic no està configurada, i les notificacions estan desactivades.\nConfigura el teu servidor SMTP per a habilitar-les." diff --git a/config/locales/crowdin/ckb-IR.yml b/config/locales/crowdin/ckb-IR.yml index a893fb52d61..5e172c2fb8c 100644 --- a/config/locales/crowdin/ckb-IR.yml +++ b/config/locales/crowdin/ckb-IR.yml @@ -107,9 +107,8 @@ ckb-IR: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ ckb-IR: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ ckb-IR: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ ckb-IR: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ ckb-IR: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ ckb-IR: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index 6a8c1c7c0ea..460d252f7d2 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -107,9 +107,8 @@ cs: trial: "Trial" jemalloc_allocator: Jemalloc alokátor paměti journal_aggregation: - explanation: - text: "Individuální akce/úpravy uživatele (např. dvojnásobná aktualizace pracovního balíčku se sečtou do jediné akce, pokud je jejich časový rozdíl menší než stanovený čas. Budou zobrazeny jako jedna akce v rámci aplikace. Toto také zpozdí oznámení o stejný čas a sníží tak počet zasílaných e-mailů a ovlivní se také zpoždění na adrese %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1405,9 +1404,6 @@ cs: dependencies: "Závislosti" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -1531,7 +1527,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" @@ -1903,7 +1899,7 @@ cs: message: cannot_move_message_to_forum_of_different_project: "A message cannot be moved to a forum of a different project." 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í " @@ -2204,11 +2200,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" @@ -3300,7 +3296,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: @@ -3386,7 +3382,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." @@ -3627,7 +3623,6 @@ cs: label_float: "Desetinné číslo" label_folder: "Složka" label_follows: "sleduje" - label_force_user_language_to_default: "Nastavte jazyk uživatelů, kteří nemají povolený jazyk na výchozí" label_form_configuration: "Konfigurace formuláře" label_formula: "Vzorec" label_gantt_chart: "Ganttův diagram" @@ -3831,9 +3826,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" @@ -3857,7 +3852,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" @@ -4006,12 +4001,6 @@ cs: label_user_named: "Uživatel %{name}" label_user_activity: "Aktivita %{value}" label_user_anonymous: "Anonymní" - label_user_mail_option_all: "Pro všechny události všech mých projektů" - label_user_mail_option_none: "Žádné události" - label_user_mail_option_only_assigned: "Pouze pro věci, na které jsem přidělen" - label_user_mail_option_only_my_events: "Pouze pro věci, které sleduji nebo jsem v nich zapojen" - label_user_mail_option_only_owner: "Pouze pro věci, kde jsem majitelem" - label_user_mail_option_selected: "Pouze pro každou událost na vybraných projektech" label_user_menu: "User menu" label_user_new: "Nový uživatel" label_user_plural: "Uživatelé" @@ -4022,7 +4011,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" @@ -4051,7 +4040,6 @@ cs: label_wiki_show_index_page_link: "Zobrazit položku podnabídky 'Obsah'" label_wiki_show_menu_item: "Zobrazit jako položku nabídky navigaci projektu" label_wiki_show_new_page_link: "Zobrazit položku menu \"Vytvořit novou podřízenou stránku\"" - label_wiki_show_submenu_item: "Zobrazit jako položku podnabídky " label_wiki_start: "Úvodní stránka" label_work: "Práce" label_work_package: "Pracovní balíček" @@ -4130,28 +4118,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" @@ -4160,7 +4148,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" @@ -4461,7 +4449,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" @@ -4948,7 +4936,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" @@ -5126,10 +5114,10 @@ cs: text_default_administrator_account_changed: "Výchozí účet správce změněn" text_default_encoding: "Výchozí: UTF-8" text_destroy: "Odstranit" - 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_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" 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 95b0ae94ff5..6ac7a05f16e 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -107,9 +107,8 @@ da: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1373,9 +1372,6 @@ da: dependencies: "Aflæggere" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3513,7 +3509,6 @@ da: label_float: "Flyd" label_folder: "Folder" label_follows: "følger" - label_force_user_language_to_default: "Sæt sprog for brugere, der har forhåndsvalgt et ikke gangbart sprog" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3892,12 +3887,6 @@ da: label_user_named: "User %{name}" label_user_activity: "%{value}s aktivitet" label_user_anonymous: "Anonymt" - label_user_mail_option_all: "For enhver hændelse i alle mine projekter" - label_user_mail_option_none: "Ingen hændelser" - label_user_mail_option_only_assigned: "Kun i forbindelse med det jeg er tilknyttet" - label_user_mail_option_only_my_events: "Kun for det jeg fører tilsyn med eller er involveret i" - label_user_mail_option_only_owner: "Kun for det jeg er ejer af" - label_user_mail_option_selected: "Kun for hændelser i de valgte projekter" label_user_menu: "User menu" label_user_new: "Ny bruger" label_user_plural: "Brugere" @@ -3937,7 +3926,6 @@ da: label_wiki_show_index_page_link: "Vis undermenupunkt 'Indhold'" label_wiki_show_menu_item: "Vis som menupunkt i projektnavigeringen" label_wiki_show_new_page_link: "Vis undermenupunktet 'Opret ny underside'" - label_wiki_show_submenu_item: "Vis som punkt i undermenu til " label_wiki_start: "Startside" label_work: "Work" label_work_package: "Arbejdspakke" @@ -5007,7 +4995,7 @@ da: text_default_administrator_account_changed: "Forhåndsvalgt administratorkonto ændret" text_default_encoding: "Forhåndsvalgt: UTF-8" text_destroy: "Slet" - text_destroy_with_associated: "Der er yderligere objekter tilknyttet arbejdspakken/-kerne som skal slettes. Disse er af følgende art:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Hvad vil du lave?" text_diff_truncated: "... Denne afvigelse blev undertrykt da den overstiger den længde, der kan vises." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index 5535cfd11d0..ac7954d9227 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -86,11 +86,11 @@ de: type_token_text: "Ihr Enterprise-Token-Text" token_placeholder: "Enterprise-Token Text hier einfügen" token_caption: "Weitere Informationen über die Aktivierung der Enterprise Edition finden Sie in unserer [Dokumentation](docs_url)." - 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" @@ -107,9 +107,8 @@ de: trial: "Probezeitraum" jemalloc_allocator: Jemalloc Speicher allocator journal_aggregation: - explanation: - text: "Individuelle Aktionen eines Benutzers (z.B. ein Arbeitspaket zweimal aktualisieren) werden zu einer einzigen Aktion zusammengefasst, wenn ihr Altersunterschied kleiner ist als der angegebene Zeitraum. Sie werden als eine einzige Aktion innerhalb der Anwendung angezeigt. Dadurch werden Benachrichtigungen um die gleiche Zeit verzögert, wodurch die Anzahl der gesendeten E-Mails verringert wird. Dies wirkt sich auch auf die Verzögerung von %{webhook_link} aus." - link: "Webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1133,10 +1132,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: @@ -1190,7 +1189,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." @@ -1292,7 +1291,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" @@ -1367,9 +1366,6 @@ de: dependencies: "Abhängigkeiten" activerecord: attributes: - agile/sprint: - sharing: "Teilen" - finish_date: "Enddatum" jira_import: projects: "Projekte" "import/jira": @@ -1548,7 +1544,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" @@ -2048,7 +2044,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: @@ -2862,7 +2858,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}" @@ -3150,7 +3146,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" @@ -3474,7 +3470,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" @@ -3507,7 +3503,6 @@ de: label_float: "Gleitkommazahl" label_folder: "Ordner" label_follows: "folgt" - label_force_user_language_to_default: "Setze Sprache für Nutzer, die eine nicht erlaubte Sprache gewählt haben, auf die Standard-Sprache" label_form_configuration: "Formularkonfiguration" label_formula: "Formel" label_gantt_chart: "Gantt-Diagramm" @@ -3886,12 +3881,6 @@ de: label_user_named: "Benutzer %{name}" label_user_activity: "Aktivität von %{value}" label_user_anonymous: "Anonym" - label_user_mail_option_all: "Für alle Ereignisse in all meinen Projekten" - label_user_mail_option_none: "Für keine Ereignisse" - label_user_mail_option_only_assigned: "Nur für Aufgaben, für die ich zuständig bin." - label_user_mail_option_only_my_events: "Nur für Aufgaben, die ich beobachte oder an welchen ich mitarbeite" - label_user_mail_option_only_owner: "Nur für Aufgaben, die ich angelegt habe" - label_user_mail_option_selected: "Für alle Ereignisse in den ausgewählten Projekten" label_user_menu: "Benutzermenü" label_user_new: "Neuer Benutzer" label_user_plural: "Benutzer" @@ -3931,7 +3920,6 @@ de: label_wiki_show_index_page_link: "'Inhaltsverzeichnis' Link als Untermenüpunkt anzeigen" label_wiki_show_menu_item: "als Menüpunkt in der Projektnavigation anzeigen" label_wiki_show_new_page_link: "'Neue Unterseite anlegen' Link als Untermenüpunkt anzeigen" - label_wiki_show_submenu_item: "als Untermenüpunkt anzeigen von " label_wiki_start: "Hauptseite" label_work: "Aufwand" label_work_package: "Arbeitspaket" @@ -4535,7 +4523,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: @@ -5005,7 +4993,7 @@ de: text_default_administrator_account_changed: "Administrator-Kennwort geändert" text_default_encoding: "Default: UTF-8" text_destroy: "Löschen" - text_destroy_with_associated: "Es sind weitere Datenobjekte mit den bzw. dem zu löschenden Arbeitpaket(en) verbunden. Es handelt sich dabei um Objekte der folgenden Typen:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Was soll mit den Objekten geschehen?" text_diff_truncated: "... Dieser Diff wurde abgeschnitten, weil er die maximale Anzahl anzuzeigender Zeilen überschreitet." text_email_delivery_not_configured: "E-Mail-Zustellung ist nicht konfiguriert und Benachrichtigungen sind deaktiviert.\nKonfigurieren Sie Ihren SMTP-Server, um sie zu aktivieren." @@ -5176,7 +5164,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 185bc11e39a..9ee312c8da4 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -107,9 +107,8 @@ el: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1371,9 +1370,6 @@ el: dependencies: "Εξαρτήσεις" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3511,7 +3507,6 @@ el: label_float: "Float" label_folder: "Φάκελος" label_follows: "ακολουθεί" - label_force_user_language_to_default: "Ορίστε την γλώσσα των χρηστών που έχουν μια μη επιτρεπόμενη γλώσσα ως προεπιλογή" label_form_configuration: "Διαμόρφωση φόρμας" label_formula: "Formula" label_gantt_chart: "Διάγραμμα Gantt" @@ -3890,12 +3885,6 @@ el: label_user_named: "Χρήστης %{name}" label_user_activity: "δραστηριότητα του %{value}" label_user_anonymous: "Ανώνυμος" - label_user_mail_option_all: "Για όλα τα συμβάντα σε όλα τα έργα μου" - label_user_mail_option_none: "Κανένα συμβάν" - label_user_mail_option_only_assigned: "Μόνο για αντικείμενα που έχουν ανατεθεί σε μένα" - label_user_mail_option_only_my_events: "Μόνο για αντικείμενα που παρακολουθώ ή συμμετέχω" - label_user_mail_option_only_owner: "Μόνο για αντικείμενα που μου ανήκουν" - label_user_mail_option_selected: "Για όλες τις εξελίξεις μόνο στα επιλεγμένα έργα" label_user_menu: "User menu" label_user_new: "Νέος χρήστης" label_user_plural: "Χρήστες" @@ -3935,7 +3924,6 @@ el: label_wiki_show_index_page_link: "Εμφάνιση του αντικειμένου υπομενού 'Πίνακας Περιεχομένων'" label_wiki_show_menu_item: "Εμφάνιση ως αντικείμενο μενού στην πλοήγηση έργου" label_wiki_show_new_page_link: "Εμφάνιση του αντικειμένου υπομενού 'Δημιουργία καινούργιας σελίδας παιδιού'" - label_wiki_show_submenu_item: "Εμφάνιση ως αντικείμενο υπομενού του " label_wiki_start: "Σελίδα έναρξης" label_work: "Work" label_work_package: "Εργασία" @@ -5006,7 +4994,7 @@ el: text_default_administrator_account_changed: "Ο προεπιλεγμένος λογαριασμός του διαχειριστή άλλαξε" text_default_encoding: "Προεπιλογή: UTF-8" text_destroy: "Διαγραφή" - text_destroy_with_associated: "Υπάρχουν επιπλέον αντικείμενα μαζί με το πακέτο εργασίας που πρόκειται να διαγραφεί. Τα αντικείμενα αυτά είναι των παρακάτω τύπων:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Τι θέλετε να κάνετε;" text_diff_truncated: "... Αυτό το diff περικόπηκε επειδή υπερβαίνει το μέγιστο μέγεθος που μπορεί να εμφανιστεί." text_email_delivery_not_configured: "Η διανομή email δεν είναι διαμορφωμένη και οι ειδοποιήσεις είναι απενεργοποιημένες.\nΔιαμορφώστε τον SMTP εξυπηρετητή για τις ενεργοποιήσετε." diff --git a/config/locales/crowdin/eo.yml b/config/locales/crowdin/eo.yml index 91f0a0e977d..266e900f561 100644 --- a/config/locales/crowdin/eo.yml +++ b/config/locales/crowdin/eo.yml @@ -107,9 +107,8 @@ eo: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ eo: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ eo: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Formulara agordo" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ eo: label_user_named: "Uzanto %{name}" label_user_activity: "Aktiveco de %{value}" label_user_anonymous: "Aanonima" - label_user_mail_option_all: "Por ajna evento en ĉiuj miaj projektoj" - label_user_mail_option_none: "Ne estas eventoj" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "Nova uzanto" label_user_plural: "Uzantoj" @@ -3939,7 +3928,6 @@ eo: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Laborpakaĵo" @@ -5011,7 +4999,7 @@ eo: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Forigi" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index c3645102ab0..e38267c9e15 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -107,9 +107,8 @@ es: trial: "Prueba" jemalloc_allocator: Asignador de memoria Jemalloc journal_aggregation: - explanation: - text: "Las acciones individuales de un usuario (como actualizar dos veces un paquete de trabajo) se combinan en una sola acción si la diferencia de antigüedad es inferior al intervalo de tiempo especificado. Se mostrarán como una sola acción en la aplicación. También se retrasarán las notificaciones por la misma cantidad de tiempo, lo que reducirá el número de correos electrónicos enviados y causará también que se retrase el %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Importar" jira: @@ -1198,7 +1197,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." @@ -1372,9 +1371,6 @@ es: dependencies: "Dependencias" activerecord: attributes: - agile/sprint: - sharing: "Uso compartido" - finish_date: "Fecha de finalización" jira_import: projects: "Proyectos" "import/jira": @@ -3512,7 +3508,6 @@ es: label_float: "Desprender" label_folder: "Carpeta" label_follows: "sigue" - label_force_user_language_to_default: "Configurar idioma de usuarios que tienen un lenguaje no permitido por defecto" label_form_configuration: "Configuración del formato" label_formula: "Fórmula" label_gantt_chart: "Diagrama de Gantt" @@ -3891,12 +3886,6 @@ es: label_user_named: "Usuario %{name}" label_user_activity: "Actividad de %{value} " label_user_anonymous: "Anónimo" - label_user_mail_option_all: "Para cualquier evento en todos mis proyectos" - label_user_mail_option_none: "No hay eventos" - label_user_mail_option_only_assigned: "Sólo para cosas asignadas a mí" - label_user_mail_option_only_my_events: "Sólo para cosas que controlo o en las que participo" - label_user_mail_option_only_owner: "Sólo para cosas de las que soy el dueño" - label_user_mail_option_selected: "Únicamente para cualquier evento de los proyectos seleccionados" label_user_menu: "Menú de usuario" label_user_new: "Nuevo usuario" label_user_plural: "Usuarios" @@ -3936,7 +3925,6 @@ es: label_wiki_show_index_page_link: "Mostrar el elemento de submenú 'Tabla de contenidos'" label_wiki_show_menu_item: "Mostrar como elemento de menú de navegación de proyecto" label_wiki_show_new_page_link: "Mostrar el elemento de submenú 'Crear nueva página hija'" - label_wiki_show_submenu_item: "Mostrar como elemento de submenú de " label_wiki_start: "Página de inicio" label_work: "Trabajo" label_work_package: "Paquete de trabajo" @@ -5007,7 +4995,7 @@ es: text_default_administrator_account_changed: "Se ha cambiado la cuenta de administrador por defecto" text_default_encoding: "Por defecto: UTF-8" text_destroy: "Borrar" - text_destroy_with_associated: "Hay objetos adicionales asociados a los paquetes de trabajo que van a ser eliminados. Esos objetos son de los siguientes tipos:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "¿Qué quieres hacer?" text_diff_truncated: "... Esta diferencia se truncó porque excede el tamaño máximo que puede visualizarse." text_email_delivery_not_configured: "No se ha configurado la entrega de correo electrónico y se han deshabilitado las notificaciones.\nConfigure el servidor SMTP para habilitarlas." diff --git a/config/locales/crowdin/et.yml b/config/locales/crowdin/et.yml index 5fd8e489728..2097a65a155 100644 --- a/config/locales/crowdin/et.yml +++ b/config/locales/crowdin/et.yml @@ -107,9 +107,8 @@ et: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ et: dependencies: "Sõltuvused" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ et: label_float: "Ujuvkomaarv" label_folder: "Kaust" label_follows: "järgneb" - label_force_user_language_to_default: "Sea keel kasutajatele, kellel on vaikimisi keeleks valitud mittelubatud keel" label_form_configuration: "Vormi seadistamine" label_formula: "Formula" label_gantt_chart: "Gantt diagrammi" @@ -3894,12 +3889,6 @@ et: label_user_named: "User %{name}" label_user_activity: "%{value}-s tegevus" label_user_anonymous: "Anonüümne" - label_user_mail_option_all: "Kõigi sündmuste kohta minu projektides" - label_user_mail_option_none: "Sündmusi pole" - label_user_mail_option_only_assigned: "Ainult minule määratud tööde kohta" - label_user_mail_option_only_my_events: "Ainult tööde kohta, mida ma jälgin või millega olen seotud" - label_user_mail_option_only_owner: "Ainult minu omanduses olevate asjade kohta" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "Uus kasutaja" label_user_plural: "Kasutajad" @@ -3939,7 +3928,6 @@ et: label_wiki_show_index_page_link: "Kuva alammenüü valik \"Sisukord\"" label_wiki_show_menu_item: "Näita lehe nime projektimenüüs" label_wiki_show_new_page_link: "Näita alammenüüs valikut 'Loo uus alamleht'" - label_wiki_show_submenu_item: "Näita kui alammenüüd (<--) " label_wiki_start: "Esileht" label_work: "Töö" label_work_package: "Teema" @@ -5011,7 +4999,7 @@ et: text_default_administrator_account_changed: "Algne administraatori konto on muudetud" text_default_encoding: "Vaikimisi: UTF-8" text_destroy: "Kustuta" - text_destroy_with_associated: "Kustutamiseks määratud teema(de)ga on seostatud veel objekte. Objektid on järgnevate tüüpidega:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Mis sa soovid ette võtta?" text_diff_truncated: "... Osa erinevusi jäi välja, sest neid on näitamiseks liiga palju." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/eu.yml b/config/locales/crowdin/eu.yml index b188ebcab9d..d256c03af4d 100644 --- a/config/locales/crowdin/eu.yml +++ b/config/locales/crowdin/eu.yml @@ -107,9 +107,8 @@ eu: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ eu: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ eu: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ eu: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ eu: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ eu: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/fa.yml b/config/locales/crowdin/fa.yml index 996cc5a1e9b..78fd1f9d79d 100644 --- a/config/locales/crowdin/fa.yml +++ b/config/locales/crowdin/fa.yml @@ -107,9 +107,8 @@ fa: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ fa: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ fa: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "نمودار گانت" @@ -3894,12 +3889,6 @@ fa: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ fa: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "صفحه‌ی شروع" label_work: "Work" label_work_package: "کاربسته" @@ -5011,7 +4999,7 @@ fa: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "حذف" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index 374f4993bef..a7244449a04 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -107,9 +107,8 @@ fi: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ fi: dependencies: "Riippuvuudet" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ fi: label_float: "Liukuluku" label_folder: "Kansio" label_follows: "seuraa" - label_force_user_language_to_default: "Määritä oletuskieli jos käytössä on kieli joka ei ole sallittu" label_form_configuration: "Lomakkeen muokkaus" label_formula: "Formula" label_gantt_chart: "Gantt-kaavio" @@ -3894,12 +3889,6 @@ fi: label_user_named: "Käyttäjä %{name}" label_user_activity: "Käyttäjän %{value} historia" label_user_anonymous: "Anonyymi" - label_user_mail_option_all: "Kaikista tapahtumista kaikissa projekteistani" - label_user_mail_option_none: "Ei tapahtumia" - label_user_mail_option_only_assigned: "Vain niistä joihin olen liittynyt" - label_user_mail_option_only_my_events: "Vain niistä, joita seuraan tai joissa olen mukana" - label_user_mail_option_only_owner: "Vain niistä, jotka omistan" - label_user_mail_option_selected: "Kaikista tapahtumista vain valitsemistani projekteista" label_user_menu: "User menu" label_user_new: "Uusi käyttäjä" label_user_plural: "Käyttäjät" @@ -3939,7 +3928,6 @@ fi: label_wiki_show_index_page_link: "Näytä alivalikon vaihtoehto \"Sisällysluettelo\"" label_wiki_show_menu_item: "Näytä valikon kohta projekti navigaatio" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Aloitussivu" label_work: "Work" label_work_package: "Työpaketti" @@ -5011,7 +4999,7 @@ fi: text_default_administrator_account_changed: "Oletuarvoinen järjestelmävalvoja muutettu" text_default_encoding: "Default: UTF-8" text_destroy: "Poista" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Mitä sinä haluat tehdä?" text_diff_truncated: "... Tämä katkaistiin koska suurin mahdollinen näytettävä määrä tuli täyteen." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index 14b05d728d6..ab0051bcdf2 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -107,9 +107,8 @@ fil: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ fil: dependencies: "Dependencia" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ fil: label_float: "Lumutang" label_folder: "Folder" label_follows: "sundan" - label_force_user_language_to_default: "Magtakda ng linggwahe ng mga gumagamit na mayroon pinayagang linggwahe sa default" label_form_configuration: "Form kompigurasyon" label_formula: "Formula" label_gantt_chart: "Gantt tsart" @@ -3894,12 +3889,6 @@ fil: label_user_named: "User %{name}" label_user_activity: "%{value} aktibidad" label_user_anonymous: "Hindi kilala" - label_user_mail_option_all: "Sa kahit anong kaganapan sa lahat ng aking proyekto" - label_user_mail_option_none: "Walang mga kaganapan" - label_user_mail_option_only_assigned: "Sa mga bagay lamang ako nakatalaga sa" - label_user_mail_option_only_my_events: "Sa mga bagay ako tumingin o kasama ako sa" - label_user_mail_option_only_owner: "Sa mga bagay lamg ako na ako ang may-ari ng" - label_user_mail_option_selected: "Sa kahit anong kaganapan sa mga napiling proyekto lamang" label_user_menu: "User menu" label_user_new: "Bagong gumagamit" label_user_plural: "Mga gumagamit" @@ -3939,7 +3928,6 @@ fil: label_wiki_show_index_page_link: "Ipakita ang aytem ng submenu 'Talaan ng Nilalaman'" label_wiki_show_menu_item: "Ipakita bilanh aytem na pagpipilian sa proyektong nabigasyon" label_wiki_show_new_page_link: "Ipakita ang submenu aytem 'Lumikha ng bagong bata na pahina'" - label_wiki_show_submenu_item: "Ipakita bilang submenu. aytem ng " label_wiki_start: "Simulan ang pahina" label_work: "Work" label_work_package: "Work package" @@ -5009,7 +4997,7 @@ fil: text_default_administrator_account_changed: "I-default ang tagapangasiwa ng pagbago ng akwant" text_default_encoding: "I-default: UTF-8" text_destroy: "Burahin" - text_destroy_with_associated: "Mayroong karagdagang mga bagay naka-associaye sa work packGe na maaring burahin. Yung mga bagay ay ang mga sumusunod na uri:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Ano gusto mong gawin?" text_diff_truncated: "... Itong diff ay naka-truncatw dahil ito ay lamps na sa pinakamataas na laki na maaring ipakita." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 5a528c7bc50..8ae638ca522 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -107,9 +107,8 @@ fr: trial: "Essai" jemalloc_allocator: Allocateur de mémoire Jemalloc journal_aggregation: - explanation: - text: "Les actions individuelles d'un utilisateur (par ex. un lot de travaux mis à jour deux fois) sont agrégées en une seule action si leur différence d'âge est inférieure à la période spécifiée. Elles seront affichées en une seule action dans l'application. Cela retardera également les notifications du même temps réduisant donc le nombre d'e-mails envoyés et affectera également le délai %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Importation" jira: @@ -1373,9 +1372,6 @@ fr: dependencies: "Dépendances" activerecord: attributes: - agile/sprint: - sharing: "Partage" - finish_date: "Échéance" jira_import: projects: "Projets" "import/jira": @@ -3513,7 +3509,6 @@ fr: label_float: "Flottant" label_folder: "Dossier" label_follows: "suivi" - label_force_user_language_to_default: "Pour cet utilisateur, revenir au langage par defaut" label_form_configuration: "Configuration du formulaire" label_formula: "Formule" label_gantt_chart: "Diagramme de Gantt" @@ -3892,12 +3887,6 @@ fr: label_user_named: "Utilisateur %{name}" label_user_activity: "Activité de %{value}" label_user_anonymous: "Anonyme" - label_user_mail_option_all: "Tout évènement dans tous mes projets" - label_user_mail_option_none: "Aucun évènement" - label_user_mail_option_only_assigned: "Les éléments qui me sont assignés" - label_user_mail_option_only_my_events: "Uniquement pour les éléments que je suis ou auxquels je participe" - label_user_mail_option_only_owner: "Les éléments dont je suis le propriétaire" - label_user_mail_option_selected: "Pour chaque événement sur le projet selectionné seulement" label_user_menu: "Menu utilisateur" label_user_new: "Nouvel utilisateur" label_user_plural: "Utilisateurs" @@ -3937,7 +3926,6 @@ fr: label_wiki_show_index_page_link: "Montrer l'entrée de sous-menu 'Table des Matières'" label_wiki_show_menu_item: "Afficher en tant qu'élément dans le menu de navigation du projet" label_wiki_show_new_page_link: "Montrer le sous-menu 'Créer une nouvelle page enfant'" - label_wiki_show_submenu_item: "Montrer comme sous-menu de " label_wiki_start: "Page d'accueil" label_work: "Travail" label_work_package: "Lot de travaux" diff --git a/config/locales/crowdin/he.yml b/config/locales/crowdin/he.yml index 55c6765e015..17be71154c6 100644 --- a/config/locales/crowdin/he.yml +++ b/config/locales/crowdin/he.yml @@ -107,9 +107,8 @@ he: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1405,9 +1404,6 @@ he: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3627,7 +3623,6 @@ he: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -4006,12 +4001,6 @@ he: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "אנונימי" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "משתמש חדש" label_user_plural: "משתמשים" @@ -4051,7 +4040,6 @@ he: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "דף פתיחה" label_work: "Work" label_work_package: "חבילת עבודה" @@ -5127,7 +5115,7 @@ he: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "ברירת מחדל: UTF-8" text_destroy: "מחק" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/hi.yml b/config/locales/crowdin/hi.yml index 1a9d7c18ae9..6b5d70bd0e4 100644 --- a/config/locales/crowdin/hi.yml +++ b/config/locales/crowdin/hi.yml @@ -107,9 +107,8 @@ hi: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1373,9 +1372,6 @@ hi: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3513,7 +3509,6 @@ hi: label_float: "फ्लोट" label_folder: "Folder" label_follows: "फॉलो करें" - label_force_user_language_to_default: "डिफ़ॉल्ट करने के लिए एक गैर अनुमति भाषा वाले उपयोगकर्ताओं की भाषा सेट करें" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "गैंट चार्ट" @@ -3892,12 +3887,6 @@ hi: label_user_named: "उपयोगकर्ता %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "अज्ञात" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3937,7 +3926,6 @@ hi: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "प्रारंभ पृष्ठ" label_work: "Work" label_work_package: "कार्य पैकेज" @@ -5009,7 +4997,7 @@ hi: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "मिटाएँ" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index 10dff54d8b7..2e0e5a37496 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -107,9 +107,8 @@ hr: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1390,9 +1389,6 @@ hr: dependencies: "Ovisnosti" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3571,7 +3567,6 @@ hr: label_float: "Plutajući" label_folder: "Mapa" label_follows: "slijedi" - label_force_user_language_to_default: "Postavi zadani jezik za korisnike koji imaju nedozvoljeni jezik kao zadani" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3950,12 +3945,6 @@ hr: label_user_named: "User %{name}" label_user_activity: "%{value}-a aktivnost" label_user_anonymous: "Anonimno" - label_user_mail_option_all: "Za bilo koji događaj na svim mojim projektima" - label_user_mail_option_none: "Nema novih događanja" - label_user_mail_option_only_assigned: "Samo stvari koje su mi dodijeljene" - label_user_mail_option_only_my_events: "Samo stvari koje nadgledam ili u koje sam uključen" - label_user_mail_option_only_owner: "Samo za stvari kojih sam vlasnik" - label_user_mail_option_selected: "Za bilo koji događaj na samo odabranim projektima" label_user_menu: "User menu" label_user_new: "Novi korisnik" label_user_plural: "Korisnici" @@ -3995,7 +3984,6 @@ hr: label_wiki_show_index_page_link: "Prikaži stavku podizbornika 'Kazalo sadržaja'" label_wiki_show_menu_item: "Prikaži kao stavku izbornika u izborniku projekta" label_wiki_show_new_page_link: "Prikaži stavku podizbornika 'Kreiraj novu podređenu stranicu'" - label_wiki_show_submenu_item: "Prikaži kao stavku podizbornika iz " label_wiki_start: "Početna stranica" label_work: "Work" label_work_package: "Radni paket" @@ -5069,7 +5057,7 @@ hr: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Zadana vrijednost: UTF-8" text_destroy: "Obriši" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Što želite uraditi?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index 1274f34175b..f5af9fffa46 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -107,9 +107,8 @@ hu: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "A felhasználó egyes műveletei (pl. egy munkacsomag kétszeri frissítése) egyetlen műveletté egyesülnek, ha korkülönbségük kisebb, mint a megadott időtartam. Ezek egyetlen műveletként jelennek meg az alkalmazásban. Ez ugyanannyi idővel késlelteti az értesítéseket, csökkenti az elküldött emailek számát, valamint befolyásolja a %{webhook_link} késleltetését is.\n" - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1374,9 +1373,6 @@ hu: dependencies: "Szükséges összetevők" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3514,7 +3510,6 @@ hu: label_float: "Mozgó" label_folder: "Mappa" label_follows: "követi ezt" - label_force_user_language_to_default: "Nyelv beállitása a felhasználóknak, amelyeknél nem az alapértelmezett nyelv az elfogadott" label_form_configuration: "Űrlap konfiguráció" label_formula: "Formula" label_gantt_chart: "Gantt-diagram" @@ -3893,12 +3888,6 @@ hu: label_user_named: "Felhasználó: %{name}" label_user_activity: "%{value} tevékenység" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "Minden saját projektet érintő esetben" - label_user_mail_option_none: "Nem kérek értesítéseket" - label_user_mail_option_only_assigned: "Csak a hozzám rendelt feladatokról" - label_user_mail_option_only_my_events: "Csak a megfigyelt feladatok vagy amelyben részt veszek" - label_user_mail_option_only_owner: "Csak azok a feladatok, amelyeknek én vagyok a tulajdonosa" - label_user_mail_option_selected: "Minden eseményről a kiválasztott projektekben" label_user_menu: "User menu" label_user_new: "Új felhasználó" label_user_plural: "Felhasználók" @@ -3938,7 +3927,6 @@ hu: label_wiki_show_index_page_link: "Megjeleníti az almenüpont feladatainak \"Tartalomjegyzékét\"" label_wiki_show_menu_item: "Megjeleníti mint menüpont a projekt navigációban" label_wiki_show_new_page_link: "Almenüpont megjelenítése \" új gyermekoldal létrehozása\"" - label_wiki_show_submenu_item: "Megjelenítés mint: almenüpont " label_wiki_start: "Kezdő oldal" label_work: "Munka" label_work_package: "Feladatcsoport" @@ -5009,7 +4997,7 @@ hu: text_default_administrator_account_changed: "Az alapértelmezett rendszergazdai fiók megváltozott" text_default_encoding: "Alapértelmezett érték: UTF-8" text_destroy: "Törlés" - text_destroy_with_associated: "További objektumok vannak hozzárendelve, a feladatcsoport(ok)hoz, amelyeket törölni kell. Ezek az objektumok a következők:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Mit szeretne tenni?" text_diff_truncated: "... Ez a különbség csonkolva lett, mert meghaladja a maximálisan megjeleníthető méretet." text_email_delivery_not_configured: "Email küldés nincs bekonfigurálva, és az értesítések ki vannak kapcsolva.\nÁllítsa be az SMTP szervert, hogy engedélyezze azokat." diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index d07f52f2e1c..44c0c8fac20 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -107,9 +107,8 @@ id: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Setiap tindakan pengguna (mis. memperbarui paket kerja dua kali) digabungkan menjadi satu tindakan jika perbedaan usianya kurang dari rentang waktu yang ditentukan. Mereka akan ditampilkan sebagai tindakan tunggal dalam aplikasi. Ini juga akan menunda pemberitahuan dengan jumlah waktu yang sama sehingga mengurangi jumlah email yang dikirim dan juga akan memengaruhi penundaan %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1356,9 +1355,6 @@ id: dependencies: "Dependensi" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3455,7 +3451,6 @@ id: label_float: "Float" label_folder: "Folder" label_follows: "Berikut" - label_force_user_language_to_default: "Set bahasa user ke default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Bagan Gantt" @@ -3834,12 +3829,6 @@ id: label_user_named: "User %{name}" label_user_activity: "%{value} aktivitas" label_user_anonymous: "Anonimus" - label_user_mail_option_all: "Untuk semua Event pada semua proyek saya" - label_user_mail_option_none: "Tidak ada event" - label_user_mail_option_only_assigned: "Hanya untuk hal-hal yang saya ditugaskan untuk" - label_user_mail_option_only_my_events: "Hanya untuk hal-hal yang saya pantau atau saya terlibat didalamnya" - label_user_mail_option_only_owner: "Hanya untuk hal-hal yang saya sebagai Ownernya" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "User baru" label_user_plural: "User" @@ -3879,7 +3868,6 @@ id: label_wiki_show_index_page_link: "Tampilkan item submenu 'Daftar isi'" label_wiki_show_menu_item: "Tampilkan sebagai item menu navigasi Project" label_wiki_show_new_page_link: "Tampilkan submenu item 'Buat sub-halaman baru'" - label_wiki_show_submenu_item: "Tampilkan sebagai submenu item " label_wiki_start: "Homepage" label_work: "Work" label_work_package: "Paket-Penugasan" @@ -4946,7 +4934,7 @@ id: text_default_administrator_account_changed: "Akun administrator default terlah dirubah" text_default_encoding: "Default: UTF-8" text_destroy: "Hapus" - text_destroy_with_associated: "Ada tambahan objek terasosiasi dengan Work Package yang akan dihapus. Objek tersebut adalah Tipe :" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Apa yang ingin Anda lakukan?" text_diff_truncated: "... diff ini telah dipotong karena melebihi batas maks. yang dapat ditampilkan." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 46d8c11c16c..6fc282640d4 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -107,9 +107,8 @@ it: trial: "Prova" jemalloc_allocator: Allocatore di memoria Jemalloc journal_aggregation: - explanation: - text: "Le singole azioni di un utente (es. l'aggiornamento di una macro-attività due volte) vengono aggregate in un'unica azione se il tempo intercorso tra esse è inferiore al periodo minimo di tempo impostato. Verranno visualizzate quindi come un'unica azione all'interno dell'applicazione. Questo ritarderà anche le notifiche della stessa quantità di tempo, riducendo così il numero di email inviate, e influirà anche sul ritardo di %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Importa" jira: @@ -1372,9 +1371,6 @@ it: dependencies: "Dipendenze" activerecord: attributes: - agile/sprint: - sharing: "Condivisione" - finish_date: "Data di fine" jira_import: projects: "Progetti" "import/jira": @@ -3512,7 +3508,6 @@ it: label_float: "Virgola mobile" label_folder: "Cartella" label_follows: "segue" - label_force_user_language_to_default: "Imposta la lingua degli utenti che non hanno una lingua consentita a quella predefinita" label_form_configuration: "Configurazione del modulo" label_formula: "Formula" label_gantt_chart: "Diagramma di Gantt" @@ -3891,12 +3886,6 @@ it: label_user_named: "Utente %{name}" label_user_activity: "attività di %{value}" label_user_anonymous: "Anonimo" - label_user_mail_option_all: "Per qualsiasi evento su tutti i miei progetti" - label_user_mail_option_none: "Per nessun evento" - label_user_mail_option_only_assigned: "Solo per cose alle quali sono stato assegnato" - label_user_mail_option_only_my_events: "Solo per le cose che osservo o nelle quali sono coinvolto" - label_user_mail_option_only_owner: "Solo per le cose di cui sono proprietario" - label_user_mail_option_selected: "Per qualsiasi evento solo nei progetti selezionati" label_user_menu: "Menu utente" label_user_new: "Nuovo utente" label_user_plural: "Utenti" @@ -3936,7 +3925,6 @@ it: label_wiki_show_index_page_link: "Mostra voce del sotto-menù 'Sommario'" label_wiki_show_menu_item: "Mostra come voce del menù nella navigazione del progetto" label_wiki_show_new_page_link: "Mostra voce del sotto-menù 'Crea nuova pagina figlio'" - label_wiki_show_submenu_item: "Mostra come voce di sottomenù di " label_wiki_start: "Pagina iniziale" label_work: "Lavoro" label_work_package: "Macro-attività" @@ -5008,7 +4996,7 @@ it: text_default_administrator_account_changed: "Account amministratore predefinito cambiato" text_default_encoding: "Predefinito: UTF-8" text_destroy: "Cancella" - text_destroy_with_associated: "Ci sono ulteriori oggetti associati con la/le macro-attività che devono essere eliminati. Tali oggetti sono dei seguenti tipi:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Cosa vuoi fare?" text_diff_truncated: "... Questo diff è stato troncato perché supera la dimensione massima che può essere visualizzata." text_email_delivery_not_configured: "Consegna email non configurata. Le notifiche sono state disabilitate.\nConfigura il tuo server SMTP per abilitarle." diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index 57d0fed0b18..a266d2d5d6b 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -107,9 +107,8 @@ ja: trial: "試用版" jemalloc_allocator: Jemalloc メモリアロケータ journal_aggregation: - explanation: - text: "ユーザーの個々のアクション(例えば、ワークパッケージを2回更新する)は、それらの年齢差が指定されたタイムスパン未満である場合、単一のアクションに集約されます。これらはアプリケーション内で1つのアクションとして表示されます。これはまた、送信されるメールの数を減らし、 %{webhook_link} の遅延にも影響します。" - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -318,13 +317,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: "トークンが生成されました" @@ -337,21 +336,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クライアントがまだ設定されていません" @@ -902,26 +901,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: @@ -945,7 +944,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" @@ -966,7 +965,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: "ブラウザ" @@ -980,17 +979,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: @@ -1047,7 +1046,7 @@ ja: placeholder_users: right_to_manage_members_missing: > プレースホルダーユーザを削除する権限がありません。 プレースホルダー ユーザーがメンバーであるすべてのプロジェクトのメンバーを管理する権利はありません。 - delete_tooltip: "プレースホルダー・ユーザーの削除" + delete_tooltip: "プレースホルダー ユーザーを削除" deletion_info: heading: "プレースホルダー ユーザー %{name} を削除" data_consequences: > @@ -1065,11 +1064,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: 現在、ステータス報告はありません。 @@ -1080,19 +1079,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: 新しいステータスを追加 @@ -1102,7 +1102,7 @@ ja: is_readonly: "読み取り専用" excluded_from_totals: "合計から除外" themes: - dark: "暗い" + dark: "ダーク" light: "ライト" sync_with_os: "自動(OSのテーマ設定に追従)" types: @@ -1220,15 +1220,15 @@ 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: "ワークパッケージの共有のためのワークフローがありません" @@ -1252,9 +1252,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} を追加" @@ -1358,9 +1358,6 @@ ja: dependencies: "依存関係" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3457,7 +3454,6 @@ ja: label_float: "小数" label_folder: "フォルダ" label_follows: "次の項目に後続" - label_force_user_language_to_default: "ユーザーの言語設定が利用許可されていない場合、デフォルト言語を利用する" label_form_configuration: "フォーム設定" label_formula: "数式" label_gantt_chart: "ガントチャート" @@ -3836,12 +3832,6 @@ ja: label_user_named: "ユーザー名 %{name}" label_user_activity: "%{value}の活動" label_user_anonymous: "匿名ユーザ" - label_user_mail_option_all: "参加しているプロジェクトの全イベント" - label_user_mail_option_none: "通知しない" - label_user_mail_option_only_assigned: "自分が担当している事柄のみ" - label_user_mail_option_only_my_events: "ウォッチまたは関係している事柄のみ" - label_user_mail_option_only_owner: "自分が作成した事柄のみ" - label_user_mail_option_selected: "選択したプロジェクトのみのイベントに対して" label_user_menu: "ユーザーメニュー" label_user_new: "新規ユーザ" label_user_plural: "ユーザ" @@ -3881,7 +3871,6 @@ ja: label_wiki_show_index_page_link: "下位のメニューで「目次」を表示" label_wiki_show_menu_item: "プロジェクトのメニューで項目として表示" label_wiki_show_new_page_link: "下位のメニューで「子ページを新規作成」の項目を表示" - label_wiki_show_submenu_item: "上位のメニュー項目" label_wiki_start: "開始ページ" label_work: "予定時間" label_work_package: "ワーク パッケージ" @@ -4951,7 +4940,7 @@ ja: text_default_administrator_account_changed: "管理者アカウントでデフォルト設定が変更済み" text_default_encoding: "既定値: UTF-8" text_destroy: "削除" - text_destroy_with_associated: "削除されるワークパッケージと追加の対象物が関連付けています。それらの対象物は次の種類です:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "どれかを選択して下さい。" text_diff_truncated: "... 差分の行数が表示可能な上限を超えました。超過分は表示しません。" text_email_delivery_not_configured: "メール配信が設定されておらず、通知が無効になっています。\nSMTPサーバーを有効にしてください。" diff --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 86ed4916a23..67d57f04cc4 100644 --- a/config/locales/crowdin/ka.yml +++ b/config/locales/crowdin/ka.yml @@ -107,9 +107,8 @@ ka: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "ვებჰუკი" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ ka: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ ka: label_float: "მცურავი" label_folder: "საქაღალდე" label_follows: "მიჰყვება" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "განტის დიაგრამა" @@ -3894,12 +3889,6 @@ ka: label_user_named: "მომხმარებელი %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "ანონიმური" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "მოვლენების გარეშე" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "ახალი მომხმარებელი" label_user_plural: "მომხმარებლები" @@ -3939,7 +3928,6 @@ ka: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "საწყისი გვერდი" label_work: "სამუშაო" label_work_package: "სამუშაო პაკეტი" @@ -5011,7 +4999,7 @@ ka: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "ნაგულისხმევი: UTF-8" text_destroy: "წაშლა" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/kk.yml b/config/locales/crowdin/kk.yml index 8402614de98..7733ec34ca1 100644 --- a/config/locales/crowdin/kk.yml +++ b/config/locales/crowdin/kk.yml @@ -107,9 +107,8 @@ kk: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ kk: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ kk: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ kk: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ kk: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ kk: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index 68058e90e59..fa6354fd687 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -107,9 +107,8 @@ ko: trial: "평가판" jemalloc_allocator: Jemalloc 메모리 할당기 journal_aggregation: - explanation: - text: "사용자의 개별 작업(예: 작업 패키지를 두 번 업데이트)은 연령 차이가 지정된 기간 미만인 경우 단일 작업으로 집계됩니다. 애플리케이션 내에서 단일 작업으로 표시됩니다. 또한 이는 전송되는 이메일 수를 줄이는 동일한 시간만큼 알림을 지연시키고 %{webhook_link} 지연에도 영향을 미칩니다." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "가져오기" jira: @@ -1360,9 +1359,6 @@ ko: dependencies: "종속성" activerecord: attributes: - agile/sprint: - sharing: "공유" - finish_date: "종료 날짜" jira_import: projects: "프로젝트" "import/jira": @@ -3459,7 +3455,6 @@ ko: label_float: "부동" label_folder: "폴더" label_follows: "팔로우" - label_force_user_language_to_default: "허용되지 않는 언어가 있는 사용자의 언어를 기본값으로 설정" label_form_configuration: "양식 구성" label_formula: "공식" label_gantt_chart: "Gantt 차트" @@ -3838,12 +3833,6 @@ ko: label_user_named: "사용자 %{name}" label_user_activity: "%{value}의 작업" label_user_anonymous: "익명" - label_user_mail_option_all: "모든 내 프로젝트의 이벤트에 대해" - label_user_mail_option_none: "이벤트 없음" - label_user_mail_option_only_assigned: "나에게 할당된 사항만" - label_user_mail_option_only_my_events: "내가 주시하거나 관련된 사항만" - label_user_mail_option_only_owner: "내가 소유자인 사항만" - label_user_mail_option_selected: "선택된 프로젝트에서 발생하는 이벤트만" label_user_menu: "사용자 메뉴" label_user_new: "새 사용자" label_user_plural: "사용자" @@ -3883,7 +3872,6 @@ ko: label_wiki_show_index_page_link: "하위 메뉴 항목 '목차' 표시" label_wiki_show_menu_item: "프로젝트 탐색에서 메뉴 항목으로 표시" label_wiki_show_new_page_link: "하위 메뉴 항목 '새 자식 페이지 만들기' 표시" - label_wiki_show_submenu_item: "다음의 하위 메뉴 항목으로 표시: " label_wiki_start: "시작 페이지" label_work: "작업" label_work_package: "작업 패키지" @@ -4950,7 +4938,7 @@ ko: text_default_administrator_account_changed: "기본 관리자 계정 변경됨" text_default_encoding: "기본: UTF-8" text_destroy: "삭제" - text_destroy_with_associated: "삭제할 작업 패키지와 연결된 추가 개체가 있습니다. 이러한 개체의 유형은 다음과 같습니다." + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "어떤 작업을 수행하시겠습니까?" text_diff_truncated: "... 이 차이점은 표시할 수 있는 최대 크기를 초과하므로 잘렸습니다." text_email_delivery_not_configured: "이메일 배달이 구성되지 않았고, 알림이 비활성화되었습니다.\nSMTP 서버를 구성하여 활성화하세요." diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index f921d493031..efb0d065aa9 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -107,9 +107,8 @@ lt: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Visi atskiri naudotojo veiksmai (t.y. darbų paketo atnaujinimas du kartus) yra sugrupuojami į vieną veiksmą, jei laiko tarpas tarp jų yra mažesnis už šį nustatymą. Programoje jie bus rodomi kaip vienas veiksmas. Tiek pat bus pavėlinti ir pranešimai. Dėl to sumažės siunčiamų el.laiškų skaičius ir taipogi įtakos %{webhook_link} delsimą." - link: "tinklo jungtis" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1402,9 +1401,6 @@ lt: dependencies: "Priklausomybės" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3624,7 +3620,6 @@ lt: label_float: "Skaičius su kableliu" label_folder: "Aplankas" label_follows: "seka" - label_force_user_language_to_default: "Nustatyti numatytąją kalbą vartotojams, kurie nurodo, sistemoje nežinomą kalbą" label_form_configuration: "Formos konfigūracija" label_formula: "Formula" label_gantt_chart: "Ganto grafikas" @@ -4003,12 +3998,6 @@ lt: label_user_named: "Naudotojas %{name}" label_user_activity: "%{value} veikla" label_user_anonymous: "Anonimas" - label_user_mail_option_all: "Bet kokiam įvykiui visuose mano projektuose" - label_user_mail_option_none: "Jokių įvykių" - label_user_mail_option_only_assigned: "Tiktai dalykams, kuriems esu priskirtas" - label_user_mail_option_only_my_events: "Tiktai dalykams, kuriuos stebiu arba esu įtrauktas" - label_user_mail_option_only_owner: "Tiktai dalykams, kurių šeimininkas esu aš" - label_user_mail_option_selected: "Bet kokiam įvykiui tiktai pasirinktuose projektuose" label_user_menu: "User menu" label_user_new: "Naujas vartotojas" label_user_plural: "Naudotojai" @@ -4048,7 +4037,6 @@ lt: label_wiki_show_index_page_link: "Rodyti submeniu punktą „Turinys“" label_wiki_show_menu_item: "Rodyti kaip meniu punktą projekto navigacijoje" label_wiki_show_new_page_link: "Rodyti submeniu punktą „Sukurti naują vaiko puslapį“" - label_wiki_show_submenu_item: "Rodyti kaip submeniu punktą " label_wiki_start: "Pradžios puslapis" label_work: "Darbas" label_work_package: "Darbų paketas" @@ -5121,7 +5109,7 @@ lt: text_default_administrator_account_changed: "Administratoriaus numatytoji paskyra pakeista" text_default_encoding: "Numatytasis: UTF-8" text_destroy: "Ištrinti" - text_destroy_with_associated: "Yra papildomų objektų, kurie susieti su darbų paketu(-ais), kurie ketinami ištrinti.\nTie objektai yra šių tipų:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Ką norite daryti?" text_diff_truncated: "... Šis diff'as sutrauktas, nes viršija maksimalų rodomų eilučių skaičių." text_email_delivery_not_configured: "El. laiškų pristatymas yra nesukonfigūruotas, dėl to pranešimai yra išjungti.\nSukonfigūruokite prisijungimą prie SMTP serverio pranešimų įjungimui." diff --git a/config/locales/crowdin/lv.yml b/config/locales/crowdin/lv.yml index 7b5362a2127..1db4f4c270b 100644 --- a/config/locales/crowdin/lv.yml +++ b/config/locales/crowdin/lv.yml @@ -107,9 +107,8 @@ lv: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1390,9 +1389,6 @@ lv: dependencies: "Saistītie projekti" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3571,7 +3567,6 @@ lv: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3950,12 +3945,6 @@ lv: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "Par jebkuru notikumu, visos manos projektos" - label_user_mail_option_none: "Nevēlos saņemt e-pasta paziņojumus" - label_user_mail_option_only_assigned: "Tikai par lietām kas piešķirtas man" - label_user_mail_option_only_my_events: "Tikai par lietām, kam sekoju, vai es esmu iesaistīts" - label_user_mail_option_only_owner: "Tikai par lietām, kuras es esmu izveidojis" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Lietotāji" @@ -3995,7 +3984,6 @@ lv: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Sākuma lapa" label_work: "Work" label_work_package: "Darba pieteikums" @@ -5069,7 +5057,7 @@ lv: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Dzēst" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/mn.yml b/config/locales/crowdin/mn.yml index 7f182c3ea40..ae2ba14fce3 100644 --- a/config/locales/crowdin/mn.yml +++ b/config/locales/crowdin/mn.yml @@ -107,9 +107,8 @@ mn: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ mn: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ mn: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ mn: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ mn: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ mn: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/ms.yml b/config/locales/crowdin/ms.yml index 73af5cf02ca..b5544415285 100644 --- a/config/locales/crowdin/ms.yml +++ b/config/locales/crowdin/ms.yml @@ -107,9 +107,8 @@ ms: trial: "Trial" jemalloc_allocator: Pengagih ingatan Jemalloc journal_aggregation: - explanation: - text: "Tindakan individu pengguna (cth. mengemas kini pakej kerja dua kali) dikumpulkan ke dalam satu tindakan tunggal jika perbezaan umur mereka kurang daripada tempoh masa yang ditetapkan. Mereka akan dipaparkan sebagai tindakan tunggal dalam aplikasi. Ini juga akan menangguhkan pemberitahuan dengan jumlah masa yang sama, mengurangkan bilangan e-mel yang dihantar serta akan memberi kesan kepada penagguhan %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1358,9 +1357,6 @@ ms: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3457,7 +3453,6 @@ ms: label_float: "Terapung" label_folder: "Folder" label_follows: "ikuti" - label_force_user_language_to_default: "Tetapkan bahasa pengguna yang mempunyai bahasa yang tidak dibenarkan kepada default" label_form_configuration: "Konfigurasi borang" label_formula: "Formula" label_gantt_chart: "Carta Gantt" @@ -3836,12 +3831,6 @@ ms: label_user_named: "Pengguna %{name}" label_user_activity: "aktiviti %{value}" label_user_anonymous: "Anonim" - label_user_mail_option_all: "Untuk sebarang peristiwa pada semua projek saya" - label_user_mail_option_none: "Tiada peristiwa" - label_user_mail_option_only_assigned: "Hanya untuk perkara yang ditugaskan kepada saya" - label_user_mail_option_only_my_events: "Hanya untuk perkara yang saya perhatikan atau yang saya terlibat dalam" - label_user_mail_option_only_owner: "Hanya untuk perkara yang merupakan saya pemiliknya" - label_user_mail_option_selected: "Untuk sebarang peristiwa pada projek yang terpilih sahaja" label_user_menu: "User menu" label_user_new: "Pengguna baharu" label_user_plural: "Pengguna-pengguna" @@ -3881,7 +3870,6 @@ ms: label_wiki_show_index_page_link: "Paparkan item submenu 'Jadual Kandungan'" label_wiki_show_menu_item: "Paparkan sebagai item menu dalam navigasi projek" label_wiki_show_new_page_link: "Paparkan item submenu 'Cipta laman anak baharu'" - label_wiki_show_submenu_item: "Paparkan sebagai item submenu " label_wiki_start: "Halaman mula" label_work: "Kerja" label_work_package: "Pakej kerja" @@ -4950,7 +4938,7 @@ ms: text_default_administrator_account_changed: "Akaun default pentadbir diubah" text_default_encoding: "Default: UTF-8" text_destroy: "Padam" - text_destroy_with_associated: "Terdapat objek tambahan yang berkaitan dengan pakej kerja yang perlu dipadam. Objek tersebut adalah daripada jenis berikut:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Apakah yang anda ingin lakukan?" text_diff_truncated: "... Perbezaan ini dipendekkan kerana ia melebihi saiz maksimum yang boleh dipaparkan." text_email_delivery_not_configured: "Penghantaran e-mel belum dikonfigurasi, dan pemberitahuan telah dinyahaktifkan.\nKonfigurasikan server SMTP anda untuk mengaktifkannya." diff --git a/config/locales/crowdin/ne.yml b/config/locales/crowdin/ne.yml index 641291065b7..28f107c8494 100644 --- a/config/locales/crowdin/ne.yml +++ b/config/locales/crowdin/ne.yml @@ -107,9 +107,8 @@ ne: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ ne: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ ne: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ ne: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ ne: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ ne: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index 7f8c913e427..b17ffaa38c7 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -107,9 +107,8 @@ nl: trial: "Trial" jemalloc_allocator: Jemalloc geheugentoewijzer journal_aggregation: - explanation: - text: "Individuele acties van een gebruiker (bijv. het bijwerken van een werkpakket twee keer) wordt samengevoegd tot een enkele actie als hun leeftijdverschil minder is dan de aangegeven timespat. Ze worden weergegeven als een enkele actie binnen de applicatie. Dit zal ook meldingen vertragen met dezelfde tijd die het aantal verstuurde e-mails vermindert en zal ook %{webhook_link} vertraging beïnvloeden." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1371,9 +1370,6 @@ nl: dependencies: "Afhankelijkheden" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3511,7 +3507,6 @@ nl: label_float: "Zwevend" label_folder: "Map" label_follows: "volgt" - label_force_user_language_to_default: "Taal van gebruikers met een niet toegestane taal op standaard instellen" label_form_configuration: "Formulierconfiguratie" label_formula: "Formula" label_gantt_chart: "Gantt-grafiek" @@ -3890,12 +3885,6 @@ nl: label_user_named: "Gebruiker %{name}" label_user_activity: "%{value} activiteit" label_user_anonymous: "Anoniem" - label_user_mail_option_all: "Voor een evenement op al mijn projecten" - label_user_mail_option_none: "Geen evenementen" - label_user_mail_option_only_assigned: "Alleen voor dingen die aan mij toegewezen zijn" - label_user_mail_option_only_my_events: "Alleen voor dingen die ik kijk of waar ik bij betrokken ben" - label_user_mail_option_only_owner: "Alleen voor dingen waar ik de eigenaar van ben" - label_user_mail_option_selected: "Voor een gebeurtenis op de geselecteerde projecten alleen" label_user_menu: "User menu" label_user_new: "Nieuwe gebruiker" label_user_plural: "Gebruikers" @@ -3935,7 +3924,6 @@ nl: label_wiki_show_index_page_link: "Toon submenu item 'Inhoudsopgave'" label_wiki_show_menu_item: "Weergeven als menu-item in project navigatie" label_wiki_show_new_page_link: "Vervolgmenu-item 'Maken nieuwe onderliggende pagina' weergeven" - label_wiki_show_submenu_item: "Toon als submenu-item van " label_wiki_start: "Startpagina" label_work: "Werk" label_work_package: "Werkpakket" @@ -5006,7 +4994,7 @@ nl: text_default_administrator_account_changed: "Standaard beheerdersaccount gewijzigd" text_default_encoding: "Standaardinstelling: UTF-8" text_destroy: "Verwijderen" - text_destroy_with_associated: "Er zijn extra objecten gerelateerd aan het/de te verwijderen werkpakket(ten). Deze objecten zijn van de soorten:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Wat wilt u doen?" text_diff_truncated: "... deze diff is truncated vanwege dat de maximale grote is overschreden om te tonen." text_email_delivery_not_configured: "E-mailbezorging is niet geconfigureerd, en meldingen zijn uitgeschakeld.\nConfigureer uw SMTP-server om deze in te schakelen." diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index 28cfb7cae48..b05690137f1 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -107,9 +107,8 @@ trial: "Trial" jemalloc_allocator: Jemalloc minne allokator journal_aggregation: - explanation: - text: "Individuelle handlinger av en bruker (f.eks. oppdatering av en arbeidspakke to ganger) aggregeres til en enkelt handling hvis aldersforskjellen er mindre enn det spesifiserte tidsrommet. De vil bli vist som en enkelt handling i programmet. Dette vil også forsinke varslinger med samme tidsperiode som reduserer antall e-poster som sendes, og vil også påvirke %{webhook_link} forsinkelse." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1374,9 +1373,6 @@ dependencies: "Avhengigheter" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3514,7 +3510,6 @@ label_float: "Flyt" label_folder: "Mappe" label_follows: "følger" - label_force_user_language_to_default: "Sett språk for brukere som har et ikke-tillatt språk som standard" label_form_configuration: "Skjema konfigurering" label_formula: "Formula" label_gantt_chart: "Gantt diagram" @@ -3893,12 +3888,6 @@ label_user_named: "Bruker %{name}" label_user_activity: "%{value}s aktivitet" label_user_anonymous: "Anonym" - label_user_mail_option_all: "For enhver hendelse i alle mine prosjekter" - label_user_mail_option_none: "Ingen hendelser" - label_user_mail_option_only_assigned: "Kun for ting jeg er involvert i" - label_user_mail_option_only_my_events: "Kun for ting jeg overvåker eller er involvert i" - label_user_mail_option_only_owner: "Kun for ting jeg står som eier av" - label_user_mail_option_selected: "For alle hendelser kun på valgte prosjekter" label_user_menu: "User menu" label_user_new: "Ny bruker" label_user_plural: "Brukere" @@ -3938,7 +3927,6 @@ label_wiki_show_index_page_link: "Vis undermenyelement 'Innholdsfortegnelse'" label_wiki_show_menu_item: "Vis som menyelement i prosjektmeny" label_wiki_show_new_page_link: "Vis undermenyelement 'Opprett ny underside'" - label_wiki_show_submenu_item: "Vis som undermenyelement for " label_wiki_start: "Startside" label_work: "Arbeid" label_work_package: "Arbeidspakke" @@ -5010,7 +4998,7 @@ text_default_administrator_account_changed: "Standard administratorkonto er endret" text_default_encoding: "Standard: UTF-8" text_destroy: "Slett" - text_destroy_with_associated: "Det er andre objekter tilknyttet arbeidspakken(e) som er i ferd med å bli slettet. Disse objektene er av følgende typer:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Hva vil du gjøre?" text_diff_truncated: "... Denne diff ble forkortet fordi den overstiger maksimalgrensen for hva som kan vises." text_email_delivery_not_configured: "E-postlevering er ikke konfigurert, og varsler er deaktivert.\nKonfigurer SMTP-serveren for å aktivere dem." diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index 350f6022d5b..04aaf6e47be 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -107,9 +107,8 @@ pl: trial: "Wersja próbna" jemalloc_allocator: Alokator pamięci Jemalloc journal_aggregation: - explanation: - text: "Indywidualne działania użytkownika (np. dwukrotna aktualizacja pakietu roboczego) są agregowane w jedno działanie, jeśli różnica czasowa między nimi jest mniejsza niż określony przedział czasowy. Będą one wyświetlane jako jedno działanie w aplikacji. Spowoduje to również opóźnienie powiadomień o tę samą ilość czasu, zmniejszając liczbę wysyłanych wiadomości e-mail i wpłynie też na opóźnienie %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1401,9 +1400,6 @@ pl: dependencies: "Zależności" activerecord: attributes: - agile/sprint: - sharing: "Udostępnianie" - finish_date: "Data zakończenia" jira_import: projects: "Projekty" "import/jira": @@ -3623,7 +3619,6 @@ pl: label_float: "Liczba rzeczywista" label_folder: "Katalog" label_follows: "Następujący" - label_force_user_language_to_default: "Ustaw język domyślny dla użytkowników o innych ustawieniach językowych niż dozwolone" label_form_configuration: "Konfiguracja formularza" label_formula: "Wzór" label_gantt_chart: "Wykres Gantta" @@ -4002,12 +3997,6 @@ pl: label_user_named: "Użytkownik %{name}" label_user_activity: "Aktywność użytkownika: %{value}" label_user_anonymous: "Anonimowy" - label_user_mail_option_all: "Dla każdego zdarzenia we wszystkich moich projektach" - label_user_mail_option_none: "Brak zdarzeń" - label_user_mail_option_only_assigned: "Tylko do rzeczy do których jestem przydzielony" - label_user_mail_option_only_my_events: "Tylko to co obserwuję lub jestem zaangażowany" - label_user_mail_option_only_owner: "Tylko do rzeczy, których jestem właścicielem" - label_user_mail_option_selected: "Dla każdego zdarzenia w wybranych projektach" label_user_menu: "Menu użytkownika" label_user_new: "Nowy użytkownik" label_user_plural: "Użytkownicy" @@ -4047,7 +4036,6 @@ pl: label_wiki_show_index_page_link: "Pokaż element podmenu \"Spis treści\"" label_wiki_show_menu_item: "Pokaż jako element menu w nawigacji projektu" label_wiki_show_new_page_link: "Pokaż element podmenu \"Utwórz nową stronę podrzędną\"" - label_wiki_show_submenu_item: "Pokaż jako pozycję podmenu " label_wiki_start: "Strona startowa" label_work: "Praca" label_work_package: "Zadanie" @@ -5120,7 +5108,7 @@ pl: text_default_administrator_account_changed: "Domyślne konto administratora zostało zmienione" text_default_encoding: "Domyślnie: UTF-8" text_destroy: "Usuń" - text_destroy_with_associated: "Istnieją dodatkowe obiekty powiązane z pakietami roboczymi, które będą usunięte. Te obiekty są następujących typów:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Co chcesz zrobić?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Dostarczanie poczty elektronicznej nie jest skonfigurowane, a powiadomienia są wyłączone.\nSkonfiguruj swój serwer SMTP, aby je włączyć." diff --git a/config/locales/crowdin/pt-BR.yml b/config/locales/crowdin/pt-BR.yml index 2aeaff87a1b..5da435df53b 100644 --- a/config/locales/crowdin/pt-BR.yml +++ b/config/locales/crowdin/pt-BR.yml @@ -107,9 +107,8 @@ pt-BR: trial: "Avaliação" jemalloc_allocator: Alocador de memória Jemalloc journal_aggregation: - explanation: - text: "As ações individuais de um usuário (por exemplo, atualizar um pacote de trabalho duas vezes) são agregadas em uma única ação se o intervalo de tempo for menor que o intervalo especificado. Eles serão exibidos como uma única ação dentro do aplicativo. Isso também atrasará as notificações no mesmo intervalo de tempo, reduzindo o número de e-mails enviados e também afetará o atraso de %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Importar" jira: @@ -1372,9 +1371,6 @@ pt-BR: dependencies: "Dependências" activerecord: attributes: - agile/sprint: - sharing: "Compartilhamento" - finish_date: "Data de término" jira_import: projects: "Projetos" "import/jira": @@ -3512,7 +3508,6 @@ pt-BR: label_float: "Ponto flutuante" label_folder: "Pasta" label_follows: "Segue" - label_force_user_language_to_default: "Configurar o idioma de usuário que tem uma linguagem não permitida como padrão" label_form_configuration: "Configuração do formulário" label_formula: "Fórmula" label_gantt_chart: "Gráfico de Gantt" @@ -3891,12 +3886,6 @@ pt-BR: label_user_named: "Usuário %{name}" label_user_activity: "atividade do %{value}" label_user_anonymous: "Anônimo" - label_user_mail_option_all: "Para qualquer evento em todos os meus projetos" - label_user_mail_option_none: "Não há eventos" - label_user_mail_option_only_assigned: "Só para coisas que estou designado" - label_user_mail_option_only_my_events: "Somente para as coisas que eu acompanho ou participo" - label_user_mail_option_only_owner: "Somente para as coisas que eu sou o dono" - label_user_mail_option_selected: "Para qualquer evento somente nos projetos selecionados" label_user_menu: "Menu de usuários" label_user_new: "Novo usuário" label_user_plural: "Usuários" @@ -3936,7 +3925,6 @@ pt-BR: label_wiki_show_index_page_link: "Mostrar submenu 'Tabela de Conteúdos'" label_wiki_show_menu_item: "Mostrar como item de menu de navegação do projeto" label_wiki_show_new_page_link: "Mostrar o item de submenu 'Criar nova página filho'" - label_wiki_show_submenu_item: "Mostrar como item do submenu de " label_wiki_start: "Página inicial" label_work: "Trabalho" label_work_package: "Pacote de trabalho" @@ -5007,7 +4995,7 @@ pt-BR: text_default_administrator_account_changed: "Conta do administrador padrão alterada" text_default_encoding: "Padrão: UTF-8" text_destroy: "Excluir" - text_destroy_with_associated: "Existem objetos adicionais associados com o pacote de trabalho que serão excluídos. Esses objetos são dos seguintes tipos:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "O que você quer fazer?" text_diff_truncated: "... Este diff foi truncado porque excede o tamanho máximo que pode ser exibido." text_email_delivery_not_configured: "O envio de e-mails não está configurado e as notificações estão desativadas.\nConfigure seu servidor de SMTP para ativá-los." diff --git a/config/locales/crowdin/pt-PT.yml b/config/locales/crowdin/pt-PT.yml index fbd8a8483b7..d9b50b5c425 100644 --- a/config/locales/crowdin/pt-PT.yml +++ b/config/locales/crowdin/pt-PT.yml @@ -107,9 +107,8 @@ pt-PT: trial: "Teste" jemalloc_allocator: Alocador de memória Jemalloc journal_aggregation: - explanation: - text: "As ações individuais de um utilizador (por exemplo, atualizar um pacote de trabalho duas vezes) são agregadas numa única ação se a sua diferença de idade for menor que o intervalo de tempo especificado. Serão mostradas como uma única ação dentro da aplicação. Também vai atrasar as notificações pelo mesmo período de tempo, o que reduz o número de e-mails enviados, e afeta ainda o atraso de %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Importar" jira: @@ -1372,9 +1371,6 @@ pt-PT: dependencies: "Dependências" activerecord: attributes: - agile/sprint: - sharing: "Partilhar" - finish_date: "Data de término" jira_import: projects: "Projetos" "import/jira": @@ -3512,7 +3508,6 @@ pt-PT: label_float: "Float" label_folder: "Pasta" label_follows: "segue" - label_force_user_language_to_default: "Idioma de utilizadores definida com uma linguagem não permitida como padrão" label_form_configuration: "Configuração do formulário" label_formula: "Fórmula" label_gantt_chart: "Gráfico de Gantt" @@ -3891,12 +3886,6 @@ pt-PT: label_user_named: "Utilizador %{name}" label_user_activity: "Atividade de %{value}" label_user_anonymous: "Anónimo" - label_user_mail_option_all: "Para qualquer evento em todos os meus projetos" - label_user_mail_option_none: "Sem eventos" - label_user_mail_option_only_assigned: "Só para coisas que estou designado" - label_user_mail_option_only_my_events: "Apenas para coisas que observo ou em que estou envolvido" - label_user_mail_option_only_owner: "Apenas para coisas das quais sou proprietário" - label_user_mail_option_selected: "Para qualquer evento apenas nos projetos selecionados" label_user_menu: "Menu do utilizador" label_user_new: "Novo Utilizador" label_user_plural: "Utilizadores" @@ -3936,7 +3925,6 @@ pt-PT: label_wiki_show_index_page_link: "Mostrar sub-menu 'Tabela de Conteúdos'" label_wiki_show_menu_item: "Mostrar como item de menu de navegação do projecto" label_wiki_show_new_page_link: "Mostrar o item de sub-menu 'Criar nova página filha'" - label_wiki_show_submenu_item: "Mostrar como item do sub-menu de " label_wiki_start: "Página inicial" label_work: "Trabalho" label_work_package: "Pacote de trabalho" @@ -5005,7 +4993,7 @@ pt-PT: text_default_administrator_account_changed: "Conta padrão de administrador alterada" text_default_encoding: "Padrão: UTF-8" text_destroy: "Eliminar" - text_destroy_with_associated: "Existem objetos adicionais associados com o pacote de trabalho que está a ser apagado. Esses objetos são dos seguintes tipos:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "O que deseja fazer?" text_diff_truncated: "... Este diff foi truncado porque excede o tamanho máximo que pode ser mostrado." text_email_delivery_not_configured: "A entrega de emails não está configurada e as notificações estão desativadas.\nConfigure o seu servidor de SMTP para ativar." diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index 44400e81894..6742c0cb934 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -107,9 +107,8 @@ ro: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Acțiunile individuale ale unui utilizator (de exemplu, actualizarea de două ori a unui pachet de lucru) sunt agregate într-o singură acțiune dacă diferența de vechime dintre ele este mai mică decât intervalul de timp specificat. Acestea vor fi afișate ca o singură acțiune în cadrul aplicației. De asemenea, acest lucru va întârzia notificările cu aceeași perioadă de timp, reducând numărul de e-mailuri trimise și va afecta, de asemenea, întârzierea %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1390,9 +1389,6 @@ ro: dependencies: "Dependenţe" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3524,7 +3520,7 @@ ro: label_duplicated_by: "dublat de" label_duplicate: "duplicat" label_duplicates: "dublează" - label_edit: "Editează" + label_edit: "Editare" label_edit_x: "Editare: %{x}" label_view_x: "View: %{x}" label_enable_multi_select: "Comutare selecție multiplă" @@ -3571,7 +3567,6 @@ ro: label_float: "Număr real" label_folder: "Dosar" label_follows: "urmează după" - label_force_user_language_to_default: "Setare limbă pentru utilizatorii care au o limbă implicită nepermisă" label_form_configuration: "Configurare formular" label_formula: "Formulă" label_gantt_chart: "Grafic Gantt" @@ -3582,7 +3577,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}" @@ -3950,12 +3945,6 @@ ro: label_user_named: "Utilizator %{name}" label_user_activity: "Activitatea lui %{value}" label_user_anonymous: "Anonim" - label_user_mail_option_all: "Pentru orice eveniment în toate proiectele mele" - label_user_mail_option_none: "Pentru niciun eveniment" - label_user_mail_option_only_assigned: "Doar pentru tichete pe care trebuie să le execut" - label_user_mail_option_only_my_events: "Doar pentru tichete pentru care sunt observator sau în care sunt implicat" - label_user_mail_option_only_owner: "Doar pentru tichete pentru care sunt responsabil" - label_user_mail_option_selected: "Pentru orice eveniment doar în proiectele selectate" label_user_menu: "User menu" label_user_new: "Utilizator nou" label_user_plural: "Utilizatori" @@ -3995,7 +3984,6 @@ ro: label_wiki_show_index_page_link: "Afișare submeniu 'Cuprins'" label_wiki_show_menu_item: "Afișare ca meniu în navigarea proiectului" label_wiki_show_new_page_link: "Afișează submeniu 'Creează pagină copil'" - label_wiki_show_submenu_item: "Afișare ca submeniu pentru " label_wiki_start: "Pagina de start" label_work: "Muncă" label_work_package: "Pachet de lucru" @@ -5068,7 +5056,7 @@ ro: text_default_administrator_account_changed: "Contul de administrator implicit a fost schimbat" text_default_encoding: "Implicit: UTF-8" text_destroy: "Șterge" - text_destroy_with_associated: "Există obiecte suplimentare asociate cu pachetul(ele) de lucru care urmează să fie șters(e). Aceste obiecte sunt de următoarele tipuri:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Ce vrei să faci?" text_diff_truncated: "... Acest diff a fost trunchiat deoarece depășește dimensiunea maximă care poate fi afișată." text_email_delivery_not_configured: "Livrarea e-mailurilor nu este configurată, iar notificările sunt dezactivate.\nConfigurați serverul SMTP pentru a le activa." diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index be1e1d1773a..999ed1c8670 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -107,9 +107,8 @@ ru: trial: "Пробная версия" jemalloc_allocator: Распределитель памяти Jemalloc journal_aggregation: - explanation: - text: "Личные действия пользователя (например, обновление пакета работ дважды) агрегируются в одно действие, если их разница во времени не больше указанной. Они будут отображаться как одно действие внутри приложения. Это также задерживает уведомление на такое же количество времени, уменьшая количество отправляемых писем и также повлияет на %{webhook_link} задержки." - link: "вебхук" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Импорт" jira: @@ -1403,9 +1402,6 @@ ru: dependencies: "Связи" activerecord: attributes: - agile/sprint: - sharing: "Совместное использование" - finish_date: "Дата окончания" jira_import: projects: "Проекты" "import/jira": @@ -3625,7 +3621,6 @@ ru: label_float: "Плавающий" label_folder: "Папка" label_follows: "Следует" - label_force_user_language_to_default: "Язык, который пользователям нельзя установить как действующий по-умолчанию" label_form_configuration: "Настройка форм" label_formula: "Формула" label_gantt_chart: "Диаграмма Ганта" @@ -4004,12 +3999,6 @@ ru: label_user_named: "Пользователь %{name}" label_user_activity: "%{value} деятельности" label_user_anonymous: "Анонимно" - label_user_mail_option_all: "Для любого события на моих проектах" - label_user_mail_option_none: "Нет событий" - label_user_mail_option_only_assigned: "Только для тех, которые мне поручены" - label_user_mail_option_only_my_events: "Только для тех, в которых я наблюдатель или участник" - label_user_mail_option_only_owner: "Только для тех, которыми я владею" - label_user_mail_option_selected: "Для любого события, но только для выбранных проектов" label_user_menu: "Пользовательское меню" label_user_new: "Новый пользователь" label_user_plural: "Пользователи" @@ -4049,7 +4038,6 @@ ru: label_wiki_show_index_page_link: "Показать пункт подменю «Содержание»" label_wiki_show_menu_item: "Показать как меню в навигации по проекту" label_wiki_show_new_page_link: "Показать пункт подменю «Создать новую дочернюю страницу»" - label_wiki_show_submenu_item: "Показать как пункт подменю " label_wiki_start: "Начальная страница" label_work: "Предполагаемое время" label_work_package: "Пакет работ" diff --git a/config/locales/crowdin/rw.yml b/config/locales/crowdin/rw.yml index f677e9eedcc..9cb101a9340 100644 --- a/config/locales/crowdin/rw.yml +++ b/config/locales/crowdin/rw.yml @@ -107,9 +107,8 @@ rw: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ rw: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ rw: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ rw: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ rw: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ rw: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/si.yml b/config/locales/crowdin/si.yml index d4d71e0ba5d..4c888d8305a 100644 --- a/config/locales/crowdin/si.yml +++ b/config/locales/crowdin/si.yml @@ -107,9 +107,8 @@ si: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ si: dependencies: "පරායත්තතා" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ si: label_float: "පාවෙන" label_folder: "ෆෝල්ඩරය" label_follows: "පහත සඳහන්" - label_force_user_language_to_default: "පෙරනිමියට අවසර නොලත් භාෂාවක් ඇති පරිශීලකයින්ගේ භාෂාව සකසන්න" label_form_configuration: "ආකෘති වින්යාසය" label_formula: "Formula" label_gantt_chart: "ගැන්ට් සටහන" @@ -3894,12 +3889,6 @@ si: label_user_named: "පරිශීලක %{name}" label_user_activity: "%{value}ගේ ක්රියාකාරකම්" label_user_anonymous: "නිර්නාමික" - label_user_mail_option_all: "මගේ සියලු ව්යාපෘතිවල ඕනෑම සිදුවීමක් සඳහා" - label_user_mail_option_none: "සිදුවීම් නැත" - label_user_mail_option_only_assigned: "මට පවරා ඇති දේවල් සඳහා පමණි" - label_user_mail_option_only_my_events: "මම නරඹන දේවල් සඳහා පමණක් හෝ මම සම්බන්ධ වෙනවා" - label_user_mail_option_only_owner: "මම අයිතිකරු වන්නේ දේවල් සඳහා පමණි" - label_user_mail_option_selected: "තෝරාගත් ව්යාපෘතිවල ඕනෑම සිදුවීමක් සඳහා පමණි" label_user_menu: "User menu" label_user_new: "නව පරිශීලක" label_user_plural: "පරිශීලකයන්" @@ -3939,7 +3928,6 @@ si: label_wiki_show_index_page_link: "උප මෙනු අයිතමය පෙන්වන්න 'අන්තර්ගත වගුව'" label_wiki_show_menu_item: "ව්යාපෘති සංචලනය තුළ මෙනු අයිතමය ලෙස පෙන්වන්න" label_wiki_show_new_page_link: "උප මෙනු අයිතමය පෙන්වන්න 'නව ළමා පිටුව සාදන්න'" - label_wiki_show_submenu_item: "උප මෙනු අයිතමය ලෙස පෙන්වන්න " label_wiki_start: "ආරම්භක පිටුව" label_work: "Work" label_work_package: "වැඩ පැකේජය" @@ -5011,7 +4999,7 @@ si: text_default_administrator_account_changed: "පෙරනිමි පරිපාලක ගිණුම වෙනස්" text_default_encoding: "පෙරනිමි: UTF-8" text_destroy: "මකන්න" - text_destroy_with_associated: "මකා දැමිය යුතු වැඩ පැකේජය (ය) සමඟ එකතු වී ඇති අතිරේක වස්තු තිබේ. එම වස්තූන් පහත සඳහන් වර්ග වලින් සමන්විත වේ:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "ඔබට කුමක් කිරීමට අවශ්යද?" text_diff_truncated: "... එය ප්රදර්ශනය කළ හැකි උපරිම ප්රමාණය ඉක්මවා යන නිසා මෙම diff ටන්ක කරන ලදී." text_email_delivery_not_configured: "විද්යුත් තැපැල් බෙදා හැරීම වින්යාසගත කර නොමැති අතර දැනුම්දීම් අක්රීය කර ඇත.\nඒවා සක්රීය කිරීම සඳහා ඔබේ SMTP සේවාදායකය සකසන්න." diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index 935f04db755..a31637cb0c3 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -107,9 +107,8 @@ sk: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1405,9 +1404,6 @@ sk: dependencies: "Závislosti" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3627,7 +3623,6 @@ sk: label_float: "Plávajúce (float)" label_folder: "Priečinok" label_follows: "nasleduje" - label_force_user_language_to_default: "Nastavenie jazyka užívateľov ktorí majú nastavený ako predvolený nepovolený jazyk" label_form_configuration: "Konfigurácia formulára" label_formula: "Formula" label_gantt_chart: "Ganttov diagram" @@ -4006,12 +4001,6 @@ sk: label_user_named: "Užívateľ %{name}" label_user_activity: "Aktivita používateľa %{value}" label_user_anonymous: "Anonymný" - label_user_mail_option_all: "Pre všetky udalosti všetkých mojich projektov" - label_user_mail_option_none: "Žiadne udalosti" - label_user_mail_option_only_assigned: "Len pre veci, na ktoré som priradený" - label_user_mail_option_only_my_events: "Len pre veci, ktoré sledujem, alebo v ktorých som zapojený" - label_user_mail_option_only_owner: "Len pre veci, ktorých som vlastníkom" - label_user_mail_option_selected: "Pre každú udalosť len na vybraných projektoch" label_user_menu: "User menu" label_user_new: "Nový uživateľ" label_user_plural: "Užívatelia" @@ -4051,7 +4040,6 @@ sk: label_wiki_show_index_page_link: "Zobraziť položku podmenu \"Obsah\"" label_wiki_show_menu_item: "Zobraziť ako položku menu v navigácii projektu" label_wiki_show_new_page_link: "Zobraziť položku submenu \"Vytvoriť novú podradenú stránku\"" - label_wiki_show_submenu_item: "Zobraziť ako položku submenu " label_wiki_start: "Úvodná stránka" label_work: "Work" label_work_package: "Pracovný balíček" @@ -5126,7 +5114,7 @@ sk: text_default_administrator_account_changed: "Predvolené nastavenie administrátorského účtu bolo zmenené" text_default_encoding: "Predvolené: UTF-8" text_destroy: "Odstrániť" - text_destroy_with_associated: "Existujú ďalšie objekty spojené s pracovným balíčkom(-ami), ktoré sa majú vymazať. Tieto objekty sú z týchto typov:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Čo chcete urobiť?" text_diff_truncated: "... Tento výpis rozdielov bol skrátený, pretože prekračuje maximálny počet riadkov, ktorý môže byť zobrazený." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index 3b6e3320b23..4b044911670 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -107,9 +107,8 @@ sl: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1404,9 +1403,6 @@ sl: dependencies: "Odvisnosti" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -2655,8 +2651,8 @@ sl: - "avgust" - "september" - "oktober" - - "november" - - "december" + - "November" + - "December" order: - :leto - :mesec @@ -3626,7 +3622,6 @@ sl: label_float: "Lebdeti" label_folder: "Mapa" label_follows: "Sledi" - label_force_user_language_to_default: "Nastavite jezik uporabnikov ki nimajo dovoljenja privzetega jezika" label_form_configuration: "Konfiguracija obrazca" label_formula: "Formula" label_gantt_chart: "Gantogram" @@ -4005,12 +4000,6 @@ sl: label_user_named: "Uporabnik %{name}" label_user_activity: "%{value}'s aktivnost" label_user_anonymous: "Anonimno" - label_user_mail_option_all: "Za vsak dogodek na vseh mojih projektih" - label_user_mail_option_none: "Ni dogodkov" - label_user_mail_option_only_assigned: "Samo za stvari, ki smo mi dodeljene" - label_user_mail_option_only_my_events: "Samo za stvari, ki jih opazujem ali v katere sem vpleten" - label_user_mail_option_only_owner: "Samo za stvari katerih lastnik sem" - label_user_mail_option_selected: "Za vsak dogodek samo na izbranih projektih..." label_user_menu: "User menu" label_user_new: "Nov uporabnik" label_user_plural: "Uporabniki" @@ -4050,7 +4039,6 @@ sl: label_wiki_show_index_page_link: "Pokaži postavko podmenija „Vsebina“" label_wiki_show_menu_item: "Prikaži kot element v meniju navigacija projekta" label_wiki_show_new_page_link: "Pokaži element v podmeniju »Ustvari novo podrejeno stran«" - label_wiki_show_submenu_item: "Prikaži kot element podmenija v" label_wiki_start: "Začetna stran" label_work: "Work" label_work_package: "Delovni paket" @@ -5125,7 +5113,7 @@ sl: text_default_administrator_account_changed: "Spremenjen privzeti administratorski račun" text_default_encoding: "Privzeto: UTF-8" text_destroy: "Izbriši" - text_destroy_with_associated: "Obstajajo dodatni objekti, povezani z delovnim paketom, ki jih je treba izbrisati. Ti predmeti so naslednje vrste:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Kaj želite storiti?" text_diff_truncated: "... Ta sprememba je bila odsekana ker presega največjo velikost ki je lahko prikazana." text_email_delivery_not_configured: "Dostava e-pošte ni konfigurirana in obvestila so onemogočena.\nKonfigurirajte strežnik SMTP, da jih omogočite." diff --git a/config/locales/crowdin/sr.yml b/config/locales/crowdin/sr.yml index cbbf37ac558..ee64e4390f2 100644 --- a/config/locales/crowdin/sr.yml +++ b/config/locales/crowdin/sr.yml @@ -107,9 +107,8 @@ sr: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1390,9 +1389,6 @@ sr: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3571,7 +3567,6 @@ sr: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3950,12 +3945,6 @@ sr: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3995,7 +3984,6 @@ sr: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5069,7 +5057,7 @@ sr: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index f3c7673671d..fe6b260b1ce 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -107,9 +107,8 @@ sv: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ sv: dependencies: "Beroenden" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ sv: label_float: "Flyttal" label_folder: "Mapp" label_follows: "följer" - label_force_user_language_to_default: "Välj standardspråk för användare som har ett icke tillåtet språk" label_form_configuration: "Ställ in formulär" label_formula: "Formel" label_gantt_chart: "Gantt-schema" @@ -3894,12 +3889,6 @@ sv: label_user_named: "Användare %{name}" label_user_activity: "%{value}s aktivitet" label_user_anonymous: "Anonym" - label_user_mail_option_all: "För alla händelser i alla mina projekt" - label_user_mail_option_none: "Inga händelser" - label_user_mail_option_only_assigned: "Bara är saker jag tilldelade" - label_user_mail_option_only_my_events: "Endast för saker jag bevakar eller deltar i" - label_user_mail_option_only_owner: "Endast för saker är jag ägare till" - label_user_mail_option_selected: "Endast för alla händelser i valda projekt" label_user_menu: "Användarmeny" label_user_new: "Ny användare" label_user_plural: "Användare" @@ -3939,7 +3928,6 @@ sv: label_wiki_show_index_page_link: "Visa undermenyn \"Innehållsförteckning\"" label_wiki_show_menu_item: "Visa som menyobjekt i projektets meny" label_wiki_show_new_page_link: "Visa undermenyn \"Skapa ny underordnad sida\"" - label_wiki_show_submenu_item: "Visa som undermenyalternativ för " label_wiki_start: "Startsida" label_work: "Arbete" label_work_package: "Arbetspaket" @@ -5009,7 +4997,7 @@ sv: text_default_administrator_account_changed: "Standardadministratörskontot har ändrats" text_default_encoding: "Standard: UTF-8" text_destroy: "Ta bort" - text_destroy_with_associated: "Det finns ytterligare objekt kopplade med det/de arbetspaket som ska tas bort. Dessa objekt är av följande typer:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Vad vill du göra?" text_diff_truncated: "... Denna diff trunkerades eftersom den överskrider den maximala storleken som kan visas." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/th.yml b/config/locales/crowdin/th.yml index 43131ee6cef..ba4d2d5c081 100644 --- a/config/locales/crowdin/th.yml +++ b/config/locales/crowdin/th.yml @@ -107,9 +107,8 @@ th: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1360,9 +1359,6 @@ th: dependencies: "ส่วนที่อ้างอิง" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3459,7 +3455,6 @@ th: label_float: "ลอย" label_folder: "Folder" label_follows: "ดังต่อไปนี้" - label_force_user_language_to_default: "พบภาษาที่ไม่อนุญาตให้ใช้เป็นค่าเริ่มต้น ในชุดภาษาของผู้ใช้" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3838,12 +3833,6 @@ th: label_user_named: "User %{name}" label_user_activity: "กิจกรรมของ %{value}" label_user_anonymous: "ไม่ระบุชื่อ" - label_user_mail_option_all: "สำหรับเหตุการณ์ใด ๆ ก็ตามในโครงการทั้งหมดของฉัน" - label_user_mail_option_none: "ไม่พบเหตุการณ์" - label_user_mail_option_only_assigned: "เฉพาะสิ่งที่ฉันได้รับมอบหมาย" - label_user_mail_option_only_my_events: "เฉพาะสิ่งที่ฉันเฝ้าดูหรือมีความเกี่ยวข้องด้วย" - label_user_mail_option_only_owner: "เฉพาะสิ่งที่ฉันเป็นเจ้าของ" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "ผู้ใช้ใหม่" label_user_plural: "ผู้ใช้" @@ -3883,7 +3872,6 @@ th: label_wiki_show_index_page_link: "แสดงเมนูย่อยของ 'สารบัญ'" label_wiki_show_menu_item: "แสดงเป็นเมนูในการนำทางของโครงการ" label_wiki_show_new_page_link: "แสดงรายการเมนูย่อย 'สร้างหน้าย่อยใหม่'" - label_wiki_show_submenu_item: "แสดงเป็นรายการเมนูย่อยของ " label_wiki_start: "เพจเริ่มต้น" label_work: "Work" label_work_package: "ชุดภารกิจ" @@ -4953,7 +4941,7 @@ th: text_default_administrator_account_changed: "มีการเปลี่ยนค่าตั้งต้นของผู้ดูแลระบบ" text_default_encoding: "ค่าเริ่มต้น: UTF-8" text_destroy: "ลบ" - text_destroy_with_associated: "มีการลบออบเจคต์ที่เกี่ยวข้องกับชุดภารกิจ ซึ่งเป็นประเภทดังต่อไปนี้:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "คุณต้องการจะทำอะไร ?" text_diff_truncated: "... มีการตัดผลต่างเนื่องจากมีความยาวเกินกว่าที่สามารถแสดงผลได้" text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index 8dacce9c779..77e7ecaff7b 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -107,9 +107,8 @@ tr: trial: "Deneme" jemalloc_allocator: Jemalloc bellek ayırıcı journal_aggregation: - explanation: - text: "Bir kullanıcının bireysel eylemleri (örneğin, bir iş paketini iki kez güncelleme), yaş farkı belirtilen zaman aralığından azsa tek bir eylemde toplanır. Uygulama içinde tek bir eylem olarak görüntülenecektir. Bu aynı zamanda gönderilen e-posta sayısını azaltarak bildirimleri aynı süre kadar geciktirecek ve ayrıca %{webhook_link} gecikmesini etkileyecektir." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ tr: dependencies: "Bağımlılıklar" activerecord: attributes: - agile/sprint: - sharing: "Paylaşım" - finish_date: "Bitiş tarihi" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ tr: label_float: "Ondalık" label_folder: "Klasör" label_follows: "takip eder" - label_force_user_language_to_default: "Varsayılan olmayan izin verildi bir dil olan kullanıcı dilini ayarlama" label_form_configuration: "Formu yapılandırma" label_formula: "Formül" label_gantt_chart: "Gantt görünümü" @@ -3894,12 +3889,6 @@ tr: label_user_named: "%{name} kullanıcısı" label_user_activity: "%{value}'ın faaliyet" label_user_anonymous: "Anonim" - label_user_mail_option_all: "Tüm projelerimdeki herhangi bir olay için" - label_user_mail_option_none: "Etkinlik yok" - label_user_mail_option_only_assigned: "Sadece bana atanan şeyler için" - label_user_mail_option_only_my_events: "Sadece takip ettiğim ya da dahil olduklarım için" - label_user_mail_option_only_owner: "Sadece sahibi olduğum şeyler için" - label_user_mail_option_selected: "Yalnızca seçilen projelerdeki herhangi bir etkinlik için" label_user_menu: "Kullanıcı menüsü" label_user_new: "Yeni kullanıcı" label_user_plural: "Kullanıcılar" @@ -3939,7 +3928,6 @@ tr: label_wiki_show_index_page_link: "Alt menü öğesini göster 'İçindekiler tablosu'" label_wiki_show_menu_item: "Proje gezintisinde menü öğesi olarak göster" label_wiki_show_new_page_link: "Alt menü öğesini göster 'Yeni alt sayfa oluştur'" - label_wiki_show_submenu_item: "Alt menü öğesi olarak göster " label_wiki_start: "Başlangıç sayfası" label_work: "Çalışma" label_work_package: "İş paketi" @@ -5009,7 +4997,7 @@ tr: text_default_administrator_account_changed: "Varsayılan yönetici hesabı değişti" text_default_encoding: "Varsayılan: UTF-8" text_destroy: "Sil" - text_destroy_with_associated: "Silinecek iş paketleri ile birlikte ek nesne(ler) vardır. Bu nesneler aşağıdaki türdedir:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Ne yapmak istiyorsun?" text_diff_truncated: "... Bu fark, görüntülenebilecek maksimum boyutu aştığı için kesildi." text_email_delivery_not_configured: "E-posta teslimi yapılandırılmamış ve bildirimler devre dışı bırakılmış.\nSMTP sunucunuzu etkinleştirmek için yapılandırın." diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index d22654dfd61..a3868bf6259 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -107,9 +107,8 @@ uk: trial: "Пробний період" jemalloc_allocator: Розподіл пам'яті Jemalloc journal_aggregation: - explanation: - text: "Окремі дії користувача (напр., оновлення робочого пакета двічі) зводяться в одну дію, якщо відмінність у часі між ними менша за вказаний проміжок часу. Їх буде виведено як окремі дії в межах додатка. Крім того, це призведе до затримки сповіщень на такий самий проміжок часу, що зменшить кількість електронних листів, які надсилатимуться, а також вплине на затримку %{webhook_link}." - link: "вебгука" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Iмпорт" jira: @@ -1399,9 +1398,6 @@ uk: dependencies: "Залежності" activerecord: attributes: - agile/sprint: - sharing: "Надання доступу" - finish_date: "Дата завершення" jira_import: projects: "Проєкти" "import/jira": @@ -3621,7 +3617,6 @@ uk: label_float: "З плаваючою крапкою" label_folder: "Папка" label_follows: "слідкувати" - label_force_user_language_to_default: "Встановити мову користувачів, які мають не підтримувану мову, за умовчанням" label_form_configuration: "Конфігурація форми" label_formula: "Формула" label_gantt_chart: "Діаграма Ґанта" @@ -3659,7 +3654,7 @@ uk: label_index_by_title: "Індекс за назвою" label_information: "Інформація" label_information_plural: "Інформація" - label_installation_guides: "Інструкції із встановлення" + label_installation_guides: "Інструкції зі встановлення" label_integer: "Ціле число" label_interface: "Інтерфейс" label_internal: "Власне" @@ -4000,12 +3995,6 @@ uk: label_user_named: "User %{name}" label_user_activity: "Дії користувача %{value}" label_user_anonymous: "Невідомий" - label_user_mail_option_all: "Для всіх подій у всіх моїх проектах" - label_user_mail_option_none: "Ніяких подій" - label_user_mail_option_only_assigned: "Тільки за те що я призначений" - label_user_mail_option_only_my_events: "Тільки для речей які я дивлюся чи я займаюся" - label_user_mail_option_only_owner: "Тільки для речей, за якими я спостерігаю або є власником" - label_user_mail_option_selected: "Для будь-якої події на вибраних проектах" label_user_menu: "Меню користувача" label_user_new: "Новий користувач" label_user_plural: "Користувачі" @@ -4045,7 +4034,6 @@ uk: label_wiki_show_index_page_link: "Показати пункт підменю \"Зміст\"" label_wiki_show_menu_item: "Показати як пункт меню в навігації по проекту" label_wiki_show_new_page_link: "Показати елемент підменю \"Створити нову початкову сторінку\"" - label_wiki_show_submenu_item: "Показати як пункт підменю " label_wiki_start: "Початкова сторінка" label_work: "Робота" label_work_package: "Робочий пакет" @@ -5119,7 +5107,7 @@ uk: text_default_administrator_account_changed: "Обліковий запис адміністратора за замовчуванням змінений" text_default_encoding: "По замовчуванню: UTF-8" text_destroy: "Видалити" - text_destroy_with_associated: "Існують додаткові об'єкти, асоційовані з робочими пакетами, які потрібно видалити. Ці об'єкти мають наступні типи:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Що ти хочеш зробити?" text_diff_truncated: "... Це порівняння (diff) було скорочене, оскільки воно перевищує максимальний розмір, який можна відобразити." text_email_delivery_not_configured: "Доставку електронної пошти не налаштовано, і сповіщення вимкнуто.\nНалаштуйте свій SMTP-сервер, щоб увімкнути їх." diff --git a/config/locales/crowdin/uz.yml b/config/locales/crowdin/uz.yml index 6ba7e0b3772..b223f6b21ec 100644 --- a/config/locales/crowdin/uz.yml +++ b/config/locales/crowdin/uz.yml @@ -107,9 +107,8 @@ uz: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1375,9 +1374,6 @@ uz: dependencies: "Dependencies" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3515,7 +3511,6 @@ uz: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -3894,12 +3889,6 @@ uz: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -3939,7 +3928,6 @@ uz: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5011,7 +4999,7 @@ uz: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index 4fbc0443545..a97674e7712 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -107,9 +107,8 @@ vi: trial: "thử nghiệm" jemalloc_allocator: Bộ cấp phát bộ nhớ Jemalloc journal_aggregation: - explanation: - text: "Các hành động riêng lẻ của người dùng (ví dụ: cập nhật gói công việc hai lần) được tổng hợp thành một hành động nếu chênh lệch tuổi tác của họ nhỏ hơn khoảng thời gian đã chỉ định. Chúng sẽ được hiển thị dưới dạng một hành động duy nhất trong ứng dụng. Điều này cũng sẽ làm chậm thông báo với cùng một khoảng thời gian làm giảm số lượng email được gửi và cũng sẽ ảnh hưởng đến độ trễ %{webhook_link}." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1358,9 +1357,6 @@ vi: dependencies: "phụ thuộc" activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -2232,7 +2228,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" @@ -3457,7 +3453,6 @@ vi: label_float: "Số thực" label_folder: "Thư mục" label_follows: "theo dõi" - label_force_user_language_to_default: "Có một ngôn ngữ không được cho phép để mặc định trong thiết lập ngôn ngữ cho người sử dụng" label_form_configuration: "Mẫu cấu hình" label_formula: "Công thức" label_gantt_chart: "Biểu đồ Gantt" @@ -3831,17 +3826,11 @@ 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" label_user_anonymous: "vô danh" - label_user_mail_option_all: "Bất kỳ sự kiện trên tất cả dự án của tôi" - label_user_mail_option_none: "Không có sự kiện" - label_user_mail_option_only_assigned: "Chỉ những thứ tôi được phân công" - label_user_mail_option_only_my_events: "Chỉ những thứ tôi theo dõi hoặc liên quan" - label_user_mail_option_only_owner: "Chỉ những thứ tôi sở hữu" - label_user_mail_option_selected: "Chỉ dành cho bất kỳ sự kiện nào trên các dự án đã chọn" label_user_menu: "Trình đơn người dùng" label_user_new: "Người dùng mới" label_user_plural: "Người dùng" @@ -3881,7 +3870,6 @@ vi: label_wiki_show_index_page_link: "Hiển thị mục menu con 'Mục lục'" label_wiki_show_menu_item: "Hiển thị dưới dạng mục menu trong điều hướng dự án" label_wiki_show_new_page_link: "Hiển thị mục menu con 'Tạo trang con mới'" - label_wiki_show_submenu_item: "Hiển thị dưới dạng mục menu con của" label_wiki_start: "Trang bắt đầu" label_work: "làm việc" label_work_package: "Work Package" @@ -4951,7 +4939,7 @@ vi: text_default_administrator_account_changed: "Tài khoản quản trị viên mặc định đã thay đổi" text_default_encoding: "Mặc định: UTF-8" text_destroy: "xóa" - text_destroy_with_associated: "Có các đối tượng bổ sung liên quan đến (các) gói công việc sẽ bị xóa. Các đối tượng đó có các loại sau:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Bạn muốn làm gì?" text_diff_truncated: "... Sự khác biệt này đã bị cắt bớt vì nó vượt quá kích thước tối đa có thể được hiển thị." text_email_delivery_not_configured: "Gửi email không được cấu hình và thông báo bị vô hiệu hóa.\nCấu hình máy chủ SMTP của bạn để kích hoạt chúng." diff --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 f9108877e93..99ffac2256a 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -88,7 +88,7 @@ zh-CN: token_caption: "要详细了解如何激活企业版,请查阅我们的[文档](docs_url)。" add_token: "上传企业版支持令牌" replace_token: "替换您当前的支持令牌" - order: "订购本地部署版的 Enterprise edition" + order: "订购本地部署的 Enterprise edition" paste: "粘贴您企业版的支持令牌" required_for_feature: "此功能仅限具激活的企业版支持令牌的订阅者使用。" enterprise_link: "如需了解详细信息,请单击此处。" @@ -107,9 +107,8 @@ zh-CN: trial: "试用" jemalloc_allocator: 使用 jemalloc 内存分配器 journal_aggregation: - explanation: - text: "如果用户的多项操作(例如,更新工作包两次)的时间间隔小于指定的时间跨度,则这些操作将被聚合为单个操作,并在应用程序中显示为单个操作。这也会将通知延迟同等的时间,从而减少电子邮件的发送数量,并且还会影响 %{webhook_link} 延迟。" - link: "Webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "导入" jira: @@ -1356,9 +1355,6 @@ zh-CN: dependencies: "依赖项" activerecord: attributes: - agile/sprint: - sharing: "共享" - finish_date: "结束日期" jira_import: projects: "项目" "import/jira": @@ -1445,7 +1441,7 @@ zh-CN: page: "页" row_count: "行数" column_count: "列数" - widgets: "微件" + widgets: "小部件" journal: notes: "备注" cause_type: "Cause 类型" @@ -3455,7 +3451,6 @@ zh-CN: label_float: "浮点数" label_folder: "文件夹" label_follows: "跟踪" - label_force_user_language_to_default: "设置拥有非允许语言的用户的默认语言" label_form_configuration: "表格配置" label_formula: "公式" label_gantt_chart: "甘特图" @@ -3749,7 +3744,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} 超时" @@ -3834,12 +3829,6 @@ zh-CN: label_user_named: "用户 %{name}" label_user_activity: "%{value} 的活动" label_user_anonymous: "匿名" - label_user_mail_option_all: "对于我所有项目的任何事件" - label_user_mail_option_none: "没有事件" - label_user_mail_option_only_assigned: "仅为指派给我的内容" - label_user_mail_option_only_my_events: "仅为我关注或参与的内容" - label_user_mail_option_only_owner: "仅为我是所有者的内容" - label_user_mail_option_selected: "只对于所选项目的任何事件......" label_user_menu: "用户菜单" label_user_new: "新用户" label_user_plural: "用户" @@ -3879,7 +3868,6 @@ zh-CN: label_wiki_show_index_page_link: "显示‘目录’子菜单项" label_wiki_show_menu_item: "显示为项目导航中的菜单项" label_wiki_show_new_page_link: "显示‘创建新的子页面’子菜单项" - label_wiki_show_submenu_item: "显示为子菜单项" label_wiki_start: "起始页" label_work: "工时" label_work_package: "工作包" @@ -4473,7 +4461,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: "如果您在配置新文件存储方面需要帮助,请查看文档:" @@ -4687,7 +4675,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: "计算 完成% 层次结构总数" @@ -4944,7 +4932,7 @@ zh-CN: text_default_administrator_account_changed: "更改默认管理员帐户" text_default_encoding: "默认值: UTF-8" text_destroy: "删除" - text_destroy_with_associated: "有额外的对象关联到要删除的工作包。这些对象是以下类型:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "你想做什么?" text_diff_truncated: "...这个比较被截断,因为它超出了可显示的最大大小。" text_email_delivery_not_configured: "电子邮件传送未配置,通知已禁用。\n配置您的 SMTP 服务器将其启用。" @@ -5114,7 +5102,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 f054aae52dd..f3e4e36d2c0 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -107,9 +107,8 @@ zh-TW: trial: "試用" jemalloc_allocator: Jemalloc 記憶體分配器 journal_aggregation: - explanation: - text: "使用者的個別操作(例如:在短時間內更新同一工作套件兩次)若時間差距小於指定時長,系統會將這些操作合併為單一動作,並於應用程式中以單一動作顯示。\n此機制同時會延遲通知發送相同的時間,以減少電子郵件數量,並且會影響 %{webhook_link} 的延遲時間。" - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -1356,9 +1355,6 @@ zh-TW: dependencies: "依賴套件" activerecord: attributes: - agile/sprint: - sharing: "分享" - finish_date: "結束日期" jira_import: projects: "Projects" "import/jira": @@ -3450,12 +3446,11 @@ 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: "資料夾" label_follows: "關注" - label_force_user_language_to_default: "設置使用者使用預設語言" label_form_configuration: "表單設置" label_formula: "公式" label_gantt_chart: "甘特圖" @@ -3465,8 +3460,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}" @@ -3478,7 +3473,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: "在" @@ -3529,7 +3524,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: "清單" @@ -3834,12 +3829,6 @@ zh-TW: label_user_named: "用戶名 %{name}" label_user_activity: "%{value} 的活動" label_user_anonymous: "匿名者" - label_user_mail_option_all: "對於我所有的專案的任何事件" - label_user_mail_option_none: "沒有事件" - label_user_mail_option_only_assigned: "只針對我被指派的事情" - label_user_mail_option_only_my_events: "只針對我關注的或者與我有關的事情" - label_user_mail_option_only_owner: "針對我是擁有者的事情" - label_user_mail_option_selected: "只有在已選取的專案的任何事件" label_user_menu: "使用者選單" label_user_new: "新增使用者" label_user_plural: "使用者" @@ -3879,7 +3868,6 @@ zh-TW: label_wiki_show_index_page_link: "顯示子功能表 '表格內容'" label_wiki_show_menu_item: "在專案導覽中以子功能表呈現" label_wiki_show_new_page_link: "顯示子功能表 '新增子頁面'" - label_wiki_show_submenu_item: "呈現於子功能表的 " label_wiki_start: "開始頁面" label_work: "工時" label_work_package: "工作套件" @@ -4947,7 +4935,7 @@ zh-TW: text_default_administrator_account_changed: "預設管理者帳號已更改" text_default_encoding: "預設值: UTF-8" text_destroy: "删除" - text_destroy_with_associated: "欲刪除的工作套件還關聯有其他物件,這些物件類型如下:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "您想要做什麼?" text_diff_truncated: "... 此差異被截斷, 因為它超出了可以顯示的最大大小。" text_email_delivery_not_configured: "電子郵件傳送未配置,通知已禁用。\n配置您的 SMTP 服務器將其啓用。" diff --git a/config/locales/en.yml b/config/locales/en.yml index 7f5a669513c..d00f4a5bcae 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -116,9 +116,8 @@ en: trial: "Trial" jemalloc_allocator: Jemalloc memory allocator journal_aggregation: - explanation: - text: "Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect %{webhook_link} delay." - link: "webhook" + caption: > + Individual actions of a user (e.g. updating a work package twice) are aggregated into a single action if their age difference is less than the specified timespan. They will be displayed as a single action within the application. This will also delay notifications by the same amount of time reducing the number of emails being sent and will also affect the [webhook](webhook_link) delay. import: title: "Import" jira: @@ -292,7 +291,7 @@ en: confirm_button: "Start import" description: > This importer is an alpha feature. It is not yet able to import all data from Jira and might leave incomplete data on this OpenProject instance. - + Do not use a production environment and create a backup of your OpenProject data before starting. confirm: "I understand and made the necessary preparations" revert_dialog: @@ -1477,9 +1476,6 @@ en: activerecord: attributes: - agile/sprint: - sharing: "Sharing" - finish_date: "End date" jira_import: projects: "Projects" "import/jira": @@ -3674,7 +3670,6 @@ en: label_float: "Float" label_folder: "Folder" label_follows: "follows" - label_force_user_language_to_default: "Set language of users having a non allowed language to default" label_form_configuration: "Form configuration" label_formula: "Formula" label_gantt_chart: "Gantt chart" @@ -4053,12 +4048,6 @@ en: label_user_named: "User %{name}" label_user_activity: "%{value}'s activity" label_user_anonymous: "Anonymous" - label_user_mail_option_all: "For any event on all my projects" - label_user_mail_option_none: "No events" - label_user_mail_option_only_assigned: "Only for things I am assigned to" - label_user_mail_option_only_my_events: "Only for things I watch or I'm involved in" - label_user_mail_option_only_owner: "Only for things I am the owner of" - label_user_mail_option_selected: "For any event on the selected projects only" label_user_menu: "User menu" label_user_new: "New user" label_user_plural: "Users" @@ -4098,7 +4087,6 @@ en: label_wiki_show_index_page_link: "Show submenu item 'Table of Contents'" label_wiki_show_menu_item: "Show as menu item in project navigation" label_wiki_show_new_page_link: "Show submenu item 'Create new child page'" - label_wiki_show_submenu_item: "Show as submenu item of " label_wiki_start: "Start page" label_work: "Work" label_work_package: "Work package" @@ -5292,7 +5280,7 @@ en: text_default_administrator_account_changed: "Default administrator account changed" text_default_encoding: "Default: UTF-8" text_destroy: "Delete" - text_destroy_with_associated: "There are additional objects assossociated with the work package(s) that are to be deleted. Those objects are of the following types:" + text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "What do you want to do?" text_diff_truncated: "... This diff was truncated because it exceeds the maximum size that can be displayed." text_email_delivery_not_configured: "Email delivery is not configured, and notifications are disabled.\nConfigure your SMTP server to enable them." diff --git a/config/puma.rb b/config/puma.rb index 91b7e7b442c..45237d89362 100644 --- a/config/puma.rb +++ b/config/puma.rb @@ -129,8 +129,7 @@ if Rails.env.development? end if siginfo_supported - # Using on_booted is needed to override puma adding handler to show thread statuses - on_booted do + after_booted do Signal.trap("INFO") do system "open", Rails.application.root_url end diff --git a/db/migrate/20260212145213_migrate_backlogs_permissions.rb b/db/migrate/20260212145213_migrate_backlogs_permissions.rb new file mode 100644 index 00000000000..f6e0fa4675a --- /dev/null +++ b/db/migrate/20260212145213_migrate_backlogs_permissions.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require Rails.root.join("db/migrate/migration_utils/permission_renamer") +require Rails.root.join("db/migrate/migration_utils/permission_adder") + +class MigrateBacklogsPermissions < ActiveRecord::Migration[8.1] + def up + ::Migration::MigrationUtils::PermissionRenamer.rename(:view_master_backlog, :view_sprints) + ::Migration::MigrationUtils::PermissionRenamer.rename(:view_taskboards, :view_sprints) + + ::Migration::MigrationUtils::PermissionAdder.add(:manage_versions, :create_sprints) + ::Migration::MigrationUtils::PermissionRenamer.rename(:update_sprints, :create_sprints) + + ::Migration::MigrationUtils::PermissionAdder.add(:assign_versions, :manage_sprint_items) + end + + def down + # Note: Ideally the `:view_taskboards`, `:view_master_backlog`, `:manage_versions`, + # `:update_sprints` permissions should be restored too, but unfortunately we cannot know + # which one lead to the user gaining `:view_sprints` or `:create_sprints` permissions. + # There are 2 possible solutions for this issue: + # 1. Grant both the `:view_taskboards`, `:view_master_backlog` where `:view_sprints` was granted. + # Respectively, grant `:manage_versions`, `:update_sprints` permissions where `:create_sprints` + # was granted. Unfortunately this leads to users gaining permissions they didn't possibly had + # before the migration. + # 2. Grant none of the undecisible permissions, which leads to users losing permissions they had + # before the migration. + # + # The conservative approach here is to pick #2, because it avoids accidentally leaking permissions + # to users. + + # Remove new permissions that were added during the up migration + RolePermission.delete_by(permission: %w(view_sprints create_sprints manage_sprint_items)) + end +end diff --git a/db/migrate/20260304160505_fix_sprint_role_dependencies.rb b/db/migrate/20260304160505_fix_sprint_role_dependencies.rb new file mode 100644 index 00000000000..a68994188c9 --- /dev/null +++ b/db/migrate/20260304160505_fix_sprint_role_dependencies.rb @@ -0,0 +1,39 @@ +# 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 Rails.root.join("db/migrate/migration_utils/permission_adder") + +class FixSprintRoleDependencies < ActiveRecord::Migration[8.1] + def change + ::Migration::MigrationUtils::PermissionAdder.add(:manage_sprint_items, :view_sprints) + ::Migration::MigrationUtils::PermissionAdder.add(:create_sprints, :view_sprints) + ::Migration::MigrationUtils::PermissionAdder.add(:view_sprints, :view_work_packages) + end +end diff --git a/db/migrate/migration_utils/permission_renamer.rb b/db/migrate/migration_utils/permission_renamer.rb index 39642d2f1ee..4547eb4f385 100644 --- a/db/migrate/migration_utils/permission_renamer.rb +++ b/db/migrate/migration_utils/permission_renamer.rb @@ -28,26 +28,17 @@ # See COPYRIGHT and LICENSE files for more details. #++ +require Rails.root.join("db/migrate/migration_utils/permission_adder") + module Migration module MigrationUtils class PermissionRenamer class << self - def rename(from, to) - ActiveRecord::Base.connection.execute <<-SQL.squish - UPDATE #{role_permissions_table} - SET permission = #{quote_value(to)} - WHERE permission = #{quote_value(from)} - SQL - end - - private - - def role_permissions_table - @role_permissions_table ||= ActiveRecord::Base.connection.quote_table_name("role_permissions") - end - - def quote_value(value) - ActiveRecord::Base.connection.quote(value) + # The `force: true` argument is the default because usually when a rename + # happens, the `from`` permission is no longer defined. + def rename(from, to, force: true) + ::Migration::MigrationUtils::PermissionAdder.add(from, to, force:) + RolePermission.delete_by(permission: from) end end end diff --git a/docker/dev/backend/Dockerfile b/docker/dev/backend/Dockerfile index bc86675ea45..e2a04a60889 100644 --- a/docker/dev/backend/Dockerfile +++ b/docker/dev/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:3.4.7-trixie AS develop +FROM ruby:4.0.1-trixie AS develop LABEL org.opencontainers.image.authors="operations@openproject.com" ARG DEV_UID=1000 diff --git a/docker/dev/hocuspocus/docker-compose.yml b/docker/dev/hocuspocus/docker-compose.yml index 6ff6d5fbda9..bb1eade4c7e 100644 --- a/docker/dev/hocuspocus/docker-compose.yml +++ b/docker/dev/hocuspocus/docker-compose.yml @@ -2,7 +2,7 @@ services: hocuspocus: # In case of MacOS you need to specify the platform in your `docker/dev/hocuspocus/docker-compose.override.yml`: # platform: linux/amd64 - image: openproject/hocuspocus:latest + image: openproject/hocuspocus:dev-latest labels: - "traefik.enable=true" - "traefik.http.routers.hocuspocus.rule=Host(`hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD:-local}`)" @@ -14,7 +14,6 @@ services: networks: - gateway environment: - - ALLOWED_DOMAINS=openproject.${OPENPROJECT_DOCKER_DEV_TLD:-local},localhost - NODE_TLS_REJECT_UNAUTHORIZED=0 - SECRET=secret12345 networks: diff --git a/docker/dev/tls/docker-compose.yml b/docker/dev/tls/docker-compose.yml index 12411a09859..a75a3773a94 100644 --- a/docker/dev/tls/docker-compose.yml +++ b/docker/dev/tls/docker-compose.yml @@ -17,6 +17,7 @@ services: - openproject.${OPENPROJECT_DOCKER_DEV_TLD:-local} - openproject-assets.${OPENPROJECT_DOCKER_DEV_TLD:-local} - nextcloud.${OPENPROJECT_DOCKER_DEV_TLD:-local} + - xwiki.${OPENPROJECT_DOCKER_DEV_TLD:-local} - gitlab.${OPENPROJECT_DOCKER_DEV_TLD:-local} - keycloak.${OPENPROJECT_DOCKER_DEV_TLD:-local} - hocuspocus.${OPENPROJECT_DOCKER_DEV_TLD:-local} diff --git a/docker/prod/Dockerfile b/docker/prod/Dockerfile index bc73fdc9e42..69d16e05961 100755 --- a/docker/prod/Dockerfile +++ b/docker/prod/Dockerfile @@ -1,4 +1,5 @@ -ARG RUBY_VERSION="3.4.7" +ARG RUBY_VERSION="4.0.1" +ARG DEBIAN_BASE="trixie" # Add SBOM scan context for intermediate steps ARG BUILDKIT_SBOM_SCAN_CONTEXT=true ARG BUILDKIT_SBOM_SCAN_STAGE=true diff --git a/docs/api/apiv3/components/examples/meeting_collection_simple_response.yml b/docs/api/apiv3/components/examples/meeting_collection_simple_response.yml new file mode 100644 index 00000000000..ff956f7e64c --- /dev/null +++ b/docs/api/apiv3/components/examples/meeting_collection_simple_response.yml @@ -0,0 +1,30 @@ +description: |- + A simple response of a meeting collection. +value: + _type: 'Collection' + total: 2 + count: 2 + pageSize: 20 + offset: 1 + _embedded: + _abbreviated: Embedded resources shortened for brevity + elements: + - _type: 'Meeting' + id: 6 + title: 'Weekly' + startTime: '2026-02-17T20:54:35.523Z' + endTime: '2026-02-17T21:54:35.523Z' + - _type: 'Meeting' + id: 5 + title: 'Weekly' + startTime: '2026-02-10T20:54:35.523Z' + endTime: '2026-02-10T21:54:35.523Z' + _links: + self: + href: '/api/v3/meetings?filters=%5B%5D&offset=1&pageSize=20' + jumpTo: + href: '/api/v3/meetings?filters=%5B%5D&offset=%7Boffset%7D&pageSize=20' + templated: true + changeSize: + href: '/api/v3/meetings?filters=%5B%5D&offset=1&pageSize=%7Bsize%7D' + templated: true diff --git a/docs/api/apiv3/components/examples/meeting_simple_response.yml b/docs/api/apiv3/components/examples/meeting_simple_response.yml new file mode 100644 index 00000000000..337071c803e --- /dev/null +++ b/docs/api/apiv3/components/examples/meeting_simple_response.yml @@ -0,0 +1,42 @@ +description: |- + A simple meeting response. +value: + _type: 'Meeting' + id: 1 + title: 'Meeting No 42' + location: 'Death Star - Level 1337 L13/42' + lockVersion: 13 + startTime: '2026-02-16T10:00:00.000Z' + endTime: '2026-02-16T11:00:00.000Z' + duration: 1.0 + createdAt: '2026-02-13T14:47:12.315Z' + updatedAt: '2026-02-13T14:47:12.315Z' + _links: + attachments: + href: '/api/v3/meetings/1/attachments' + addAttachment: + href: '/api/v3/meetings/1/attachments' + method: 'post' + self: + href: '/api/v3/meetings/1' + title: 'Meeting No 42' + author: + href: '/api/v3/users/4' + title: 'Darth Vader' + project: + href: '/api/v3/projects/10' + title: 'Death Star' + _embedded: + _abbreviated: Embedded resources shortened for brevity + attachments: + _type: Collection + total: 2 + count: 2 + author: + _type: User + name: Darth Vader + id: 4 + project: + _type: Project + name: Death Star + id: 10 diff --git a/docs/api/apiv3/components/examples/membership-simple-response.yml b/docs/api/apiv3/components/examples/membership_simple_response.yml similarity index 100% rename from docs/api/apiv3/components/examples/membership-simple-response.yml rename to docs/api/apiv3/components/examples/membership_simple_response.yml diff --git a/docs/api/apiv3/components/examples/membership-update-additional-roles.yml b/docs/api/apiv3/components/examples/membership_update_additional_roles.yml similarity index 100% rename from docs/api/apiv3/components/examples/membership-update-additional-roles.yml rename to docs/api/apiv3/components/examples/membership_update_additional_roles.yml diff --git a/docs/api/apiv3/components/schemas/meeting_collection_model.yml b/docs/api/apiv3/components/schemas/meeting_collection_model.yml new file mode 100644 index 00000000000..ab2ec48781a --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_collection_model.yml @@ -0,0 +1,30 @@ +# Schema: MeetingCollectionModel +--- +allOf: + - $ref: './paginated_collection_model.yml' + - type: object + required: + - _embedded + - _links + properties: + _embedded: + type: object + required: + - elements + properties: + elements: + type: array + items: + $ref: './meeting_model.yml' + _links: + type: object + required: + - self + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + The link to this meeting collection resource + + **Resource**: MeetingCollection diff --git a/docs/api/apiv3/components/schemas/meeting_model.yml b/docs/api/apiv3/components/schemas/meeting_model.yml index 37e0f4c6827..0b518a1d20e 100644 --- a/docs/api/apiv3/components/schemas/meeting_model.yml +++ b/docs/api/apiv3/components/schemas/meeting_model.yml @@ -1,7 +1,8 @@ -# Schema: Meeting_PageModel +# Schema: MeetingModel --- type: object required: + - _type - id - lockVersion - title @@ -11,11 +12,14 @@ required: - createdAt - updatedAt properties: + _type: + type: string + enum: + - Meeting id: type: integer description: Identifier of this meeting - readOnly: true - exclusiveMinimum: 0 + minimum: 1 title: type: string description: The meeting's title @@ -25,7 +29,6 @@ properties: lockVersion: type: integer description: The version of the item as used for optimistic locking - readOnly: true startTime: type: string format: date-time @@ -36,39 +39,47 @@ properties: description: The scheduled meeting start time. duration: type: number - description: The meeting duration in minutes. + description: The meeting duration in hours. createdAt: type: string format: date-time description: Time of creation. Can be writable by admins with the `apiv3_write_readonly_attributes` setting enabled. - readOnly: true updatedAt: type: string format: date-time description: Time of the most recent change to the meeting. - readOnly: true + _embedded: + type: object + required: + - attachments + - author + properties: + attachments: + $ref: './attachments_model.yml' + author: + $ref: './user_model.yml' + project: + $ref: './project_model.yml' _links: type: object properties: self: allOf: - - "$ref": "./link.yml" + - $ref: './link.yml' - description: |- This meeting **Resource**: Meeting - readOnly: true author: allOf: - - "$ref": "./link.yml" + - $ref: './link.yml' - description: |- The user having created the meeting **Resource**: User - readOnly: true project: allOf: - - "$ref": "./link.yml" + - $ref: './link.yml' - description: |- The project the meeting is in @@ -82,42 +93,10 @@ properties: **Resource**: AttachmentCollection addAttachment: allOf: - - "$ref": "./link.yml" + - $ref: './link.yml' - description: |- Attach a file to the meeting # Conditions **Permission**: edit meeting - readOnly: true -example: - _type: Meeting - id: 72 - lockVersion: 5 - title: A meeting - startTime: '2014-05-21T08:00:00.000Z' - endTime: '2014-05-21T10:00:00.000Z' - duration: 120 - createdAt: '2014-05-21T08:51:20.396Z' - updatedAt: '2014-05-21T09:14:02.776Z' - _embedded: - project: - _type: Project... - id: 12 - author: - _type: User... - id: 2 - _links: - addAttachment: - href: "/api/v3/meetings/72/attachments" - method: post - attachments: - href: "/api/v3/meetings/72/attachments" - project: - href: "/api/v3/projects/12" - title: some project - author: - href: "/api/v3/users/2" - title: Peggie Feeney - self: - href: "/api/v3/meetings/72" diff --git a/docs/api/apiv3/openapi-spec.yml b/docs/api/apiv3/openapi-spec.yml index 0ec085c9124..91d141331ab 100644 --- a/docs/api/apiv3/openapi-spec.yml +++ b/docs/api/apiv3/openapi-spec.yml @@ -266,6 +266,8 @@ paths: "$ref": "./paths/help_texts.yml" "/api/v3/help_texts/{id}": "$ref": "./paths/help_text.yml" + "/api/v3/meetings": + "$ref": "./paths/meetings.yml" "/api/v3/meetings/{id}": "$ref": "./paths/meeting.yml" "/api/v3/meetings/{id}/attachments": @@ -589,6 +591,10 @@ components: $ref: "./components/examples/hierarchy_item_collection_response.yml" HierarchyItemResponse: $ref: "./components/examples/hierarchy_item_response.yml" + MeetingCollectionSimpleResponse: + $ref: "./components/examples/meeting_collection_simple_response.yml" + MeetingSimpleResponse: + $ref: "./components/examples/meeting_simple_response.yml" MembershipCreateRequestCustomMessage: $ref: "./components/examples/membership-create-request-custom-message.yml" MembershipCreateRequestGlobalRole: @@ -602,9 +608,9 @@ components: MembershipSimpleCollectionResponse: $ref: "./components/examples/membership-simple-collection-response.yml" MembershipSimpleResponse: - $ref: "./components/examples/membership-simple-response.yml" + $ref: "./components/examples/membership_simple_response.yml" MembershipUpdateAdditionalRoles: - $ref: "./components/examples/membership-update-additional-roles.yml" + $ref: "./components/examples/membership_update_additional_roles.yml" MentionedNotification: $ref: "./components/examples/mentioned_notification.yml" NotificationCollection: @@ -827,6 +833,8 @@ components: "$ref": "./components/schemas/list_of_news_model.yml" List_workspaces_by_versionModel: "$ref": "./components/schemas/list_workspaces_by_version_model.yml" + MeetingCollectionModel: + "$ref": "./components/schemas/meeting_collection_model.yml" MeetingModel: "$ref": "./components/schemas/meeting_model.yml" MarkdownModel: diff --git a/docs/api/apiv3/paths/meeting.yml b/docs/api/apiv3/paths/meeting.yml index fc052659abc..e0ec3ef46f9 100644 --- a/docs/api/apiv3/paths/meeting.yml +++ b/docs/api/apiv3/paths/meeting.yml @@ -1,9 +1,14 @@ # /api/v3/meetings/{id} --- get: + summary: Get a meeting + operationId: get_meeting + tags: + - Meetings + description: Retrieve an individual meeting as identified by the id parameter parameters: - description: Meeting identifier - example: '1' + example: 1 in: path name: id required: true @@ -11,44 +16,14 @@ get: type: integer responses: '200': + description: OK content: application/hal+json: examples: response: - value: - _embedded: - project: - _type: Project... - id: 12 - author: - _type: User... - id: 2 - _links: - addAttachment: - href: "/api/v3/meetings/72/attachments" - method: post - attachments: - href: "/api/v3/meetings/72/attachments" - project: - href: "/api/v3/projects/12" - title: some project - author: - href: "/api/v3/users/2" - title: Peggie Feeney - self: - href: "/api/v3/meetings/72" - _type: Meeting - id: 72 - lockVersion: 5 - title: A meeting - startTime: '2014-05-21T08:00:00.000Z' - duration: 120 - createdAt: '2014-05-21T08:51:20.396Z' - updatedAt: '2014-05-21T09:14:02.776Z' + $ref: "../components/examples/meeting_simple_response.yml" schema: - "$ref": "../components/schemas/meeting_model.yml" - description: OK - headers: { } + $ref: "../components/schemas/meeting_model.yml" '404': content: application/hal+json: @@ -64,9 +39,3 @@ get: Returned if the meeting does not exist or the client does not have sufficient permissions to see it. **Required permission:** view meetings in the page's project - headers: { } - tags: - - Meetings - description: Retrieve an individual meeting as identified by the id parameter - operationId: View_Meeting - summary: View Meeting Page diff --git a/docs/api/apiv3/paths/meeting_attachments.yml b/docs/api/apiv3/paths/meeting_attachments.yml index 200d9f58528..cdceb3c141a 100644 --- a/docs/api/apiv3/paths/meeting_attachments.yml +++ b/docs/api/apiv3/paths/meeting_attachments.yml @@ -3,7 +3,7 @@ get: parameters: - description: ID of the meeting whose attachments will be listed - example: '1' + example: 1 in: path name: id required: true @@ -84,7 +84,7 @@ get: post: parameters: - description: ID of the meeting to receive the attachment - example: '1' + example: 1 in: path name: id required: true diff --git a/docs/api/apiv3/paths/meetings.yml b/docs/api/apiv3/paths/meetings.yml new file mode 100644 index 00000000000..8550f7ebabe --- /dev/null +++ b/docs/api/apiv3/paths/meetings.yml @@ -0,0 +1,19 @@ +# /api/v3/meetings +--- +get: + summary: List all visible meetings + operationId: list_meetings + tags: + - Meetings + description: |- + Retrieve a paginated collection of meetings visible to the authenticated user. + responses: + '200': + description: OK + content: + application/hal+json: + examples: + response: + $ref: "../components/examples/meeting_collection_simple_response.yml" + schema: + $ref: "../components/schemas/meeting_collection_model.yml" diff --git a/docs/api/apiv3/paths/membership.yml b/docs/api/apiv3/paths/membership.yml index eff60e35dd0..ffc8c2a9dc1 100644 --- a/docs/api/apiv3/paths/membership.yml +++ b/docs/api/apiv3/paths/membership.yml @@ -75,7 +75,7 @@ get: $ref: '../components/schemas/membership_read_model.yml' examples: 'simple membership': - $ref: '../components/examples/membership-simple-response.yml' + $ref: '../components/examples/membership_simple_response.yml' '404': content: application/hal+json: @@ -121,7 +121,7 @@ patch: $ref: '../components/schemas/membership_write_model.yml' examples: 'add roles': - $ref: '../components/examples/membership-update-additional-roles.yml' + $ref: '../components/examples/membership_update_additional_roles.yml' responses: '200': description: OK @@ -131,7 +131,7 @@ patch: $ref: '../components/schemas/membership_read_model.yml' examples: 'simple membership': - $ref: '../components/examples/membership-simple-response.yml' + $ref: '../components/examples/membership_simple_response.yml' '400': $ref: '../components/responses/invalid_request_body.yml' '403': diff --git a/docs/api/apiv3/paths/membership_form.yml b/docs/api/apiv3/paths/membership_form.yml index eb9914e3652..0e242091969 100644 --- a/docs/api/apiv3/paths/membership_form.yml +++ b/docs/api/apiv3/paths/membership_form.yml @@ -35,7 +35,7 @@ post: $ref: '../components/schemas/membership_read_model.yml' examples: 'simple membership': - $ref: '../components/examples/membership-simple-response.yml' + $ref: '../components/examples/membership_simple_response.yml' '400': $ref: '../components/responses/invalid_request_body.yml' '403': diff --git a/docs/api/apiv3/paths/memberships.yml b/docs/api/apiv3/paths/memberships.yml index 4fa8a207c20..e1aa67e29d2 100644 --- a/docs/api/apiv3/paths/memberships.yml +++ b/docs/api/apiv3/paths/memberships.yml @@ -124,7 +124,7 @@ post: $ref: '../components/schemas/membership_read_model.yml' examples: 'simple membership': - $ref: '../components/examples/membership-simple-response.yml' + $ref: '../components/examples/membership_simple_response.yml' '400': $ref: '../components/responses/invalid_request_body.yml' '403': diff --git a/docs/development/development-environment/linux/README.md b/docs/development/development-environment/linux/README.md index 6c222b0c829..223026715eb 100644 --- a/docs/development/development-environment/linux/README.md +++ b/docs/development/development-environment/linux/README.md @@ -40,10 +40,10 @@ sudo apt-get install git curl build-essential zlib1g-dev libyaml-dev libssl-dev Use [rbenv](https://github.com/rbenv/rbenv) and [ruby-build](https://github.com/rbenv/ruby-build#readme) to install Ruby. You can check available ruby versions with `rbenv install --list`. -At the time of this writing, the latest stable version is `3.4.7`, which we also require. +At the time of this writing, the latest stable version is `4.0.1`, which we also require. We suggest you install the version we require in [.ruby-version](https://github.com/opf/openproject/blob/dev/.ruby-version). -Read the first line e.g. `3.4.7` and install that version. +Read the first line e.g. `4.0.1` and install that version. #### Install rbenv and ruby-build @@ -80,18 +80,18 @@ With both installed, we can now install ruby. You can check available ruby versions with `rbenv install --list`. We suggest you install the version we require in [.ruby-version](https://github.com/opf/openproject/blob/dev/.ruby-version). -Read the first line e.g. `3.4.7` and install that version. +Read the first line e.g. `4.0.1` and install that version. ```shell # Install the required version as read from the .ruby-version file -rbenv install 3.4.7 +rbenv install 4.0.1 ``` This might take a while depending on whether ruby is built from source. After it is complete, you need to tell rbenv to globally activate this version ```shell -rbenv global 3.4.7 +rbenv global 4.0.1 rbenv rehash ``` @@ -181,10 +181,10 @@ You should now have an active ruby and node installation. Verify that it works w ```shell ruby --version -ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [arm64-darwin25] +ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25] bundler --version -Bundler version 2.7.2 +4.0.3 node --version v22.21.0 diff --git a/docs/development/development-environment/macos/README.md b/docs/development/development-environment/macos/README.md index e1f2e45519c..ba6694a3e7e 100644 --- a/docs/development/development-environment/macos/README.md +++ b/docs/development/development-environment/macos/README.md @@ -28,7 +28,7 @@ their homepage. Use [rbenv](https://github.com/rbenv/rbenv) and [ruby-build](https://github.com/rbenv/ruby-build#readme) to install Ruby. We always require the latest ruby versions, and you can check which version is required by [checking the Gemfile](https://github.com/opf/openproject/blob/dev/Gemfile#L31) for the `ruby "~> X.Y"` statement. At -the time of writing, this version is "3.4.7" +the time of writing, this version is "4.0.1" #### Install rbenv and ruby-build @@ -48,18 +48,18 @@ With both installed, we can now install the actual ruby version. You can check available ruby versions with `rbenv install --list`. We suggest you install the version we require in [.ruby-version](https://github.com/opf/openproject/blob/dev/.ruby-version). -Read the first line e.g. `3.4.7` and install that version. +Read the first line e.g. `4.0.1` and install that version. ```shell # Install the required version as read from the .ruby-version file -rbenv install 3.4.7 +rbenv install 4.0.1 ``` This might take a while depending on whether ruby is built from source. After it is complete, you need to tell rbenv to globally activate this version ```shell -rbenv global 3.4.7 +rbenv global 4.0.1 ``` You also need to install [bundler](https://github.com/bundler/bundler/), the ruby gem bundler. @@ -134,10 +134,10 @@ You should now have an active ruby and node installation. Verify that it works w ```shell $ ruby --version -ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [arm64-darwin25]4] +ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25] $ bundler --version -Bundler version 2.7.2 +4.0.3 node --version v22.21.0 diff --git a/docs/development/testing/handling-flaky-tests/README.md b/docs/development/testing/handling-flaky-tests/README.md index fe23041249b..e4332bb5224 100644 --- a/docs/development/testing/handling-flaky-tests/README.md +++ b/docs/development/testing/handling-flaky-tests/README.md @@ -21,6 +21,10 @@ Developers notice a failing spec in CI runs related to the PR they are working o The failing spec is suspicious as it seems unrelated to the changes introduced by the commits. +Out-of-hours correlation is a lead, not proof of a datetime bug. Evening or weekend failures can still be caused by +ordinary flakiness, branch-specific regressions, or infrastructure issues. Start by separating build/setup failures from +actual `Unit tests` or `Feature tests`, then look for recurring spec names before concluding that time-sensitive logic is involved. + To get the failing spec names, use `script/github_pr_errors` and give it the URL of the failing run as argument, for example: ```bash @@ -29,6 +33,16 @@ script/github_pr_errors https://github.com/opf/openproject/actions/runs/18215876 There are options to display images or display advice to reproduce the failures. Use `--help` to know more. +To aggregate recent `Test suite` failures and highlight specs that skew outside 09:00-18:00 Europe/Berlin Monday to Friday, +use: + +```bash +export GITHUB_TOKEN=... +script/report_out_of_hours_ci_failures --days 30 +``` + +The report focuses on `dev` and `release/*` runs by default and excludes failures that never reached the unit or feature test steps. + ## Confirming the spec is flaky To confirm the flakiness of the spec, either: diff --git a/docs/installation-and-operations/installation/manual/README.md b/docs/installation-and-operations/installation/manual/README.md index 0e16df33786..2092086ffbf 100644 --- a/docs/installation-and-operations/installation/manual/README.md +++ b/docs/installation-and-operations/installation/manual/README.md @@ -106,19 +106,19 @@ Please be aware that the actual installation of a specific Ruby version takes so ``` We suggest you install the version we require in [.ruby-version](https://github.com/opf/openproject/blob/dev/.ruby-version). -Read the first line e.g. `3.4.7` and install that version. +Read the first line e.g. `4.0.1` and install that version. ```shell -[openproject@host] rbenv install 3.4.7 +[openproject@host] rbenv install 4.0.1 [openproject@host] rbenv rehash -[openproject@host] rbenv global 3.4.7 +[openproject@host] rbenv global 4.0.1 ``` To check our Ruby installation we run `ruby --version`. It should output something very similar to: ```text -ruby 3.4.7 (2025-10-08 revision 7a5688e2a2) +PRISM [arm64-darwin25] +ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25] ``` ## Installation of Node diff --git a/docs/system-admin-guide/files/external-file-storages/health-status/README.md b/docs/system-admin-guide/files/external-file-storages/health-status/README.md index 7905ff92383..6ebec39c103 100644 --- a/docs/system-admin-guide/files/external-file-storages/health-status/README.md +++ b/docs/system-admin-guide/files/external-file-storages/health-status/README.md @@ -70,12 +70,14 @@ and suggested solutions. |------------------------------------|----------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | ERR_NC_HOST_NOT_FOUND | No Nextcloud server was found at the configured host URL. | There might be a typo or the URL has changed. | Check the configuration and enter a valid URL. | | ERR_NC_DEPENDENCY_MISSING | A required dependency is missing on the file storage. | Either the Integration OpenProject app or the Team Folders app is not enabled in Nextcloud. | Add the missing dependency to the Nextcloud server. | -| ERR_NC_DEPENDENCY_VERSION_MISMATCH | A required dependency has an outdated version. | Either the Integration OpenProject app or the Team Folders app is outdated or was not updated to the officially minimal supported version. | Update your apps to the latest version. It might be necessary to update your Nextcloud server to the latest version in order to be able to install the latest app versions. | | ERR_NC_TEAM_FOLDER_NOT_FOUND | The team folder could not be found. | The team folder used by the Integration OpenProject app was not found. This folder is usually named `OpenProject` and is owned by the group `OpenProject`. | The team folder is used only by the automatically managed project folders mode. Try to disable this option in the Nextcloud administration for the Integration OpenProject app and reenable it afterwards. | | ERR_NC_OAUTH_REQUEST_UNAUTHORIZED | The current user's token is invalid. | The token of the current user could not be used for accessing the remote file storage. | If the file storage is configure to do the *Two-Way OAuth2 authorization code flow* remove the user token from **Account settings → Access tokens** of this file storage and redo the login. If the storage is configured to use SSO login, please recheck the [SSO configuration guide](../../../integrations/nextcloud/oidc-sso/) for the settings of OpenProject and Nextcloud of your specific setup. | | ERR_NC_USERLESS_ACCESS_DENIED | The userless request was unauthorized | The configured app password is invalid. | Generate a new app password in the Nextcloud administration section of the Integration OpenProject app and copy it over to the OpenProject file storage configuration form. | +| ERR_NC_PROJECT_FOLDER_MISSING | A project folder that has been created by the AMPF synchronization job in the past could not be found anymore. | The folder might have been deleted by a Nextcloud admin or it was deleted and later a folder with the same name was recreated, but is not recognized as the original folder anymore. | This error can right now only be fixed from a Rails console. Please contact OpenProject support. | +| WRN_NC_DEPENDENCY_VERSION_MISMATCH | A required dependency has an outdated version. | Either the Integration OpenProject app or the Team Folders app is outdated or was not updated to the officially minimal supported version. | Update your apps to the latest version. It might be necessary to update your Nextcloud server to the latest version in order to be able to install the latest app versions. | | WRN_NC_OAUTH_TOKEN_MISSING | The current user has no authentication token. | The current user probably never did a successful login from OpenProject to the file storage, or the token was deleted from the account details. | Visit any work package of a project, where the current file storage is used. Click on the **Login** button in the **Files** tab. | -| WRN_NC_UNEXPECTED_CONTENT | The connection request was successful, but unexpected content was found in the team folder. | The team folder `OpenProject` might contain data, that was put there by a user, or there are remnants from projects that no longer have a valid connection in OpenProject. | Go to Nextcloud and migrate or delete the data in the OpenProject team folder, that was not created by OpenProject. Further information about the unexpected data is found in the server logs. | +| WRN_NC_UNLINKED_PROJECT_FOLDERS | Some automatically managed project folders were not yet created. | If the storage was configured for the project very recently, this can be normal and the background worker might just not have created the folder yet. If This warning remains after a longer period of time, the background synchronization may have encountered an error. | Check the AMPF sync error output on the storage's configuration page. If it shows as healthy, this warning should disappear when repeating the health checks after a few minutes. If an error is shown on the storage's configuration page, this is a likely culprit for this warning. | +| WRN_NC_UNEXPECTED_FILES | The connection request was successful, but unexpected files were found in the team folder. | The team folder `OpenProject` might contain data, that was put there by a user, or there are remnants from projects that no longer have a valid connection in OpenProject. | Go to Nextcloud and migrate or delete the data in the OpenProject team folder, that was not created by OpenProject. Further information about the unexpected data is found in the server logs. | The officially minimal supported app versions are listed in the [system admin guide](../../../../system-admin-guide/integrations/nextcloud/#required-system-versions). diff --git a/docs/user-guide/wiki/wiki-menu/README.md b/docs/user-guide/wiki/wiki-menu/README.md index 10373378cd1..ba6be4d3d58 100644 --- a/docs/user-guide/wiki/wiki-menu/README.md +++ b/docs/user-guide/wiki/wiki-menu/README.md @@ -25,7 +25,6 @@ To add a wiki page as a menu item to the project menu, select the **More** funct - **Do not show this wikipage in the project navigation** will NOT display a separate menu item in the project navigation. The wiki page is just displayed within the wiki module itself. - **Show as menu item in project navigation** will add a separate menu item to the project navigation. -- **Show as submenu item of ...** will display the wiki page a sub-menu item in the project navigation. 3. **Save** your changes to the wiki page menu. @@ -35,8 +34,4 @@ To add a wiki page as a menu item to the project menu, select the **More** funct ![wiki-show-menu-item](wiki-show-menu-item.png) -**Visibility** show as submenu item of project-documentation: - -![wiki-show-submenu-item](wiki-show-submenu-item.png) - The default option is **Do not show this wiki page in project navigation**. Check this option if you want to undo earlier changes and hide the wiki page from the project menu. diff --git a/docs/user-guide/wiki/wiki-menu/wiki-show-submenu-item.png b/docs/user-guide/wiki/wiki-menu/wiki-show-submenu-item.png deleted file mode 100644 index 076bd97d6e6..00000000000 Binary files a/docs/user-guide/wiki/wiki-menu/wiki-show-submenu-item.png and /dev/null differ diff --git a/extensions/op-blocknote-hocuspocus/package-lock.json b/extensions/op-blocknote-hocuspocus/package-lock.json index a5e1eb211ae..30261379131 100644 --- a/extensions/op-blocknote-hocuspocus/package-lock.json +++ b/extensions/op-blocknote-hocuspocus/package-lock.json @@ -263,7 +263,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -286,7 +285,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=18" } @@ -947,7 +945,6 @@ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz", "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", "license": "MIT", - "peer": true, "dependencies": { "@floating-ui/core": "^1.7.3", "@floating-ui/utils": "^0.2.10" @@ -1297,9 +1294,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.3.tgz", - "integrity": "sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], @@ -1311,9 +1308,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.3.tgz", - "integrity": "sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], @@ -1325,9 +1322,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.3.tgz", - "integrity": "sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], @@ -1339,9 +1336,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.3.tgz", - "integrity": "sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], @@ -1353,9 +1350,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.3.tgz", - "integrity": "sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], @@ -1367,9 +1364,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.3.tgz", - "integrity": "sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], @@ -1381,9 +1378,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.3.tgz", - "integrity": "sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], @@ -1395,9 +1392,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.3.tgz", - "integrity": "sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], @@ -1409,9 +1406,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.3.tgz", - "integrity": "sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], @@ -1423,9 +1420,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.3.tgz", - "integrity": "sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], @@ -1437,9 +1434,23 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.3.tgz", - "integrity": "sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], @@ -1451,9 +1462,23 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.3.tgz", - "integrity": "sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], @@ -1465,9 +1490,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.3.tgz", - "integrity": "sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], @@ -1479,9 +1504,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.3.tgz", - "integrity": "sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], @@ -1493,9 +1518,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.3.tgz", - "integrity": "sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], @@ -1507,9 +1532,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.53.3.tgz", - "integrity": "sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], @@ -1521,9 +1546,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.53.3.tgz", - "integrity": "sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], @@ -1534,10 +1559,24 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.3.tgz", - "integrity": "sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], @@ -1549,9 +1588,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.3.tgz", - "integrity": "sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], @@ -1563,9 +1602,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.3.tgz", - "integrity": "sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], @@ -1577,9 +1616,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.3.tgz", - "integrity": "sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "cpu": [ "x64" ], @@ -1591,9 +1630,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.3.tgz", - "integrity": "sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], @@ -1609,7 +1648,6 @@ "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.13.0.tgz", "integrity": "sha512-oM9P+NCFri/mmQ8LoFGVfVyemm5Hi27330zuOBp0annwJdKH1kOLndw3zCtAVDehPLg9fKqoEx3Ht/wNZxolfw==", "license": "MIT", - "peer": true, "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" @@ -1682,7 +1720,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.13.0.tgz", "integrity": "sha512-iUelgiTMgPVMpY5ZqASUpk8mC8HuR9FWKaDzK27w9oWip9tuB54Z8mePTxNcQaSPb6ErzEaC8x8egrRt7OsdGQ==", "license": "MIT", - "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" @@ -1880,7 +1917,6 @@ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.13.0.tgz", "integrity": "sha512-WKR4ucALq+lwx0WJZW17CspeTpXorbIOpvKv5mulZica6QxqfMhn8n1IXCkDws/mCoLRx4Drk5d377tIjFNsvQ==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-changeset": "^2.3.0", "prosemirror-collab": "^1.3.1", @@ -2026,7 +2062,6 @@ "integrity": "sha512-gWEkeiyYE4vqjON/+Obqcoeffmk0NF15WSBwSs7zwVA2bAbTaE0SJ7P0WNGoJn8uE7fiaV5a7dKYIJriEqOrmA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2122,7 +2157,6 @@ "integrity": "sha512-PC0PDZfJg8sP7cmKe6L3QIL8GZwU5aRvUFedqSIpw3B+QjRSUZeeITC2M5XKeMXEzL6wccN196iy3JLwKNvDVA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.48.1", "@typescript-eslint/types": "8.48.1", @@ -2277,13 +2311,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -2457,7 +2491,6 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2739,6 +2772,7 @@ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -2870,7 +2904,8 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/data-urls": { "version": "5.0.0", @@ -2950,7 +2985,8 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/devlop": { "version": "1.1.0", @@ -3126,7 +3162,6 @@ "integrity": "sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -3468,6 +3503,7 @@ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=6" } @@ -3961,7 +3997,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "@babel/runtime": "^7.28.4" }, @@ -5150,9 +5185,9 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { @@ -5688,7 +5723,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", "license": "MIT", - "peer": true, "dependencies": { "orderedmap": "^2.0.0" } @@ -5718,7 +5752,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.0.0", "prosemirror-transform": "^1.0.0", @@ -5767,7 +5800,6 @@ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.3.tgz", "integrity": "sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==", "license": "MIT", - "peer": true, "dependencies": { "prosemirror-model": "^1.20.0", "prosemirror-state": "^1.0.0", @@ -5856,6 +5888,7 @@ "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.4.tgz", "integrity": "sha512-wOmoNZoOpvMminhifQYiYSTCLUDOiUbBunrMrMjA+dV52sY+vck1S4UhR6PkgnoCquvvMSeJjErXZ4qSaWCliA==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" @@ -5866,6 +5899,7 @@ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", "license": "MIT", + "peer": true, "dependencies": { "react-remove-scroll-bar": "^2.3.7", "react-style-singleton": "^2.2.3", @@ -5891,6 +5925,7 @@ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "license": "MIT", + "peer": true, "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" @@ -5913,6 +5948,7 @@ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "license": "MIT", + "peer": true, "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" @@ -5935,6 +5971,7 @@ "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", "license": "MIT", + "peer": true, "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", @@ -6125,9 +6162,9 @@ "license": "MIT" }, "node_modules/rollup": { - "version": "4.53.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.53.3.tgz", - "integrity": "sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "license": "MIT", "dependencies": { @@ -6141,28 +6178,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.53.3", - "@rollup/rollup-android-arm64": "4.53.3", - "@rollup/rollup-darwin-arm64": "4.53.3", - "@rollup/rollup-darwin-x64": "4.53.3", - "@rollup/rollup-freebsd-arm64": "4.53.3", - "@rollup/rollup-freebsd-x64": "4.53.3", - "@rollup/rollup-linux-arm-gnueabihf": "4.53.3", - "@rollup/rollup-linux-arm-musleabihf": "4.53.3", - "@rollup/rollup-linux-arm64-gnu": "4.53.3", - "@rollup/rollup-linux-arm64-musl": "4.53.3", - "@rollup/rollup-linux-loong64-gnu": "4.53.3", - "@rollup/rollup-linux-ppc64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-gnu": "4.53.3", - "@rollup/rollup-linux-riscv64-musl": "4.53.3", - "@rollup/rollup-linux-s390x-gnu": "4.53.3", - "@rollup/rollup-linux-x64-gnu": "4.53.3", - "@rollup/rollup-linux-x64-musl": "4.53.3", - "@rollup/rollup-openharmony-arm64": "4.53.3", - "@rollup/rollup-win32-arm64-msvc": "4.53.3", - "@rollup/rollup-win32-ia32-msvc": "4.53.3", - "@rollup/rollup-win32-x64-gnu": "4.53.3", - "@rollup/rollup-win32-x64-msvc": "4.53.3", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -6200,7 +6240,8 @@ "version": "0.27.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/semver": { "version": "7.7.3", @@ -7082,6 +7123,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "license": "(MIT OR CC0-1.0)", + "peer": true, "engines": { "node": ">=16" }, @@ -7267,6 +7309,7 @@ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "license": "MIT", + "peer": true, "dependencies": { "tslib": "^2.0.0" }, @@ -7288,6 +7331,7 @@ "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -7302,6 +7346,7 @@ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", "license": "MIT", + "peer": true, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -7316,6 +7361,7 @@ "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", "license": "MIT", + "peer": true, "dependencies": { "use-isomorphic-layout-effect": "^1.1.1" }, @@ -7333,6 +7379,7 @@ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "license": "MIT", + "peer": true, "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -7416,7 +7463,6 @@ "integrity": "sha512-ITcnkFeR3+fI8P1wMgItjGrR10170d8auB4EpMLPqmx6uxElH3a/hHGQabSHKdqd4FXWO1nFIp9rRn7JQ34ACQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -7796,7 +7842,6 @@ "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", "license": "MIT", - "peer": true, "dependencies": { "lib0": "^0.2.85" }, @@ -7856,7 +7901,6 @@ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", "license": "MIT", - "peer": true, "dependencies": { "lib0": "^0.2.99" }, diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3230c92ec7d..2978a88ee46 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,18 +9,18 @@ "version": "0.1.0", "license": "GPLv3", "dependencies": { - "@angular/animations": "^21.1.5", - "@angular/cdk": "^21.1.5", - "@angular/cli": "^21.1.4", - "@angular/common": "^21.1.5", - "@angular/compiler": "^21.1.5", - "@angular/compiler-cli": "^21.1.5", - "@angular/core": "^21.1.5", - "@angular/elements": "^21.1.5", - "@angular/forms": "^21.1.5", - "@angular/platform-browser": "^21.1.5", - "@angular/platform-browser-dynamic": "^21.1.5", - "@angular/router": "^21.1.5", + "@angular/animations": "^21.1.6", + "@angular/cdk": "^21.1.6", + "@angular/cli": "^21.1.5", + "@angular/common": "^21.1.6", + "@angular/compiler": "^21.1.6", + "@angular/compiler-cli": "^21.1.6", + "@angular/core": "^21.1.6", + "@angular/elements": "^21.1.6", + "@angular/forms": "^21.1.6", + "@angular/platform-browser": "^21.1.6", + "@angular/platform-browser-dynamic": "^21.1.6", + "@angular/router": "^21.1.6", "@appsignal/javascript": "^1.6.1", "@appsignal/plugin-breadcrumbs-console": "^1.1.37", "@appsignal/plugin-breadcrumbs-network": "^1.1.24", @@ -65,14 +65,14 @@ "@rails/request.js": "^0.0.13", "@stimulus-components/auto-submit": "^6.0.0", "@stimulus-components/reveal": "^5.0.0", - "@tiptap/extensions": "^3.13.0", + "@tiptap/extensions": "^3.20.0", "@types/jquery.cookie": "^1.4.36", "@uirouter/angular": "^17.0.0", "@uirouter/core": "^6.1.0", "@uirouter/rx": "^1.0.0", "@w11k/ngx-componentdestroyed": "^5.0.2", "@xeokit/xeokit-bim-viewer": "2.7.1", - "autoprefixer": "^10.4.23", + "autoprefixer": "^10.4.27", "byte-base64": "^1.1.0", "chart.js": "4.5.1", "chartjs-adapter-luxon": "^1.3.1", @@ -130,16 +130,16 @@ }, "devDependencies": { "@angular-builders/custom-esbuild": "^21.0.3", - "@angular-devkit/build-angular": "^21.1.4", + "@angular-devkit/build-angular": "^21.1.5", "@angular-eslint/builder": "20.7.0", "@angular-eslint/eslint-plugin": "20.7.0", "@angular-eslint/eslint-plugin-template": "20.7.0", "@angular-eslint/schematics": "20.7.0", "@angular-eslint/template-parser": "20.7.0", - "@angular/language-service": "21.1.5", + "@angular/language-service": "21.1.6", "@eslint/js": "^9.39.2", - "@html-eslint/eslint-plugin": "^0.54.2", - "@html-eslint/parser": "^0.54.0", + "@html-eslint/eslint-plugin": "^0.57.1", + "@html-eslint/parser": "^0.57.1", "@jsdevtools/coverage-istanbul-loader": "3.0.5", "@stylistic/eslint-plugin": "^5.7.1", "@types/codemirror": "5.60.5", @@ -155,7 +155,7 @@ "@types/mousetrap": "^1.6.3", "@types/pako": "^2.0.4", "@types/rails__request.js": "^0.0.1", - "@types/react": "^19.2.10", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/resize-observer-browser": "^0.1.4", "@types/urijs": "^1.19.26", @@ -184,7 +184,7 @@ "source-map-explorer": "^2.5.2", "ts-node": "~10.9.2", "typescript": "^5.9.3", - "typescript-eslint": "^8.54.0", + "typescript-eslint": "^8.56.1", "wscat": "^6.1.0" }, "optionalDependencies": { @@ -648,16 +648,16 @@ } }, "node_modules/@angular-devkit/build-angular": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.4.tgz", - "integrity": "sha512-2HPCo6vEu5EIwxxFYhnmdfbktRBoOVQD3q7lG9PMQPf/jRCnyIZ70qSbXbAV96IMDLFl8mLRfY4scoaFMIYGMw==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.5.tgz", + "integrity": "sha512-B2jOBAiVl+hA3PLwpxfrbW/gA7SDu9Uv+hQwHYrdwL2XXDVwaQ+c3z9BS3yJDQTkb/TrAJ0sfa2zVLC4b/rHzg==", "dev": true, "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.4", - "@angular-devkit/build-webpack": "0.2101.4", - "@angular-devkit/core": "21.1.4", - "@angular/build": "21.1.4", + "@angular-devkit/architect": "0.2101.5", + "@angular-devkit/build-webpack": "0.2101.5", + "@angular-devkit/core": "21.1.5", + "@angular/build": "21.1.5", "@babel/core": "7.28.5", "@babel/generator": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", @@ -668,7 +668,7 @@ "@babel/preset-env": "7.28.5", "@babel/runtime": "7.28.4", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "21.1.4", + "@ngtools/webpack": "21.1.5", "ansi-colors": "4.1.3", "autoprefixer": "10.4.23", "babel-loader": "10.0.0", @@ -723,7 +723,7 @@ "@angular/platform-browser": "^21.0.0", "@angular/platform-server": "^21.0.0", "@angular/service-worker": "^21.0.0", - "@angular/ssr": "^21.1.4", + "@angular/ssr": "^21.1.5", "@web/test-runner": "^0.20.0", "browser-sync": "^3.0.2", "jest": "^30.2.0", @@ -780,12 +780,12 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dev": true, "dependencies": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" }, "bin": { @@ -798,12 +798,12 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dev": true, "dependencies": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -833,6 +833,22 @@ "node": ">=14.17.0" } }, + "node_modules/@angular-devkit/build-angular/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -863,6 +879,42 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/@angular-devkit/build-angular/node_modules/autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, "node_modules/@angular-devkit/build-angular/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -1046,12 +1098,12 @@ } }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.4.tgz", - "integrity": "sha512-lPjPxeEzUha4bnlGzD3KFFf3yxcQjOfV9wwZIa4XLsqjCZsUk95TzHQH7i64OCTw9uKTEQkJBAuO6v2WXHxopw==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.5.tgz", + "integrity": "sha512-G3mvUXiSU3DL1QKngq/yXT94Wr+IdqtOM/1VC3NmsV9KX3OSfwfc560dmhY1efqc9gBA5qL+7kLlgV7Kx/Su3A==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/architect": "0.2101.5", "rxjs": "7.8.2" }, "engines": { @@ -1065,12 +1117,12 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dev": true, "dependencies": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" }, "bin": { @@ -1083,12 +1135,12 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dev": true, "dependencies": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -1109,6 +1161,22 @@ } } }, + "node_modules/@angular-devkit/build-webpack/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@angular-devkit/build-webpack/node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -1373,9 +1441,9 @@ "license": "MIT" }, "node_modules/@angular/animations": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.5.tgz", - "integrity": "sha512-gsqHX8lCYV8cgVtHs0iLwrX8SVlmcjUF44l/xCc/jBC/TeKWRl2e6Jqrn1Wcd0NDlGiNsm+mYNyqMyy5/I7kjw==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.6.tgz", + "integrity": "sha512-Ft8B0tHBRyd7ORpbaa2S0yRqHSdsyMH6oot7ODAA3kv03k4GTwp74U6Y/NyV4JoZ+lr28+EV4YMv3mAqJwIy/g==", "dependencies": { "tslib": "^2.3.0" }, @@ -1383,17 +1451,17 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "21.1.5" + "@angular/core": "21.1.6" } }, "node_modules/@angular/build": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.4.tgz", - "integrity": "sha512-7CAAQPWFMMqod40ox5MOVB/CnoBXFDehyQhs0hls6lu7bOy/M0EDy0v6bERkyNGRz1mihWWBiCV8XzEinrlq1A==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.5.tgz", + "integrity": "sha512-v2eDinWKlSKuk5pyMMY8j5TMFW8HA9B1l13TrDDpxsRGAAzekg7TFNyuh1x9Y6Rq4Vn+8/8pCjMUPZigzWbMhQ==", "dev": true, "dependencies": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/architect": "0.2101.5", "@babel/core": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -1436,7 +1504,7 @@ "@angular/platform-browser": "^21.0.0", "@angular/platform-server": "^21.0.0", "@angular/service-worker": "^21.0.0", - "@angular/ssr": "^21.1.4", + "@angular/ssr": "^21.1.5", "karma": "^6.4.0", "less": "^4.2.0", "ng-packagr": "^21.0.0", @@ -1486,12 +1554,12 @@ } }, "node_modules/@angular/build/node_modules/@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dev": true, "dependencies": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" }, "bin": { @@ -1504,12 +1572,12 @@ } }, "node_modules/@angular/build/node_modules/@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dev": true, "dependencies": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -1530,6 +1598,22 @@ } } }, + "node_modules/@angular/build/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@angular/build/node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -1587,9 +1671,9 @@ } }, "node_modules/@angular/cdk": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.5.tgz", - "integrity": "sha512-AlQPgqe3LLwXCyrDwYSX3m/WKnl2ppCMW7Gb+7bJpIcpMdWYEpSOSQF318jXGYIysKg43YbdJ1tWhJWY/cbn3w==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.6.tgz", + "integrity": "sha512-a6isY8eWxfRO99KPs8GLMQRDE06diNbPmD9tcYi+EWxjPFOZ88QJmMN8a0yja+LDQaG2kRbNBLjYUEro3qCpTQ==", "dependencies": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -1626,17 +1710,17 @@ } }, "node_modules/@angular/cli": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.4.tgz", - "integrity": "sha512-XsMHgxTvHGiXXrhYZz3zMZYhYU0gHdpoHKGiEKXwcx+S1KoYbIssyg6oF2Kq49ZaE0OYCTKjnvgDce6ZqdkJ/A==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.5.tgz", + "integrity": "sha512-ljqvAzSk8FKMaYW/aZhR+SXjudbQViYYkMlJvJUClGpokjDM9KfJWPX+QZfr2J+piW5yaaHmFaIMddO9QxkUDQ==", "dependencies": { - "@angular-devkit/architect": "0.2101.4", - "@angular-devkit/core": "21.1.4", - "@angular-devkit/schematics": "21.1.4", + "@angular-devkit/architect": "0.2101.5", + "@angular-devkit/core": "21.1.5", + "@angular-devkit/schematics": "21.1.5", "@inquirer/prompts": "7.10.1", "@listr2/prompt-adapter-inquirer": "3.0.5", "@modelcontextprotocol/sdk": "1.26.0", - "@schematics/angular": "21.1.4", + "@schematics/angular": "21.1.5", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.46.2", "ini": "6.0.0", @@ -1660,11 +1744,11 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dependencies": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" }, "bin": { @@ -1677,11 +1761,11 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dependencies": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -1703,11 +1787,11 @@ } }, "node_modules/@angular/cli/node_modules/@angular-devkit/schematics": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", - "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.5.tgz", + "integrity": "sha512-CGmoorQL5+mVCJEHwHWOrhSd1hFxB3h66i9wUDizJAEQUM3mSml5SiglHArpWY/G4GmFwi6XVe+Jm3U8J/mcFg==", "dependencies": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", @@ -1719,6 +1803,21 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@angular/cli/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@angular/cli/node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -1948,9 +2047,9 @@ } }, "node_modules/@angular/common": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.5.tgz", - "integrity": "sha512-olO2F0b+H8YBfsuQFEwo9Hjf+B714xGcttDW37+4jnY2IRS2uYeMu2RGIpY7ps+0uZ017c4iK3CCgSPBgmbTcA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.6.tgz", + "integrity": "sha512-qEfwyJhebl2tHwFhKHE/ZzsCLMWnQ0u/UDS23KUA4tTWaOKH8Usu7DS1B3KnUravII8R6ZpYn86L+B903v9WxA==", "dependencies": { "tslib": "^2.3.0" }, @@ -1958,14 +2057,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "21.1.5", + "@angular/core": "21.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/compiler": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.5.tgz", - "integrity": "sha512-yRUdWlL+AWcTL4d7zD0jkNqsjvxXpWEihvOfD2gc65DO0+E80DsWIpHq9A8yWeLukbfLcmBGI2QbfW9+SXAlvg==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.6.tgz", + "integrity": "sha512-8RFdfbWTyx+OiRkiK2LsYPuhv8b65S3x/4+98kZRCrzP6YNjq/qsuqpfMUIKwnjok78FfhGyEx4I5LOV3Vkabw==", "dependencies": { "tslib": "^2.3.0" }, @@ -1974,9 +2073,9 @@ } }, "node_modules/@angular/compiler-cli": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.5.tgz", - "integrity": "sha512-i2r2bQuWdjjFGTd2TA7FtCWNx5yJ3BMoyTGUC9lzSfmxWAfcH/NWR+6OdaEVwv6Zap3IXYYxs8S+REkx954EwA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.6.tgz", + "integrity": "sha512-0JU2cBDMSB4hU4KwDS2ThrkGh+Njf8Yfm11CKR0NWbHGwW1xHa7whlcpUzX/USqL+FNGXQ75R0fOcZrT86YvrA==", "dependencies": { "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -1995,7 +2094,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "21.1.5", + "@angular/compiler": "21.1.6", "typescript": ">=5.9 <6.0" }, "peerDependenciesMeta": { @@ -2152,9 +2251,9 @@ } }, "node_modules/@angular/core": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.5.tgz", - "integrity": "sha512-m61YHiyE+SIvS8UXcFLjYCucv6ShJJCwz9xxEk7ysYW9wOtHdfIf9tgyOsucZDAvrvpSyQLRj5jGBCGm1VIvXA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.6.tgz", + "integrity": "sha512-c+n9Ynq1Ar+4SOaL10C/arqBje0dUFFUaDyErXp3jPXU/L29fsFTlmKM2EWunM1RhJckYonJ/xtH0gwwrH6W9Q==", "dependencies": { "tslib": "^2.3.0" }, @@ -2162,7 +2261,7 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/compiler": "21.1.5", + "@angular/compiler": "21.1.6", "rxjs": "^6.5.3 || ^7.4.0", "zone.js": "~0.15.0 || ~0.16.0" }, @@ -2176,9 +2275,9 @@ } }, "node_modules/@angular/elements": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.5.tgz", - "integrity": "sha512-SoXNNLEBn9w3NP5vZJiMQfdiJ1FLY0VuDP5VvscPVqebh/nfpDw7jOu8Pb2S9wTANsfmlFX4QK1ehl3nJVY/Fw==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.6.tgz", + "integrity": "sha512-LvF0kgh1XxLlXyw5CS5C787HYFAIGVHAg9HNgGZKINeu16Og7KCnsVszAyWiVn1t3trcLARcScYgrOcuwBYzIg==", "dependencies": { "tslib": "^2.3.0" }, @@ -2186,14 +2285,14 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/core": "21.1.5", + "@angular/core": "21.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/forms": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.5.tgz", - "integrity": "sha512-Z8Vcgz5KYlCobRxLjyGGUBv0mA4nusuiD36GqYRn3sR780TLDcPFVwTCwVEWLdwID64oiHXG+x9jjU/Z3HzR6A==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.6.tgz", + "integrity": "sha512-Bw3nVDWihGUGyys7oq2zdJ2MjvJvU1x1WaExYmp3rKU3S7rQXGq6IxY8bopTtHirTANrY2KUEnJ2IlK+xVg9OA==", "dependencies": { "@standard-schema/spec": "^1.0.0", "tslib": "^2.3.0" @@ -2202,25 +2301,25 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "21.1.5", - "@angular/core": "21.1.5", - "@angular/platform-browser": "21.1.5", + "@angular/common": "21.1.6", + "@angular/core": "21.1.6", + "@angular/platform-browser": "21.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, "node_modules/@angular/language-service": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.5.tgz", - "integrity": "sha512-/MiXx/peMBjMHEX6Gm7C6ZHOlcgEzkWeBlI7WWnIEDU9vU6F3nU4kauBYdLr/EpXhHpdpOtL4eknPhAfdvVrJA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.6.tgz", + "integrity": "sha512-I2gYI9cKP/B/rUz8WVaWNpuULSdq4W+ZUm6YQyUXdGEh3dDEssK1xLucPBfUT7RAr6h7+w5/RS+zKWC1q9g24w==", "dev": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/@angular/platform-browser": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.5.tgz", - "integrity": "sha512-rAN0cu05Pg7HHe9JMRd3g5JyyVCeFW8QiB/jG6klUrOTF4QzyCbmwlm7MX0uTx3CWAZraWCGbdahUkLyYtuqFA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.6.tgz", + "integrity": "sha512-im6aNcgYdIYIVW2262ATkC39WUmhc+KVNVKwKtO5jlOsq9TWmxT1/esncEAlokMe5os6eeb/Ga4D6Ghj0gj4Ig==", "dependencies": { "tslib": "^2.3.0" }, @@ -2228,9 +2327,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/animations": "21.1.5", - "@angular/common": "21.1.5", - "@angular/core": "21.1.5" + "@angular/animations": "21.1.6", + "@angular/common": "21.1.6", + "@angular/core": "21.1.6" }, "peerDependenciesMeta": { "@angular/animations": { @@ -2239,9 +2338,9 @@ } }, "node_modules/@angular/platform-browser-dynamic": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.5.tgz", - "integrity": "sha512-Pd8nPbJSIONnze1WS9wLBAtaFw4TYIH+ZGjKHS9G1E9l09tDWtHWyB7dY82Sc//Nc8iR4V7dcsbUmFjOJHThww==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.6.tgz", + "integrity": "sha512-lVtHkhK/jnrGdX+4S8ItfMO+5buHAU9NMHeDq+QqalnXznMaC7Qd4BPLcRWW4QAI177zG0NE1Bet5cjO75N9+w==", "dependencies": { "tslib": "^2.3.0" }, @@ -2249,16 +2348,16 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "21.1.5", - "@angular/compiler": "21.1.5", - "@angular/core": "21.1.5", - "@angular/platform-browser": "21.1.5" + "@angular/common": "21.1.6", + "@angular/compiler": "21.1.6", + "@angular/core": "21.1.6", + "@angular/platform-browser": "21.1.6" } }, "node_modules/@angular/router": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.5.tgz", - "integrity": "sha512-OjFn6Nw51CU712CMbl2U9TxlCkzOmjMLYPAfnV4+RdG7o+/eOS2nV0oapJ88RNCw7Yl04PA1amc3ql3agDFd4A==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.6.tgz", + "integrity": "sha512-JJn0gfeRks2czbeLmtxxjIlmKtOmjYi3yAaoAwiwpbfVHPLJeJ32axLJREAU0dBkThF8YD+r18uEJ9UrvkqrtA==", "dependencies": { "tslib": "^2.3.0" }, @@ -2266,9 +2365,9 @@ "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "@angular/common": "21.1.5", - "@angular/core": "21.1.5", - "@angular/platform-browser": "21.1.5", + "@angular/common": "21.1.6", + "@angular/core": "21.1.6", + "@angular/platform-browser": "21.1.6", "rxjs": "^6.5.3 || ^7.4.0" } }, @@ -4705,9 +4804,9 @@ } }, "node_modules/@eslint/css-tree": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.8.tgz", - "integrity": "sha512-s0f40zY7dlMp8i0Jf0u6l/aSswS0WRAgkhgETgiCJRcxIWb4S/Sp9uScKHWbkM3BnoFLbJbmOYk5AZUDFVxaLA==", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.9.tgz", + "integrity": "sha512-3D5/OHibNEGk+wKwNwMbz63NMf367EoR4mVNNpxddCHKEb2Nez7z62J2U6YjtErSsZDoY0CsccmoUpdEbkogNA==", "dev": true, "dependencies": { "mdn-data": "2.23.0", @@ -5156,9 +5255,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==", + "version": "1.19.10", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.10.tgz", + "integrity": "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==", "engines": { "node": ">=18.14.1" }, @@ -5188,17 +5287,34 @@ "@rails/actioncable": ">=7.0" } }, + "node_modules/@html-eslint/core": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/core/-/core-0.57.0.tgz", + "integrity": "sha512-X/cKrOmXrxZSdgyKwtbaCuuJ1k/u82MK58Q6p1TzfwPatwIYx+icfBv1Vp1dLui0L0y1fwBW4H+TKhBf7mMKmg==", + "dev": true, + "dependencies": { + "@html-eslint/types": "^0.57.0", + "eslint": "^9.39.1", + "html-standard": "^0.0.13" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@html-eslint/eslint-plugin": { - "version": "0.54.2", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.54.2.tgz", - "integrity": "sha512-C6jhJqVGTS9AW3Z84Ni/Cs6h3XcRHUXi1YkRaAYI08MeNj6ZWIXhwKBEJgEGK2YxzOcM1TpZEvHL4d5z7aC7Eg==", + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.57.1.tgz", + "integrity": "sha512-IDfdk3V27eebNpdXD2NLy/lnTSbUuKrro/6YJICBn/9aiXPXagNqWJB38qcSWEoxADbXfSSn17DJWcXvQTkHBg==", "dev": true, "dependencies": { "@eslint/plugin-kit": "^0.4.1", - "@html-eslint/parser": "^0.54.0", - "@html-eslint/template-parser": "^0.54.0", - "@html-eslint/template-syntax-parser": "^0.54.0", - "@html-eslint/types": "^0.54.0" + "@html-eslint/core": "^0.57.0", + "@html-eslint/parser": "^0.57.1", + "@html-eslint/template-parser": "^0.57.0", + "@html-eslint/template-syntax-parser": "^0.57.0", + "@html-eslint/types": "^0.57.0", + "@rviscomi/capo.js": "^2.1.0", + "html-standard": "^0.0.13" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5208,41 +5324,41 @@ } }, "node_modules/@html-eslint/parser": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.54.0.tgz", - "integrity": "sha512-ia3I/6jf87679pUrhIDKSgddKbw9GLkvO86fYt7tJQHHRQu+zySErIxuI2b941oeovzOq10dTpC5Hp41qbvPgg==", + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.57.1.tgz", + "integrity": "sha512-nQ5vw7Os+Snjxq9hLLBak2bv502Obn77BNOWfGK2+GIrShxtGd8w1ehlKW3EB5/RQzqBk6VDK8nPfexlR3M7kg==", "dev": true, "dependencies": { - "@eslint/css-tree": "^3.6.8", - "@html-eslint/template-syntax-parser": "^0.54.0", - "@html-eslint/types": "^0.54.0", + "@eslint/css-tree": "^3.6.9", + "@html-eslint/template-syntax-parser": "^0.57.0", + "@html-eslint/types": "^0.57.0", "css-tree": "^3.1.0", "es-html-parser": "0.3.1" } }, "node_modules/@html-eslint/template-parser": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.54.0.tgz", - "integrity": "sha512-gSjgmGwRQehNxZ3XdRUhUoXDFzYc/LYoKA7JwExjdvklGnSh5WkH/CLOlphkDh9jJsC1O/E0I04bVGrzy3idKQ==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.57.0.tgz", + "integrity": "sha512-tddyBo4dEl4W4Ehxuyd6H4jsSqvsfL5F7Bj9/aFfdQyv36q7BGWM2BRHb6FMmYKAPGZ3VzyEbUlcqIwXpDkY3w==", "dev": true, "dependencies": { - "@html-eslint/types": "^0.54.0", + "@html-eslint/types": "^0.57.0", "es-html-parser": "0.3.1" } }, "node_modules/@html-eslint/template-syntax-parser": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.54.0.tgz", - "integrity": "sha512-7mDM4AWqz42FHwnOt8Lu5xqovaZHlEuBrmwNrMg6VwC9TPaLVyh4j3zNzNnM6tjftaXZzverJup39zGB8mvXjg==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.57.0.tgz", + "integrity": "sha512-vHp5y4TR+HhgMDi3rAkgm90LBptSZaQUJudZSj+WdvnSBjLe/fgJC4aVjtLVHTS9ynORrFio8AmH1Bz20kYk4g==", "dev": true, "dependencies": { - "@html-eslint/types": "^0.54.0" + "@html-eslint/types": "^0.57.0" } }, "node_modules/@html-eslint/types": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.54.0.tgz", - "integrity": "sha512-bfJolxay0POMYaFWTCH1MBitEaxIEKZOoROGOLZiRBaPvQrzhwYQktuyt5X1PcHqUB4HwEtYgSdpjYGT4JbrvA==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.57.0.tgz", + "integrity": "sha512-wZAHc9FHZRVAcKyx1NdMNGpw1Jo/Anh+9y+bTQ/cKjh5MHJlbs8ogthIG8efBVFIVlIgzxEA8yrX+DPXmuWisA==", "dev": true, "dependencies": { "@types/css-tree": "^2.3.11", @@ -7046,9 +7162,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.4.tgz", - "integrity": "sha512-CgKnMofIVGTwNPqFNZmkmr2aLOFUG/AKm8lauXU+juwSaY7Z28eguFd+J42uVUOnasLxINQY9y7kr9f6deTrcg==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.5.tgz", + "integrity": "sha512-5nG9v/nEzsaKxgw5NurM6tPKPw0OYsCM3DL4ZI8+TidT55hYbsroTnyBcHBouJ1qlZlQXNtlsjsjBmBDtF7JZA==", "dev": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=24.0.0", @@ -7989,350 +8105,325 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "freebsd" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "cpu": [ "arm" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "cpu": [ "loong64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "cpu": [ "ppc64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "cpu": [ "riscv64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "cpu": [ "s390x" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openbsd" ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "openharmony" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "cpu": [ "arm64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "cpu": [ "ia32" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "cpu": [ "x64" ], "dev": true, - "license": "MIT", "optional": true, "os": [ "win32" @@ -8344,13 +8435,19 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "node_modules/@rviscomi/capo.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@rviscomi/capo.js/-/capo.js-2.1.0.tgz", + "integrity": "sha512-y6J+KJqsrY8AcDswLKkvd8KdpFindjS4Q9rSuK8CIpsQOepEjgRaMR4S8OtuLOQoVYLCROT3ffMQqRWrUMQdQA==", + "dev": true + }, "node_modules/@schematics/angular": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.4.tgz", - "integrity": "sha512-I1zdSNzdbrVCWpeE2NsZQmIoa9m0nlw4INgdGIkqUH6FgwvoGKC0RoOxKAmm6HHVJ48FE/sPI13dwAeK89ow5A==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.5.tgz", + "integrity": "sha512-AndJ17ePYUoqJqiIF9VaXbGAFfOqDcHuAxhwozsQlWDzwgQSOUC/WWeG9hKVCgMD6tE02Sxr2ova9DiBKsLQNg==", "dependencies": { - "@angular-devkit/core": "21.1.4", - "@angular-devkit/schematics": "21.1.4", + "@angular-devkit/core": "21.1.5", + "@angular-devkit/schematics": "21.1.5", "jsonc-parser": "3.3.1" }, "engines": { @@ -8360,11 +8457,11 @@ } }, "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dependencies": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -8386,11 +8483,11 @@ } }, "node_modules/@schematics/angular/node_modules/@angular-devkit/schematics": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", - "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.5.tgz", + "integrity": "sha512-CGmoorQL5+mVCJEHwHWOrhSd1hFxB3h66i9wUDizJAEQUM3mSml5SiglHArpWY/G4GmFwi6XVe+Jm3U8J/mcFg==", "dependencies": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", @@ -8402,6 +8499,21 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@schematics/angular/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/@schematics/angular/node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -8523,11 +8635,11 @@ } }, "node_modules/@schematics/angular/node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "dependencies": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" @@ -8911,17 +9023,16 @@ } }, "node_modules/@tiptap/extensions": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.15.3.tgz", - "integrity": "sha512-ycx/BgxR4rc9tf3ZyTdI98Z19yKLFfqM3UN+v42ChuIwkzyr9zyp7kG8dB9xN2lNqrD+5y/HyJobz/VJ7T90gA==", - "license": "MIT", + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.0.tgz", + "integrity": "sha512-HIsXX942w3nbxEQBlMAAR/aa6qiMBEP7CsSMxaxmTIVAmW35p6yUASw6GdV1u0o3lCZjXq2OSRMTskzIqi5uLg==", "funding": { "type": "github", "url": "https://github.com/sponsors/ueberdosis" }, "peerDependencies": { - "@tiptap/core": "^3.15.3", - "@tiptap/pm": "^3.15.3" + "@tiptap/core": "^3.20.0", + "@tiptap/pm": "^3.20.0" } }, "node_modules/@tiptap/pm": { @@ -9051,9 +9162,9 @@ } }, "node_modules/@tufjs/models/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dependencies": { "brace-expansion": "^5.0.2" }, @@ -9402,9 +9513,9 @@ "dev": true }, "node_modules/@types/react": { - "version": "19.2.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", - "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "dependencies": { "csstype": "^3.2.2" @@ -9705,25 +9816,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { @@ -9765,12 +9864,12 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10049,25 +10148,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/debug": { @@ -10100,12 +10187,12 @@ } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10154,25 +10241,13 @@ "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true, - "engines": { - "node": "18 || 20 || >=22" - } - }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { - "balanced-match": "^4.0.2" - }, - "engines": { - "node": "18 || 20 || >=22" + "balanced-match": "^1.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { @@ -10193,12 +10268,12 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -10208,15 +10283,15 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10226,18 +10301,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "dev": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "engines": { @@ -10252,13 +10327,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10269,9 +10344,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10285,9 +10360,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10298,17 +10373,17 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "dev": true, "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" @@ -10325,13 +10400,13 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -10351,9 +10426,9 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "dependencies": { "balanced-match": "^4.0.2" @@ -10380,27 +10455,27 @@ } }, "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@typescript-eslint/utils/node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -10487,6 +10562,12 @@ "vite": "^6.0.0 || ^7.0.0" } }, + "node_modules/@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true + }, "node_modules/@w11k/ngx-componentdestroyed": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@w11k/ngx-componentdestroyed/-/ngx-componentdestroyed-5.0.2.tgz", @@ -11480,9 +11561,9 @@ "integrity": "sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==" }, "node_modules/autoprefixer": { - "version": "10.4.23", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", - "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", "funding": [ { "type": "opencollective", @@ -11499,7 +11580,7 @@ ], "dependencies": { "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001760", + "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -12063,9 +12144,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001764", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", - "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==", + "version": "1.0.30001776", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz", + "integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==", "funding": [ { "type": "opencollective", @@ -12079,8 +12160,7 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ], - "license": "CC-BY-4.0" + ] }, "node_modules/ccount": { "version": "2.0.1", @@ -14557,11 +14637,11 @@ } }, "node_modules/express-rate-limit": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", - "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz", + "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==", "dependencies": { - "ip-address": "10.0.1" + "ip-address": "10.1.0" }, "engines": { "node": ">= 16" @@ -14573,14 +14653,6 @@ "express": ">= 4.11" } }, - "node_modules/express-rate-limit/node_modules/ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", - "engines": { - "node": ">= 12" - } - }, "node_modules/express/node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -14759,9 +14831,9 @@ } }, "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.7.tgz", - "integrity": "sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" @@ -15202,9 +15274,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dependencies": { "brace-expansion": "^5.0.2" }, @@ -15679,9 +15751,9 @@ } }, "node_modules/hono": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.0.tgz", - "integrity": "sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==", + "version": "4.12.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", + "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", "engines": { "node": ">=16.9.0" } @@ -15764,6 +15836,16 @@ "node": ">=0.10.0" } }, + "node_modules/html-standard": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/html-standard/-/html-standard-0.0.13.tgz", + "integrity": "sha512-6oNfW3c1t44O7jVXu0tp4E5MbHifWlXrHlZBPt6y7vFdgLOUUh8hyzoRhfUgozlBUK6oLLYhqP1uIqbZ8ggcBA==", + "dev": true, + "dependencies": { + "vscode-css-languageservice": "^6.3.9", + "vscode-languageserver-textdocument": "^1.0.12" + } + }, "node_modules/html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -16105,9 +16187,9 @@ } }, "node_modules/ignore-walk/node_modules/minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dependencies": { "brace-expansion": "^5.0.2" }, @@ -16133,9 +16215,9 @@ } }, "node_modules/immutable": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", - "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "dev": true }, "node_modules/import-fresh": { @@ -19275,9 +19357,9 @@ "dev": true }, "node_modules/minimatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", - "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" @@ -22010,11 +22092,10 @@ } }, "node_modules/rollup": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", - "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, - "license": "MIT", "dependencies": { "@types/estree": "1.0.8" }, @@ -22026,31 +22107,31 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.55.1", - "@rollup/rollup-android-arm64": "4.55.1", - "@rollup/rollup-darwin-arm64": "4.55.1", - "@rollup/rollup-darwin-x64": "4.55.1", - "@rollup/rollup-freebsd-arm64": "4.55.1", - "@rollup/rollup-freebsd-x64": "4.55.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", - "@rollup/rollup-linux-arm-musleabihf": "4.55.1", - "@rollup/rollup-linux-arm64-gnu": "4.55.1", - "@rollup/rollup-linux-arm64-musl": "4.55.1", - "@rollup/rollup-linux-loong64-gnu": "4.55.1", - "@rollup/rollup-linux-loong64-musl": "4.55.1", - "@rollup/rollup-linux-ppc64-gnu": "4.55.1", - "@rollup/rollup-linux-ppc64-musl": "4.55.1", - "@rollup/rollup-linux-riscv64-gnu": "4.55.1", - "@rollup/rollup-linux-riscv64-musl": "4.55.1", - "@rollup/rollup-linux-s390x-gnu": "4.55.1", - "@rollup/rollup-linux-x64-gnu": "4.55.1", - "@rollup/rollup-linux-x64-musl": "4.55.1", - "@rollup/rollup-openbsd-x64": "4.55.1", - "@rollup/rollup-openharmony-arm64": "4.55.1", - "@rollup/rollup-win32-arm64-msvc": "4.55.1", - "@rollup/rollup-win32-ia32-msvc": "4.55.1", - "@rollup/rollup-win32-x64-gnu": "4.55.1", - "@rollup/rollup-win32-x64-msvc": "4.55.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" } }, @@ -23514,9 +23595,9 @@ } }, "node_modules/tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", + "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -24095,15 +24176,15 @@ } }, "node_modules/typescript-eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.54.0.tgz", - "integrity": "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", + "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", "dev": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "8.54.0", - "@typescript-eslint/parser": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0" + "@typescript-eslint/eslint-plugin": "8.56.1", + "@typescript-eslint/parser": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24113,21 +24194,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" @@ -24140,21 +24221,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.54.0", - "eslint": "^8.57.0 || ^9.0.0", + "@typescript-eslint/parser": "^8.56.1", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "engines": { @@ -24165,18 +24246,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "dev": true, "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "engines": { @@ -24191,13 +24272,13 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24208,9 +24289,9 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24224,14 +24305,14 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/type-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", - "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, @@ -24243,14 +24324,14 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "dev": true, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24261,17 +24342,17 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "dev": true, "dependencies": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" @@ -24288,13 +24369,13 @@ } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24314,9 +24395,9 @@ } }, "node_modules/typescript-eslint/node_modules/brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "dependencies": { "balanced-match": "^4.0.2" @@ -24343,12 +24424,12 @@ } }, "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" @@ -24364,15 +24445,15 @@ } }, "node_modules/typescript-eslint/node_modules/minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -24981,6 +25062,36 @@ "node": ">=0.10.0" } }, + "node_modules/vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "dev": true, + "dependencies": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "node_modules/vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true + }, "node_modules/w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", @@ -26134,16 +26245,16 @@ } }, "@angular-devkit/build-angular": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.4.tgz", - "integrity": "sha512-2HPCo6vEu5EIwxxFYhnmdfbktRBoOVQD3q7lG9PMQPf/jRCnyIZ70qSbXbAV96IMDLFl8mLRfY4scoaFMIYGMw==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-21.1.5.tgz", + "integrity": "sha512-B2jOBAiVl+hA3PLwpxfrbW/gA7SDu9Uv+hQwHYrdwL2XXDVwaQ+c3z9BS3yJDQTkb/TrAJ0sfa2zVLC4b/rHzg==", "dev": true, "requires": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.4", - "@angular-devkit/build-webpack": "0.2101.4", - "@angular-devkit/core": "21.1.4", - "@angular/build": "21.1.4", + "@angular-devkit/architect": "0.2101.5", + "@angular-devkit/build-webpack": "0.2101.5", + "@angular-devkit/core": "21.1.5", + "@angular/build": "21.1.5", "@babel/core": "7.28.5", "@babel/generator": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", @@ -26154,7 +26265,7 @@ "@babel/preset-env": "7.28.5", "@babel/runtime": "7.28.4", "@discoveryjs/json-ext": "0.6.3", - "@ngtools/webpack": "21.1.4", + "@ngtools/webpack": "21.1.5", "ansi-colors": "4.1.3", "autoprefixer": "10.4.23", "babel-loader": "10.0.0", @@ -26197,22 +26308,22 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dev": true, "requires": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dev": true, "requires": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -26226,6 +26337,18 @@ "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true }, + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, "ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -26241,6 +26364,19 @@ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true }, + "autoprefixer": { + "version": "10.4.23", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", + "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "dev": true, + "requires": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001760", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + } + }, "chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", @@ -26354,32 +26490,32 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.4.tgz", - "integrity": "sha512-lPjPxeEzUha4bnlGzD3KFFf3yxcQjOfV9wwZIa4XLsqjCZsUk95TzHQH7i64OCTw9uKTEQkJBAuO6v2WXHxopw==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.2101.5.tgz", + "integrity": "sha512-G3mvUXiSU3DL1QKngq/yXT94Wr+IdqtOM/1VC3NmsV9KX3OSfwfc560dmhY1efqc9gBA5qL+7kLlgV7Kx/Su3A==", "dev": true, "requires": { - "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/architect": "0.2101.5", "rxjs": "7.8.2" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dev": true, "requires": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dev": true, "requires": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -26387,6 +26523,18 @@ "source-map": "0.7.6" } }, + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, "ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -26572,21 +26720,21 @@ } }, "@angular/animations": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.5.tgz", - "integrity": "sha512-gsqHX8lCYV8cgVtHs0iLwrX8SVlmcjUF44l/xCc/jBC/TeKWRl2e6Jqrn1Wcd0NDlGiNsm+mYNyqMyy5/I7kjw==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-21.1.6.tgz", + "integrity": "sha512-Ft8B0tHBRyd7ORpbaa2S0yRqHSdsyMH6oot7ODAA3kv03k4GTwp74U6Y/NyV4JoZ+lr28+EV4YMv3mAqJwIy/g==", "requires": { "tslib": "^2.3.0" } }, "@angular/build": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.4.tgz", - "integrity": "sha512-7CAAQPWFMMqod40ox5MOVB/CnoBXFDehyQhs0hls6lu7bOy/M0EDy0v6bERkyNGRz1mihWWBiCV8XzEinrlq1A==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular/build/-/build-21.1.5.tgz", + "integrity": "sha512-v2eDinWKlSKuk5pyMMY8j5TMFW8HA9B1l13TrDDpxsRGAAzekg7TFNyuh1x9Y6Rq4Vn+8/8pCjMUPZigzWbMhQ==", "dev": true, "requires": { "@ampproject/remapping": "2.3.0", - "@angular-devkit/architect": "0.2101.4", + "@angular-devkit/architect": "0.2101.5", "@babel/core": "7.28.5", "@babel/helper-annotate-as-pure": "7.27.3", "@babel/helper-split-export-declaration": "7.24.7", @@ -26616,22 +26764,22 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "dev": true, "requires": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "dev": true, "requires": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -26639,6 +26787,18 @@ "source-map": "0.7.6" } }, + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, "ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -26679,9 +26839,9 @@ } }, "@angular/cdk": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.5.tgz", - "integrity": "sha512-AlQPgqe3LLwXCyrDwYSX3m/WKnl2ppCMW7Gb+7bJpIcpMdWYEpSOSQF318jXGYIysKg43YbdJ1tWhJWY/cbn3w==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-21.1.6.tgz", + "integrity": "sha512-a6isY8eWxfRO99KPs8GLMQRDE06diNbPmD9tcYi+EWxjPFOZ88QJmMN8a0yja+LDQaG2kRbNBLjYUEro3qCpTQ==", "requires": { "parse5": "^8.0.0", "tslib": "^2.3.0" @@ -26703,17 +26863,17 @@ } }, "@angular/cli": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.4.tgz", - "integrity": "sha512-XsMHgxTvHGiXXrhYZz3zMZYhYU0gHdpoHKGiEKXwcx+S1KoYbIssyg6oF2Kq49ZaE0OYCTKjnvgDce6ZqdkJ/A==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-21.1.5.tgz", + "integrity": "sha512-ljqvAzSk8FKMaYW/aZhR+SXjudbQViYYkMlJvJUClGpokjDM9KfJWPX+QZfr2J+piW5yaaHmFaIMddO9QxkUDQ==", "requires": { - "@angular-devkit/architect": "0.2101.4", - "@angular-devkit/core": "21.1.4", - "@angular-devkit/schematics": "21.1.4", + "@angular-devkit/architect": "0.2101.5", + "@angular-devkit/core": "21.1.5", + "@angular-devkit/schematics": "21.1.5", "@inquirer/prompts": "7.10.1", "@listr2/prompt-adapter-inquirer": "3.0.5", "@modelcontextprotocol/sdk": "1.26.0", - "@schematics/angular": "21.1.4", + "@schematics/angular": "21.1.5", "@yarnpkg/lockfile": "1.1.0", "algoliasearch": "5.46.2", "ini": "6.0.0", @@ -26729,20 +26889,20 @@ }, "dependencies": { "@angular-devkit/architect": { - "version": "0.2101.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.4.tgz", - "integrity": "sha512-3yyebORk+ovtO+LfDjIGbGCZhCMDAsyn9vkCljARj3sSshS4blOQBar0g+V3kYAweLT5Gf+rTKbN5jneOkBAFQ==", + "version": "0.2101.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.2101.5.tgz", + "integrity": "sha512-eTo6wWzUW5AyBBLTbaUTpBHhGbZhzteErtNGklWkhjicCr/soNH+2mVtvg8bqA8sNreYffK1VXKFsq5NyMh5qg==", "requires": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "rxjs": "7.8.2" } }, "@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "requires": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -26751,17 +26911,28 @@ } }, "@angular-devkit/schematics": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", - "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.5.tgz", + "integrity": "sha512-CGmoorQL5+mVCJEHwHWOrhSd1hFxB3h66i9wUDizJAEQUM3mSml5SiglHArpWY/G4GmFwi6XVe+Jm3U8J/mcFg==", "requires": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", "rxjs": "7.8.2" } }, + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, "ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -26903,25 +27074,25 @@ } }, "@angular/common": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.5.tgz", - "integrity": "sha512-olO2F0b+H8YBfsuQFEwo9Hjf+B714xGcttDW37+4jnY2IRS2uYeMu2RGIpY7ps+0uZ017c4iK3CCgSPBgmbTcA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-21.1.6.tgz", + "integrity": "sha512-qEfwyJhebl2tHwFhKHE/ZzsCLMWnQ0u/UDS23KUA4tTWaOKH8Usu7DS1B3KnUravII8R6ZpYn86L+B903v9WxA==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.5.tgz", - "integrity": "sha512-yRUdWlL+AWcTL4d7zD0jkNqsjvxXpWEihvOfD2gc65DO0+E80DsWIpHq9A8yWeLukbfLcmBGI2QbfW9+SXAlvg==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-21.1.6.tgz", + "integrity": "sha512-8RFdfbWTyx+OiRkiK2LsYPuhv8b65S3x/4+98kZRCrzP6YNjq/qsuqpfMUIKwnjok78FfhGyEx4I5LOV3Vkabw==", "requires": { "tslib": "^2.3.0" } }, "@angular/compiler-cli": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.5.tgz", - "integrity": "sha512-i2r2bQuWdjjFGTd2TA7FtCWNx5yJ3BMoyTGUC9lzSfmxWAfcH/NWR+6OdaEVwv6Zap3IXYYxs8S+REkx954EwA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-21.1.6.tgz", + "integrity": "sha512-0JU2cBDMSB4hU4KwDS2ThrkGh+Njf8Yfm11CKR0NWbHGwW1xHa7whlcpUzX/USqL+FNGXQ75R0fOcZrT86YvrA==", "requires": { "@babel/core": "7.28.5", "@jridgewell/sourcemap-codec": "^1.4.14", @@ -27020,56 +27191,56 @@ } }, "@angular/core": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.5.tgz", - "integrity": "sha512-m61YHiyE+SIvS8UXcFLjYCucv6ShJJCwz9xxEk7ysYW9wOtHdfIf9tgyOsucZDAvrvpSyQLRj5jGBCGm1VIvXA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-21.1.6.tgz", + "integrity": "sha512-c+n9Ynq1Ar+4SOaL10C/arqBje0dUFFUaDyErXp3jPXU/L29fsFTlmKM2EWunM1RhJckYonJ/xtH0gwwrH6W9Q==", "requires": { "tslib": "^2.3.0" } }, "@angular/elements": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.5.tgz", - "integrity": "sha512-SoXNNLEBn9w3NP5vZJiMQfdiJ1FLY0VuDP5VvscPVqebh/nfpDw7jOu8Pb2S9wTANsfmlFX4QK1ehl3nJVY/Fw==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-21.1.6.tgz", + "integrity": "sha512-LvF0kgh1XxLlXyw5CS5C787HYFAIGVHAg9HNgGZKINeu16Og7KCnsVszAyWiVn1t3trcLARcScYgrOcuwBYzIg==", "requires": { "tslib": "^2.3.0" } }, "@angular/forms": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.5.tgz", - "integrity": "sha512-Z8Vcgz5KYlCobRxLjyGGUBv0mA4nusuiD36GqYRn3sR780TLDcPFVwTCwVEWLdwID64oiHXG+x9jjU/Z3HzR6A==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-21.1.6.tgz", + "integrity": "sha512-Bw3nVDWihGUGyys7oq2zdJ2MjvJvU1x1WaExYmp3rKU3S7rQXGq6IxY8bopTtHirTANrY2KUEnJ2IlK+xVg9OA==", "requires": { "@standard-schema/spec": "^1.0.0", "tslib": "^2.3.0" } }, "@angular/language-service": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.5.tgz", - "integrity": "sha512-/MiXx/peMBjMHEX6Gm7C6ZHOlcgEzkWeBlI7WWnIEDU9vU6F3nU4kauBYdLr/EpXhHpdpOtL4eknPhAfdvVrJA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/language-service/-/language-service-21.1.6.tgz", + "integrity": "sha512-I2gYI9cKP/B/rUz8WVaWNpuULSdq4W+ZUm6YQyUXdGEh3dDEssK1xLucPBfUT7RAr6h7+w5/RS+zKWC1q9g24w==", "dev": true }, "@angular/platform-browser": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.5.tgz", - "integrity": "sha512-rAN0cu05Pg7HHe9JMRd3g5JyyVCeFW8QiB/jG6klUrOTF4QzyCbmwlm7MX0uTx3CWAZraWCGbdahUkLyYtuqFA==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-21.1.6.tgz", + "integrity": "sha512-im6aNcgYdIYIVW2262ATkC39WUmhc+KVNVKwKtO5jlOsq9TWmxT1/esncEAlokMe5os6eeb/Ga4D6Ghj0gj4Ig==", "requires": { "tslib": "^2.3.0" } }, "@angular/platform-browser-dynamic": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.5.tgz", - "integrity": "sha512-Pd8nPbJSIONnze1WS9wLBAtaFw4TYIH+ZGjKHS9G1E9l09tDWtHWyB7dY82Sc//Nc8iR4V7dcsbUmFjOJHThww==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-21.1.6.tgz", + "integrity": "sha512-lVtHkhK/jnrGdX+4S8ItfMO+5buHAU9NMHeDq+QqalnXznMaC7Qd4BPLcRWW4QAI177zG0NE1Bet5cjO75N9+w==", "requires": { "tslib": "^2.3.0" } }, "@angular/router": { - "version": "21.1.5", - "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.5.tgz", - "integrity": "sha512-OjFn6Nw51CU712CMbl2U9TxlCkzOmjMLYPAfnV4+RdG7o+/eOS2nV0oapJ88RNCw7Yl04PA1amc3ql3agDFd4A==", + "version": "21.1.6", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-21.1.6.tgz", + "integrity": "sha512-JJn0gfeRks2czbeLmtxxjIlmKtOmjYi3yAaoAwiwpbfVHPLJeJ32axLJREAU0dBkThF8YD+r18uEJ9UrvkqrtA==", "requires": { "tslib": "^2.3.0" } @@ -28577,9 +28748,9 @@ } }, "@eslint/css-tree": { - "version": "3.6.8", - "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.8.tgz", - "integrity": "sha512-s0f40zY7dlMp8i0Jf0u6l/aSswS0WRAgkhgETgiCJRcxIWb4S/Sp9uScKHWbkM3BnoFLbJbmOYk5AZUDFVxaLA==", + "version": "3.6.9", + "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.9.tgz", + "integrity": "sha512-3D5/OHibNEGk+wKwNwMbz63NMf367EoR4mVNNpxddCHKEb2Nez7z62J2U6YjtErSsZDoY0CsccmoUpdEbkogNA==", "dev": true, "requires": { "mdn-data": "2.23.0", @@ -28914,9 +29085,9 @@ } }, "@hono/node-server": { - "version": "1.19.9", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.9.tgz", - "integrity": "sha512-vHL6w3ecZsky+8P5MD+eFfaGTyCeOHUIFYMGpQGbrBTSmNNoxv0if69rEZ5giu36weC5saFuznL411gRX7bJDw==" + "version": "1.19.10", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.10.tgz", + "integrity": "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==" }, "@hotwired/stimulus": { "version": "3.2.2", @@ -28937,55 +29108,69 @@ "@rails/actioncable": ">=7.0" } }, + "@html-eslint/core": { + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/core/-/core-0.57.0.tgz", + "integrity": "sha512-X/cKrOmXrxZSdgyKwtbaCuuJ1k/u82MK58Q6p1TzfwPatwIYx+icfBv1Vp1dLui0L0y1fwBW4H+TKhBf7mMKmg==", + "dev": true, + "requires": { + "@html-eslint/types": "^0.57.0", + "eslint": "^9.39.1", + "html-standard": "^0.0.13" + } + }, "@html-eslint/eslint-plugin": { - "version": "0.54.2", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.54.2.tgz", - "integrity": "sha512-C6jhJqVGTS9AW3Z84Ni/Cs6h3XcRHUXi1YkRaAYI08MeNj6ZWIXhwKBEJgEGK2YxzOcM1TpZEvHL4d5z7aC7Eg==", + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.57.1.tgz", + "integrity": "sha512-IDfdk3V27eebNpdXD2NLy/lnTSbUuKrro/6YJICBn/9aiXPXagNqWJB38qcSWEoxADbXfSSn17DJWcXvQTkHBg==", "dev": true, "requires": { "@eslint/plugin-kit": "^0.4.1", - "@html-eslint/parser": "^0.54.0", - "@html-eslint/template-parser": "^0.54.0", - "@html-eslint/template-syntax-parser": "^0.54.0", - "@html-eslint/types": "^0.54.0" + "@html-eslint/core": "^0.57.0", + "@html-eslint/parser": "^0.57.1", + "@html-eslint/template-parser": "^0.57.0", + "@html-eslint/template-syntax-parser": "^0.57.0", + "@html-eslint/types": "^0.57.0", + "@rviscomi/capo.js": "^2.1.0", + "html-standard": "^0.0.13" } }, "@html-eslint/parser": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.54.0.tgz", - "integrity": "sha512-ia3I/6jf87679pUrhIDKSgddKbw9GLkvO86fYt7tJQHHRQu+zySErIxuI2b941oeovzOq10dTpC5Hp41qbvPgg==", + "version": "0.57.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.57.1.tgz", + "integrity": "sha512-nQ5vw7Os+Snjxq9hLLBak2bv502Obn77BNOWfGK2+GIrShxtGd8w1ehlKW3EB5/RQzqBk6VDK8nPfexlR3M7kg==", "dev": true, "requires": { - "@eslint/css-tree": "^3.6.8", - "@html-eslint/template-syntax-parser": "^0.54.0", - "@html-eslint/types": "^0.54.0", + "@eslint/css-tree": "^3.6.9", + "@html-eslint/template-syntax-parser": "^0.57.0", + "@html-eslint/types": "^0.57.0", "css-tree": "^3.1.0", "es-html-parser": "0.3.1" } }, "@html-eslint/template-parser": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.54.0.tgz", - "integrity": "sha512-gSjgmGwRQehNxZ3XdRUhUoXDFzYc/LYoKA7JwExjdvklGnSh5WkH/CLOlphkDh9jJsC1O/E0I04bVGrzy3idKQ==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.57.0.tgz", + "integrity": "sha512-tddyBo4dEl4W4Ehxuyd6H4jsSqvsfL5F7Bj9/aFfdQyv36q7BGWM2BRHb6FMmYKAPGZ3VzyEbUlcqIwXpDkY3w==", "dev": true, "requires": { - "@html-eslint/types": "^0.54.0", + "@html-eslint/types": "^0.57.0", "es-html-parser": "0.3.1" } }, "@html-eslint/template-syntax-parser": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.54.0.tgz", - "integrity": "sha512-7mDM4AWqz42FHwnOt8Lu5xqovaZHlEuBrmwNrMg6VwC9TPaLVyh4j3zNzNnM6tjftaXZzverJup39zGB8mvXjg==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.57.0.tgz", + "integrity": "sha512-vHp5y4TR+HhgMDi3rAkgm90LBptSZaQUJudZSj+WdvnSBjLe/fgJC4aVjtLVHTS9ynORrFio8AmH1Bz20kYk4g==", "dev": true, "requires": { - "@html-eslint/types": "^0.54.0" + "@html-eslint/types": "^0.57.0" } }, "@html-eslint/types": { - "version": "0.54.0", - "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.54.0.tgz", - "integrity": "sha512-bfJolxay0POMYaFWTCH1MBitEaxIEKZOoROGOLZiRBaPvQrzhwYQktuyt5X1PcHqUB4HwEtYgSdpjYGT4JbrvA==", + "version": "0.57.0", + "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.57.0.tgz", + "integrity": "sha512-wZAHc9FHZRVAcKyx1NdMNGpw1Jo/Anh+9y+bTQ/cKjh5MHJlbs8ogthIG8efBVFIVlIgzxEA8yrX+DPXmuWisA==", "dev": true, "requires": { "@types/css-tree": "^2.3.11", @@ -30041,9 +30226,9 @@ } }, "@ngtools/webpack": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.4.tgz", - "integrity": "sha512-CgKnMofIVGTwNPqFNZmkmr2aLOFUG/AKm8lauXU+juwSaY7Z28eguFd+J42uVUOnasLxINQY9y7kr9f6deTrcg==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-21.1.5.tgz", + "integrity": "sha512-5nG9v/nEzsaKxgw5NurM6tPKPw0OYsCM3DL4ZI8+TidT55hYbsroTnyBcHBouJ1qlZlQXNtlsjsjBmBDtF7JZA==", "dev": true }, "@npmcli/agent": { @@ -30554,177 +30739,177 @@ "dev": true }, "@rollup/rollup-android-arm-eabi": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.1.tgz", - "integrity": "sha512-9R0DM/ykwfGIlNu6+2U09ga0WXeZ9MRC2Ter8jnz8415VbuIykVuc6bhdrbORFZANDmTDvq26mJrEVTl8TdnDg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz", + "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==", "dev": true, "optional": true }, "@rollup/rollup-android-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.1.tgz", - "integrity": "sha512-eFZCb1YUqhTysgW3sj/55du5cG57S7UTNtdMjCW7LwVcj3dTTcowCsC8p7uBdzKsZYa8J7IDE8lhMI+HX1vQvg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz", + "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==", "dev": true, "optional": true }, "@rollup/rollup-darwin-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.1.tgz", - "integrity": "sha512-p3grE2PHcQm2e8PSGZdzIhCKbMCw/xi9XvMPErPhwO17vxtvCN5FEA2mSLgmKlCjHGMQTP6phuQTYWUnKewwGg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz", + "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==", "dev": true, "optional": true }, "@rollup/rollup-darwin-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.1.tgz", - "integrity": "sha512-rDUjG25C9qoTm+e02Esi+aqTKSBYwVTaoS1wxcN47/Luqef57Vgp96xNANwt5npq9GDxsH7kXxNkJVEsWEOEaQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz", + "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.1.tgz", - "integrity": "sha512-+JiU7Jbp5cdxekIgdte0jfcu5oqw4GCKr6i3PJTlXTCU5H5Fvtkpbs4XJHRmWNXF+hKmn4v7ogI5OQPaupJgOg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz", + "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==", "dev": true, "optional": true }, "@rollup/rollup-freebsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.1.tgz", - "integrity": "sha512-V5xC1tOVWtLLmr3YUk2f6EJK4qksksOYiz/TCsFHu/R+woubcLWdC9nZQmwjOAbmExBIVKsm1/wKmEy4z4u4Bw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz", + "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.1.tgz", - "integrity": "sha512-Rn3n+FUk2J5VWx+ywrG/HGPTD9jXNbicRtTM11e/uorplArnXZYsVifnPPqNNP5BsO3roI4n8332ukpY/zN7rQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz", + "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm-musleabihf": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.1.tgz", - "integrity": "sha512-grPNWydeKtc1aEdrJDWk4opD7nFtQbMmV7769hiAaYyUKCT1faPRm2av8CX1YJsZ4TLAZcg9gTR1KvEzoLjXkg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz", + "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.1.tgz", - "integrity": "sha512-a59mwd1k6x8tXKcUxSyISiquLwB5pX+fJW9TkWU46lCqD/GRDe9uDN31jrMmVP3feI3mhAdvcCClhV8V5MhJFQ==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz", + "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==", "dev": true, "optional": true }, "@rollup/rollup-linux-arm64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.1.tgz", - "integrity": "sha512-puS1MEgWX5GsHSoiAsF0TYrpomdvkaXm0CofIMG5uVkP6IBV+ZO9xhC5YEN49nsgYo1DuuMquF9+7EDBVYu4uA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz", + "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==", "dev": true, "optional": true }, "@rollup/rollup-linux-loong64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.1.tgz", - "integrity": "sha512-r3Wv40in+lTsULSb6nnoudVbARdOwb2u5fpeoOAZjFLznp6tDU8kd+GTHmJoqZ9lt6/Sys33KdIHUaQihFcu7g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz", + "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==", "dev": true, "optional": true }, "@rollup/rollup-linux-loong64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.1.tgz", - "integrity": "sha512-MR8c0+UxAlB22Fq4R+aQSPBayvYa3+9DrwG/i1TKQXFYEaoW3B5b/rkSRIypcZDdWjWnpcvxbNaAJDcSbJU3Lw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz", + "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==", "dev": true, "optional": true }, "@rollup/rollup-linux-ppc64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.1.tgz", - "integrity": "sha512-3KhoECe1BRlSYpMTeVrD4sh2Pw2xgt4jzNSZIIPLFEsnQn9gAnZagW9+VqDqAHgm1Xc77LzJOo2LdigS5qZ+gw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz", + "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==", "dev": true, "optional": true }, "@rollup/rollup-linux-ppc64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.1.tgz", - "integrity": "sha512-ziR1OuZx0vdYZZ30vueNZTg73alF59DicYrPViG0NEgDVN8/Jl87zkAPu4u6VjZST2llgEUjaiNl9JM6HH1Vdw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz", + "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.1.tgz", - "integrity": "sha512-uW0Y12ih2XJRERZ4jAfKamTyIHVMPQnTZcQjme2HMVDAHY4amf5u414OqNYC+x+LzRdRcnIG1YodLrrtA8xsxw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz", + "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==", "dev": true, "optional": true }, "@rollup/rollup-linux-riscv64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.1.tgz", - "integrity": "sha512-u9yZ0jUkOED1BFrqu3BwMQoixvGHGZ+JhJNkNKY/hyoEgOwlqKb62qu+7UjbPSHYjiVy8kKJHvXKv5coH4wDeg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz", + "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==", "dev": true, "optional": true }, "@rollup/rollup-linux-s390x-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.1.tgz", - "integrity": "sha512-/0PenBCmqM4ZUd0190j7J0UsQ/1nsi735iPRakO8iPciE7BQ495Y6msPzaOmvx0/pn+eJVVlZrNrSh4WSYLxNg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz", + "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.1.tgz", - "integrity": "sha512-a8G4wiQxQG2BAvo+gU6XrReRRqj+pLS2NGXKm8io19goR+K8lw269eTrPkSdDTALwMmJp4th2Uh0D8J9bEV1vg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz", + "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==", "dev": true, "optional": true }, "@rollup/rollup-linux-x64-musl": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.1.tgz", - "integrity": "sha512-bD+zjpFrMpP/hqkfEcnjXWHMw5BIghGisOKPj+2NaNDuVT+8Ds4mPf3XcPHuat1tz89WRL+1wbcxKY3WSbiT7w==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz", + "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==", "dev": true, "optional": true }, "@rollup/rollup-openbsd-x64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.1.tgz", - "integrity": "sha512-eLXw0dOiqE4QmvikfQ6yjgkg/xDM+MdU9YJuP4ySTibXU0oAvnEWXt7UDJmD4UkYialMfOGFPJnIHSe/kdzPxg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz", + "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==", "dev": true, "optional": true }, "@rollup/rollup-openharmony-arm64": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.1.tgz", - "integrity": "sha512-xzm44KgEP11te3S2HCSyYf5zIzWmx3n8HDCc7EE59+lTcswEWNpvMLfd9uJvVX8LCg9QWG67Xt75AuHn4vgsXw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz", + "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==", "dev": true, "optional": true }, "@rollup/rollup-win32-arm64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.1.tgz", - "integrity": "sha512-yR6Bl3tMC/gBok5cz/Qi0xYnVbIxGx5Fcf/ca0eB6/6JwOY+SRUcJfI0OpeTpPls7f194as62thCt/2BjxYN8g==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz", + "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==", "dev": true, "optional": true }, "@rollup/rollup-win32-ia32-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.1.tgz", - "integrity": "sha512-3fZBidchE0eY0oFZBnekYCfg+5wAB0mbpCBuofh5mZuzIU/4jIVkbESmd2dOsFNS78b53CYv3OAtwqkZZmU5nA==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz", + "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-gnu": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.1.tgz", - "integrity": "sha512-xGGY5pXj69IxKb4yv/POoocPy/qmEGhimy/FoTpTSVju3FYXUQQMFCaZZXJVidsmGxRioZAwpThl/4zX41gRKg==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz", + "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==", "dev": true, "optional": true }, "@rollup/rollup-win32-x64-msvc": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.1.tgz", - "integrity": "sha512-SPEpaL6DX4rmcXtnhdrQYgzQ5W2uW3SCJch88lB2zImhJRhIIK44fkUrgIV/Q8yUNfw5oyZ5vkeQsZLhCb06lw==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz", + "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==", "dev": true, "optional": true }, @@ -30734,22 +30919,28 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "@rviscomi/capo.js": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@rviscomi/capo.js/-/capo.js-2.1.0.tgz", + "integrity": "sha512-y6J+KJqsrY8AcDswLKkvd8KdpFindjS4Q9rSuK8CIpsQOepEjgRaMR4S8OtuLOQoVYLCROT3ffMQqRWrUMQdQA==", + "dev": true + }, "@schematics/angular": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.4.tgz", - "integrity": "sha512-I1zdSNzdbrVCWpeE2NsZQmIoa9m0nlw4INgdGIkqUH6FgwvoGKC0RoOxKAmm6HHVJ48FE/sPI13dwAeK89ow5A==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-21.1.5.tgz", + "integrity": "sha512-AndJ17ePYUoqJqiIF9VaXbGAFfOqDcHuAxhwozsQlWDzwgQSOUC/WWeG9hKVCgMD6tE02Sxr2ova9DiBKsLQNg==", "requires": { - "@angular-devkit/core": "21.1.4", - "@angular-devkit/schematics": "21.1.4", + "@angular-devkit/core": "21.1.5", + "@angular-devkit/schematics": "21.1.5", "jsonc-parser": "3.3.1" }, "dependencies": { "@angular-devkit/core": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.4.tgz", - "integrity": "sha512-ObPTI5gYCB1jGxTRhcqZ6oQVUBFVJ8GH4LksVuAiz0nFX7xxpzARWvlhq943EtnlovVlUd9I8fM3RQqjfGVVAQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-21.1.5.tgz", + "integrity": "sha512-KUKbllHvHefkAbTBjWNpRPyrpBqecW+6HBBAR+XNbKBuFTHkG+gxtuwMXNsvO5KECKwQphvQt5h3g05Xtaf0LQ==", "requires": { - "ajv": "8.17.1", + "ajv": "8.18.0", "ajv-formats": "3.0.1", "jsonc-parser": "3.3.1", "picomatch": "4.0.3", @@ -30758,17 +30949,28 @@ } }, "@angular-devkit/schematics": { - "version": "21.1.4", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.4.tgz", - "integrity": "sha512-Nqq0ioCUxrbEX+L4KOarETcZZJNnJ1mAJ0ubO4VM91qnn8RBBM9SnQ91590TfC34Szk/wh+3+Uj6KUvTJNuegQ==", + "version": "21.1.5", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-21.1.5.tgz", + "integrity": "sha512-CGmoorQL5+mVCJEHwHWOrhSd1hFxB3h66i9wUDizJAEQUM3mSml5SiglHArpWY/G4GmFwi6XVe+Jm3U8J/mcFg==", "requires": { - "@angular-devkit/core": "21.1.4", + "@angular-devkit/core": "21.1.5", "jsonc-parser": "3.3.1", "magic-string": "0.30.21", "ora": "9.0.0", "rxjs": "7.8.2" } }, + "ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "requires": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + } + }, "ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", @@ -30840,11 +31042,11 @@ } }, "strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "requires": { - "ansi-regex": "^6.0.1" + "ansi-regex": "^6.2.2" } } } @@ -31050,9 +31252,9 @@ "integrity": "sha512-Woh/nk1/7c31D14dIaU5i9d4NMK06TBeEA+uBidhZp+JAXVGMeQGm0K2iJxBXNvNrc6aKGlZ57W1o3CPUWOlWQ==" }, "@tiptap/extensions": { - "version": "3.15.3", - "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.15.3.tgz", - "integrity": "sha512-ycx/BgxR4rc9tf3ZyTdI98Z19yKLFfqM3UN+v42ChuIwkzyr9zyp7kG8dB9xN2lNqrD+5y/HyJobz/VJ7T90gA==" + "version": "3.20.0", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.20.0.tgz", + "integrity": "sha512-HIsXX942w3nbxEQBlMAAR/aa6qiMBEP7CsSMxaxmTIVAmW35p6yUASw6GdV1u0o3lCZjXq2OSRMTskzIqi5uLg==" }, "@tiptap/pm": { "version": "3.12.0", @@ -31143,9 +31345,9 @@ } }, "minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "requires": { "brace-expansion": "^5.0.2" } @@ -31475,9 +31677,9 @@ "dev": true }, "@types/react": { - "version": "19.2.10", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", - "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", "dev": true, "requires": { "csstype": "^3.2.2" @@ -31699,19 +31901,13 @@ "eslint-visitor-keys": "^4.2.1" } }, - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, "brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { - "balanced-match": "^4.0.2" + "balanced-match": "^1.0.0" } }, "debug": { @@ -31736,12 +31932,12 @@ "dev": true }, "minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "requires": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" } } } @@ -31893,19 +32089,13 @@ "eslint-visitor-keys": "^4.2.1" } }, - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, "brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { - "balanced-match": "^4.0.2" + "balanced-match": "^1.0.0" } }, "debug": { @@ -31924,12 +32114,12 @@ "dev": true }, "minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "requires": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" } } } @@ -31957,19 +32147,13 @@ "ts-api-utils": "^2.4.0" }, "dependencies": { - "balanced-match": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", - "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", - "dev": true - }, "brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { - "balanced-match": "^4.0.2" + "balanced-match": "^1.0.0" } }, "debug": { @@ -31982,86 +32166,86 @@ } }, "minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "requires": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^2.0.2" } } } }, "@typescript-eslint/utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.54.0.tgz", - "integrity": "sha512-9Cnda8GS57AQakvRyG0PTejJNlA2xhvyNtEVIMlDWOOeEyBkYWhGPnfrIAnqxLMTSTo6q8g12XVjjev5l1NvMA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", + "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", "dev": true, "requires": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0" + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1" }, "dependencies": { "@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "dev": true, "requires": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" } }, "@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "dev": true, "requires": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "dev": true }, "@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "dev": true, "requires": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "dev": true, "requires": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" } }, "balanced-match": { @@ -32071,9 +32255,9 @@ "dev": true }, "brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "requires": { "balanced-match": "^4.0.2" @@ -32089,15 +32273,15 @@ } }, "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true }, "minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "requires": { "brace-expansion": "^5.0.2" @@ -32152,6 +32336,12 @@ "integrity": "sha512-dOxxrhgyDIEUADhb/8OlV9JIqYLgos03YorAueTIeOUskLJSEsfwCByjbu98ctXitUN3znXKp0bYD/WHSudCeA==", "dev": true }, + "@vscode/l10n": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.18.tgz", + "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==", + "dev": true + }, "@w11k/ngx-componentdestroyed": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@w11k/ngx-componentdestroyed/-/ngx-componentdestroyed-5.0.2.tgz", @@ -32877,12 +33067,12 @@ "integrity": "sha512-VVE1H6cc4ai+ZXo/CRWoJiHXrA1qfA31DPnx6D20+kSI547hQN5Greh51LQ1baMRMfxO5K5M4ImMtZbZt2DODQ==" }, "autoprefixer": { - "version": "10.4.23", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.23.tgz", - "integrity": "sha512-YYTXSFulfwytnjAPlw8QHncHJmlvFKtczb8InXaAx9Q0LbfDnfEYDE55omerIJKihhmU61Ft+cAOSzQVaBUmeA==", + "version": "10.4.27", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz", + "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==", "requires": { "browserslist": "^4.28.1", - "caniuse-lite": "^1.0.30001760", + "caniuse-lite": "^1.0.30001774", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" @@ -33252,9 +33442,9 @@ "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==" }, "caniuse-lite": { - "version": "1.0.30001764", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001764.tgz", - "integrity": "sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==" + "version": "1.0.30001776", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001776.tgz", + "integrity": "sha512-sg01JDPzZ9jGshqKSckOQthXnYwOEP50jeVFhaSFbZcOy05TiuuaffDOfcwtCisJ9kNQuLBFibYywv2Bgm9osw==" }, "ccount": { "version": "2.0.1", @@ -35109,18 +35299,11 @@ } }, "express-rate-limit": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", - "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.1.tgz", + "integrity": "sha512-D1dKN+cmyPWuvB+G2SREQDzPY1agpBIcTa9sJxOPMCNeH3gwzhqJRDWCXW3gg0y//+LQ/8j52JbMROWyrKdMdw==", "requires": { - "ip-address": "10.0.1" - }, - "dependencies": { - "ip-address": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", - "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==" - } + "ip-address": "10.1.0" } }, "ext": { @@ -35205,9 +35388,9 @@ } }, "minimatch": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.7.tgz", - "integrity": "sha512-FjiwU9HaHW6YB3H4a1sFudnv93lvydNjz2lmyUXR6IwKhGI+bgL3SOZrBGn6kvvX2pJvhEkGSGjyTHN47O4rqA==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "requires": { "brace-expansion": "^2.0.1" @@ -35480,9 +35663,9 @@ "integrity": "sha512-r8LA6i4LP4EeWOhqBaZZjDWwehd1xUJPCJd9Sv300H0ZmcUER4+JPh7bqqZeqs1o5pgtgvXm+d9UGrB5zZGDiQ==" }, "minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "requires": { "brace-expansion": "^5.0.2" } @@ -35824,9 +36007,9 @@ } }, "hono": { - "version": "4.12.0", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.0.tgz", - "integrity": "sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==" + "version": "4.12.7", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", + "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==" }, "hosted-git-info": { "version": "9.0.2", @@ -35899,6 +36082,16 @@ } } }, + "html-standard": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/html-standard/-/html-standard-0.0.13.tgz", + "integrity": "sha512-6oNfW3c1t44O7jVXu0tp4E5MbHifWlXrHlZBPt6y7vFdgLOUUh8hyzoRhfUgozlBUK6oLLYhqP1uIqbZ8ggcBA==", + "dev": true, + "requires": { + "vscode-css-languageservice": "^6.3.9", + "vscode-languageserver-textdocument": "^1.0.12" + } + }, "html-void-elements": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", @@ -36121,9 +36314,9 @@ } }, "minimatch": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.2.tgz", - "integrity": "sha512-+G4CpNBxa5MprY+04MbgOw1v7So6n5JY166pFi9KfYwT78fxScCeSNQSNzp6dpPSW2rONOps6Ocam1wFhCgoVw==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "requires": { "brace-expansion": "^5.0.2" } @@ -36138,9 +36331,9 @@ "optional": true }, "immutable": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz", - "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==", + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.5.tgz", + "integrity": "sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==", "dev": true }, "import-fresh": { @@ -38241,9 +38434,9 @@ "dev": true }, "minimatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.3.tgz", - "integrity": "sha512-M2GCs7Vk83NxkUyQV1bkABc4yxgz9kILhHImZiBPAZ9ybuvCb0/H7lEl5XvIg3g+9d4eNotkZA5IWwYl0tibaA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "requires": { "brace-expansion": "^1.1.7" @@ -40115,36 +40308,36 @@ } }, "rollup": { - "version": "4.55.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.1.tgz", - "integrity": "sha512-wDv/Ht1BNHB4upNbK74s9usvl7hObDnvVzknxqY/E/O3X6rW1U1rV1aENEfJ54eFZDTNo7zv1f5N4edCluH7+A==", + "version": "4.59.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz", + "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==", "dev": true, "requires": { - "@rollup/rollup-android-arm-eabi": "4.55.1", - "@rollup/rollup-android-arm64": "4.55.1", - "@rollup/rollup-darwin-arm64": "4.55.1", - "@rollup/rollup-darwin-x64": "4.55.1", - "@rollup/rollup-freebsd-arm64": "4.55.1", - "@rollup/rollup-freebsd-x64": "4.55.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.55.1", - "@rollup/rollup-linux-arm-musleabihf": "4.55.1", - "@rollup/rollup-linux-arm64-gnu": "4.55.1", - "@rollup/rollup-linux-arm64-musl": "4.55.1", - "@rollup/rollup-linux-loong64-gnu": "4.55.1", - "@rollup/rollup-linux-loong64-musl": "4.55.1", - "@rollup/rollup-linux-ppc64-gnu": "4.55.1", - "@rollup/rollup-linux-ppc64-musl": "4.55.1", - "@rollup/rollup-linux-riscv64-gnu": "4.55.1", - "@rollup/rollup-linux-riscv64-musl": "4.55.1", - "@rollup/rollup-linux-s390x-gnu": "4.55.1", - "@rollup/rollup-linux-x64-gnu": "4.55.1", - "@rollup/rollup-linux-x64-musl": "4.55.1", - "@rollup/rollup-openbsd-x64": "4.55.1", - "@rollup/rollup-openharmony-arm64": "4.55.1", - "@rollup/rollup-win32-arm64-msvc": "4.55.1", - "@rollup/rollup-win32-ia32-msvc": "4.55.1", - "@rollup/rollup-win32-x64-gnu": "4.55.1", - "@rollup/rollup-win32-x64-msvc": "4.55.1", + "@rollup/rollup-android-arm-eabi": "4.59.0", + "@rollup/rollup-android-arm64": "4.59.0", + "@rollup/rollup-darwin-arm64": "4.59.0", + "@rollup/rollup-darwin-x64": "4.59.0", + "@rollup/rollup-freebsd-arm64": "4.59.0", + "@rollup/rollup-freebsd-x64": "4.59.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", + "@rollup/rollup-linux-arm-musleabihf": "4.59.0", + "@rollup/rollup-linux-arm64-gnu": "4.59.0", + "@rollup/rollup-linux-arm64-musl": "4.59.0", + "@rollup/rollup-linux-loong64-gnu": "4.59.0", + "@rollup/rollup-linux-loong64-musl": "4.59.0", + "@rollup/rollup-linux-ppc64-gnu": "4.59.0", + "@rollup/rollup-linux-ppc64-musl": "4.59.0", + "@rollup/rollup-linux-riscv64-gnu": "4.59.0", + "@rollup/rollup-linux-riscv64-musl": "4.59.0", + "@rollup/rollup-linux-s390x-gnu": "4.59.0", + "@rollup/rollup-linux-x64-gnu": "4.59.0", + "@rollup/rollup-linux-x64-musl": "4.59.0", + "@rollup/rollup-openbsd-x64": "4.59.0", + "@rollup/rollup-openharmony-arm64": "4.59.0", + "@rollup/rollup-win32-arm64-msvc": "4.59.0", + "@rollup/rollup-win32-ia32-msvc": "4.59.0", + "@rollup/rollup-win32-x64-gnu": "4.59.0", + "@rollup/rollup-win32-x64-msvc": "4.59.0", "@types/estree": "1.0.8", "fsevents": "~2.3.2" } @@ -41195,9 +41388,9 @@ "dev": true }, "tar": { - "version": "7.5.7", - "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.7.tgz", - "integrity": "sha512-fov56fJiRuThVFXD6o6/Q354S7pnWMJIVlDBYijsTNx6jKSE4pvrDTs6lUnmGvNyfJwFQQwWy3owKz1ucIhveQ==", + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", + "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", "requires": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", @@ -41573,117 +41766,117 @@ "dev": true }, "typescript-eslint": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.54.0.tgz", - "integrity": "sha512-CKsJ+g53QpsNPqbzUsfKVgd3Lny4yKZ1pP4qN3jdMOg/sisIDLGyDMezycquXLE5JsEU0wp3dGNdzig0/fmSVQ==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.56.1.tgz", + "integrity": "sha512-U4lM6pjmBX7J5wk4szltF7I1cGBHXZopnAXCMXb3+fZ3B/0Z3hq3wS/CCUB2NZBNAExK92mCU2tEohWuwVMsDQ==", "dev": true, "requires": { - "@typescript-eslint/eslint-plugin": "8.54.0", - "@typescript-eslint/parser": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0" + "@typescript-eslint/eslint-plugin": "8.56.1", + "@typescript-eslint/parser": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1" }, "dependencies": { "@typescript-eslint/eslint-plugin": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.54.0.tgz", - "integrity": "sha512-hAAP5io/7csFStuOmR782YmTthKBJ9ND3WVL60hcOjvtGFb+HJxH4O5huAcmcZ9v9G8P+JETiZ/G1B8MALnWZQ==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.56.1.tgz", + "integrity": "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A==", "dev": true, "requires": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/type-utils": "8.54.0", - "@typescript-eslint/utils": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/type-utils": "8.56.1", + "@typescript-eslint/utils": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/parser": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.54.0.tgz", - "integrity": "sha512-BtE0k6cjwjLZoZixN0t5AKP0kSzlGu7FctRXYuPAm//aaiZhmfq1JwdYpYr1brzEspYyFeF+8XF5j2VK6oalrA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.56.1.tgz", + "integrity": "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/scope-manager": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" } }, "@typescript-eslint/project-service": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.54.0.tgz", - "integrity": "sha512-YPf+rvJ1s7MyiWM4uTRhE4DvBXrEV+d8oC3P9Y2eT7S+HBS0clybdMIPnhiATi9vZOYDc7OQ1L/i6ga6NFYK/g==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", + "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "dev": true, "requires": { - "@typescript-eslint/tsconfig-utils": "^8.54.0", - "@typescript-eslint/types": "^8.54.0", + "@typescript-eslint/tsconfig-utils": "^8.56.1", + "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" } }, "@typescript-eslint/scope-manager": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.54.0.tgz", - "integrity": "sha512-27rYVQku26j/PbHYcVfRPonmOlVI6gihHtXFbTdB5sb6qA0wdAQAbyXFVarQ5t4HRojIz64IV90YtsjQSSGlQg==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", + "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "dev": true, "requires": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0" + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1" } }, "@typescript-eslint/tsconfig-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.54.0.tgz", - "integrity": "sha512-dRgOyT2hPk/JwxNMZDsIXDgyl9axdJI3ogZ2XWhBPsnZUv+hPesa5iuhdYt2gzwA9t8RE5ytOJ6xB0moV0Ujvw==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", + "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "dev": true }, "@typescript-eslint/type-utils": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.54.0.tgz", - "integrity": "sha512-hiLguxJWHjjwL6xMBwD903ciAwd7DmK30Y9Axs/etOkftC3ZNN9K44IuRD/EB08amu+Zw6W37x9RecLkOo3pMA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.56.1.tgz", + "integrity": "sha512-yB/7dxi7MgTtGhZdaHCemf7PuwrHMenHjmzgUW1aJpO+bBU43OycnM3Wn+DdvDO/8zzA9HlhaJ0AUGuvri4oGg==", "dev": true, "requires": { - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/typescript-estree": "8.54.0", - "@typescript-eslint/utils": "8.54.0", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/typescript-estree": "8.56.1", + "@typescript-eslint/utils": "8.56.1", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/types": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.54.0.tgz", - "integrity": "sha512-PDUI9R1BVjqu7AUDsRBbKMtwmjWcn4J3le+5LpcFgWULN3LvHC5rkc9gCVxbrsrGmO1jfPybN5s6h4Jy+OnkAA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", + "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.54.0.tgz", - "integrity": "sha512-BUwcskRaPvTk6fzVWgDPdUndLjB87KYDrN5EYGetnktoeAvPtO4ONHlAZDnj5VFnUANg0Sjm7j4usBlnoVMHwA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", + "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "dev": true, "requires": { - "@typescript-eslint/project-service": "8.54.0", - "@typescript-eslint/tsconfig-utils": "8.54.0", - "@typescript-eslint/types": "8.54.0", - "@typescript-eslint/visitor-keys": "8.54.0", + "@typescript-eslint/project-service": "8.56.1", + "@typescript-eslint/tsconfig-utils": "8.56.1", + "@typescript-eslint/types": "8.56.1", + "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", - "minimatch": "^9.0.5", + "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" } }, "@typescript-eslint/visitor-keys": { - "version": "8.54.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.54.0.tgz", - "integrity": "sha512-VFlhGSl4opC0bprJiItPQ1RfUhGDIBokcPwaFH4yiBCaNPeld/9VeXbiPO1cLyorQi1G1vL+ecBk1x8o1axORA==", + "version": "8.56.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", + "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "dev": true, "requires": { - "@typescript-eslint/types": "8.54.0", - "eslint-visitor-keys": "^4.2.1" + "@typescript-eslint/types": "8.56.1", + "eslint-visitor-keys": "^5.0.0" } }, "balanced-match": { @@ -41693,9 +41886,9 @@ "dev": true }, "brace-expansion": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.3.tgz", - "integrity": "sha512-fy6KJm2RawA5RcHkLa1z/ScpBeA762UF9KmZQxwIbDtRJrgLzM10depAiEQ+CXYcoiqW1/m96OAAoke2nE9EeA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "requires": { "balanced-match": "^4.0.2" @@ -41711,9 +41904,9 @@ } }, "eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true }, "ignore": { @@ -41723,9 +41916,9 @@ "dev": true }, "minimatch": { - "version": "9.0.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.6.tgz", - "integrity": "sha512-kQAVowdR33euIqeA0+VZTDqU+qo1IeVY+hrKYtZMio3Pg0P0vuh/kwRylLUddJhB6pf3q/botcOvRtx4IN1wqQ==", + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "requires": { "brace-expansion": "^5.0.2" @@ -42065,6 +42258,36 @@ "integrity": "sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung==", "dev": true }, + "vscode-css-languageservice": { + "version": "6.3.10", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.10.tgz", + "integrity": "sha512-eq5N9Er3fC4vA9zd9EFhyBG90wtCCuXgRSpAndaOgXMh1Wgep5lBgRIeDgjZBW9pa+332yC9+49cZMW8jcL3MA==", + "dev": true, + "requires": { + "@vscode/l10n": "^0.0.18", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-languageserver-types": "3.17.5", + "vscode-uri": "^3.1.0" + } + }, + "vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true + }, + "vscode-languageserver-types": { + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "dev": true + }, + "vscode-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true + }, "w3c-keyname": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index 4f083777a0f..586bb3e5919 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -6,16 +6,16 @@ "private": true, "devDependencies": { "@angular-builders/custom-esbuild": "^21.0.3", - "@angular-devkit/build-angular": "^21.1.4", + "@angular-devkit/build-angular": "^21.1.5", "@angular-eslint/builder": "20.7.0", "@angular-eslint/eslint-plugin": "20.7.0", "@angular-eslint/eslint-plugin-template": "20.7.0", "@angular-eslint/schematics": "20.7.0", "@angular-eslint/template-parser": "20.7.0", - "@angular/language-service": "21.1.5", + "@angular/language-service": "21.1.6", "@eslint/js": "^9.39.2", - "@html-eslint/eslint-plugin": "^0.54.2", - "@html-eslint/parser": "^0.54.0", + "@html-eslint/eslint-plugin": "^0.57.1", + "@html-eslint/parser": "^0.57.1", "@jsdevtools/coverage-istanbul-loader": "3.0.5", "@stylistic/eslint-plugin": "^5.7.1", "@types/codemirror": "5.60.5", @@ -31,7 +31,7 @@ "@types/mousetrap": "^1.6.3", "@types/pako": "^2.0.4", "@types/rails__request.js": "^0.0.1", - "@types/react": "^19.2.10", + "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@types/resize-observer-browser": "^0.1.4", "@types/urijs": "^1.19.26", @@ -60,22 +60,22 @@ "source-map-explorer": "^2.5.2", "ts-node": "~10.9.2", "typescript": "^5.9.3", - "typescript-eslint": "^8.54.0", + "typescript-eslint": "^8.56.1", "wscat": "^6.1.0" }, "dependencies": { - "@angular/animations": "^21.1.5", - "@angular/cdk": "^21.1.5", - "@angular/cli": "^21.1.4", - "@angular/common": "^21.1.5", - "@angular/compiler": "^21.1.5", - "@angular/compiler-cli": "^21.1.5", - "@angular/core": "^21.1.5", - "@angular/elements": "^21.1.5", - "@angular/forms": "^21.1.5", - "@angular/platform-browser": "^21.1.5", - "@angular/platform-browser-dynamic": "^21.1.5", - "@angular/router": "^21.1.5", + "@angular/animations": "^21.1.6", + "@angular/cdk": "^21.1.6", + "@angular/cli": "^21.1.5", + "@angular/common": "^21.1.6", + "@angular/compiler": "^21.1.6", + "@angular/compiler-cli": "^21.1.6", + "@angular/core": "^21.1.6", + "@angular/elements": "^21.1.6", + "@angular/forms": "^21.1.6", + "@angular/platform-browser": "^21.1.6", + "@angular/platform-browser-dynamic": "^21.1.6", + "@angular/router": "^21.1.6", "@appsignal/javascript": "^1.6.1", "@appsignal/plugin-breadcrumbs-console": "^1.1.37", "@appsignal/plugin-breadcrumbs-network": "^1.1.24", @@ -120,14 +120,14 @@ "@rails/request.js": "^0.0.13", "@stimulus-components/auto-submit": "^6.0.0", "@stimulus-components/reveal": "^5.0.0", - "@tiptap/extensions": "^3.13.0", + "@tiptap/extensions": "^3.20.0", "@types/jquery.cookie": "^1.4.36", "@uirouter/angular": "^17.0.0", "@uirouter/core": "^6.1.0", "@uirouter/rx": "^1.0.0", "@w11k/ngx-componentdestroyed": "^5.0.2", "@xeokit/xeokit-bim-viewer": "2.7.1", - "autoprefixer": "^10.4.23", + "autoprefixer": "^10.4.27", "byte-base64": "^1.1.0", "chart.js": "4.5.1", "chartjs-adapter-luxon": "^1.3.1", diff --git a/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts index 58262b7d6e4..079ef05844d 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/multi-select-edit-field.component.ts @@ -204,7 +204,7 @@ export class MultiSelectEditFieldComponent extends EditFieldComponent implements if (Array.isArray(allowedValues)) { this.setValues(allowedValues); } else if (this.schema.allowedValues) { - return this.schema.allowedValues.$load().then((values:CollectionResource) => { + return (this.schema.allowedValues.$load() as Promise).then((values:CollectionResource) => { // The select options of the project shall be sorted if (values.count > 0 && (values.elements[0] as any)._type === 'Project') { this.setValues(values.elements, true); diff --git a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts index ca0cb105028..6bec3bbad27 100644 --- a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts +++ b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.ts @@ -173,7 +173,7 @@ export abstract class FilePickerBaseModalComponent extends OpModalComponent impl return of('/'); } - if (this.locals.projectFolderMode === 'automatic' && this.locals.projectFolderHref === null) { + if (this.locals.projectFolderMode === 'automatic' && !this.locals.projectFolderHref) { this.showAlert.next('managedFolderNotFound'); return of('/'); } diff --git a/frontend/src/global_styles/common/header/app-header.sass b/frontend/src/global_styles/common/header/app-header.sass index c93df1a4edb..789b3052e0a 100644 --- a/frontend/src/global_styles/common/header/app-header.sass +++ b/frontend/src/global_styles/common/header/app-header.sass @@ -45,6 +45,7 @@ grid-area: center justify-content: center + &--center_mobile-end @media screen and (max-width: $breakpoint-md) justify-content: flex-end diff --git a/lib/api/open_project_api.rb b/lib/api/open_project_api.rb index 5123a3bb730..3f84f7ed400 100644 --- a/lib/api/open_project_api.rb +++ b/lib/api/open_project_api.rb @@ -40,27 +40,3 @@ module API end end end - -Grape::DSL::Routing::ClassMethods.module_eval do - # Be reload safe. otherwise, an infinite loop occurs on reload. - unless instance_methods.include?(:orig_namespace) - alias :orig_namespace :namespace - end - - def namespace(space = nil, options = {}, &) - orig_namespace(space, options) do - instance_eval(&) - apply_patches(space) - end - end - - def apply_patches(path) - (patches[path] || []).each do |patch| - instance_eval(&patch) - end - end - - def patches - Constants::APIPatchRegistry.patches_for(base) - end -end diff --git a/lib/api/v3/work_packages/work_package_representer.rb b/lib/api/v3/work_packages/work_package_representer.rb index 4a5f03e5307..4043e338c45 100644 --- a/lib/api/v3/work_packages/work_package_representer.rb +++ b/lib/api/v3/work_packages/work_package_representer.rb @@ -657,7 +657,8 @@ module API @current_user_update_allowed = current_user.allowed_in_work_package?(:edit_work_packages, represented) || current_user.allowed_in_project?(:change_work_package_status, represented.project) || - current_user.allowed_in_project?(:assign_versions, represented.project) + current_user.allowed_in_project?(:assign_versions, represented.project) || + current_user.allowed_in_project?(:manage_sprint_items, represented.project) end def view_time_entries_allowed? diff --git a/lib/open_project.rb b/lib/open_project.rb index 90ae8e60c3f..149308044e4 100644 --- a/lib/open_project.rb +++ b/lib/open_project.rb @@ -59,11 +59,9 @@ module OpenProject private_class_method def self.httpx_session session = HTTPX - .plugin(:oauth) - .plugin(:persistent) - .plugin(:basic_auth) - .plugin(:webdav) .with(headers: { "User-Agent" => "OpenProject #{OpenProject::VERSION.to_semver} HTTPX Client" }) + .plugin(:auth) + .plugin(:webdav) .with( timeout: { connect_timeout: OpenProject::Configuration.httpx_connect_timeout, @@ -74,6 +72,7 @@ module OpenProject keep_alive_timeout: OpenProject::Configuration.httpx_keep_alive_timeout } ) + OpenProject::Appsignal.enabled? ? session.plugin(HttpxAppsignal) : session end end diff --git a/lib/open_project/events.rb b/lib/open_project/events.rb index 143cbf70163..c2e7fbb8f9b 100644 --- a/lib/open_project/events.rb +++ b/lib/open_project/events.rb @@ -80,6 +80,8 @@ module OpenProject WATCHER_ADDED = "watcher_added" WATCHER_DESTROYED = "watcher_destroyed" + MODULE_DISABLED = "module_disabled" + WORK_PACKAGE_SHARED = "work_package_shared" end end diff --git a/lib/open_project/patches/carrierwave_fog_file.rb b/lib/open_project/patches/carrierwave_fog_file.rb deleted file mode 100644 index c1aca617364..00000000000 --- a/lib/open_project/patches/carrierwave_fog_file.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -require "carrierwave/storage/fog" - -## -# Code copied straight from the CarrierWave source. -# All we did is add `options[:expire_at]`. -# -# @todo Upgrade to CarrierWave 2.0.2 to make this patch obsolete. - -module OpenProject::Patches::FogFile - extend ActiveSupport::Concern - - included do - def authenticated_url(options = {}) # rubocop:disable Metrics/AbcSize - if ["AWS", "Google", "Rackspace", "OpenStack"].include?(@uploader.fog_credentials[:provider]) - # avoid a get by using local references - local_directory = connection.directories.new(key: @uploader.fog_directory) - local_file = local_directory.files.new(key: path) - expire_at = options[:expire_at] || (::Fog::Time.now + @uploader.fog_authenticated_url_expiration) - case @uploader.fog_credentials[:provider] - when "AWS", "Google" - # Older versions of fog-google do not support options as a parameter - if url_options_supported?(local_file) - local_file.url(expire_at, options) - else - warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider." - local_file.url(expire_at) - end - when "Rackspace" - connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options) - when "OpenStack" - connection.get_object_https_url(@uploader.fog_directory, path, expire_at) - else - local_file.url(expire_at) - end - end - end - end -end - -OpenProject::Patches.patch_gem_version "carrierwave", "1.3.4" do - CarrierWave::Storage::Fog::File.include OpenProject::Patches::FogFile -end diff --git a/lib/open_project/patches/carrierwave_sanitized_file.rb b/lib/open_project/patches/grape_dsl_routing.rb similarity index 58% rename from lib/open_project/patches/carrierwave_sanitized_file.rb rename to lib/open_project/patches/grape_dsl_routing.rb index 33d8fa02316..8aaf0402a7f 100644 --- a/lib/open_project/patches/carrierwave_sanitized_file.rb +++ b/lib/open_project/patches/grape_dsl_routing.rb @@ -27,35 +27,35 @@ # # See COPYRIGHT and LICENSE files for more details. #++ -require "carrierwave" -## -# Adapt carrierwave to match fixes for CVE-2023-49090. -# https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-vfmv-jfc5-pjjw -module OpenProject::Patches::CarrierwaveSanitizedFile +module OpenProject::Patches::GrapeDslRouting extend ActiveSupport::Concern included do - def content_type - return @content_type if @content_type + # Be reload safe. otherwise, an infinite loop occurs on reload. + unless method_defined?(:orig_namespace) + alias :orig_namespace :namespace + end - if @file.respond_to?(:content_type) and @file.content_type - Marcel::MimeType.for(declared_type: @file.content_type.to_s.chomp) - elsif path - @content_type = Attachment.content_type_for(path) + def namespace(space = nil, **, &) + orig_namespace(space, **) do + instance_eval(&) + apply_patches(space) end end - # create the directory if it doesn't exist - # Overwritten to avoid ruby 2.7 deprecations - def mkdir!(path, directory_permissions) - options = {} - options[:mode] = directory_permissions if directory_permissions - FileUtils.mkdir_p(File.dirname(path), **options) + def apply_patches(path) + (patches[path] || []).each do |patch| + instance_eval(&patch) + end + end + + def patches + Constants::APIPatchRegistry.patches_for(self) end end end -OpenProject::Patches.patch_gem_version "carrierwave", "1.3.4" do - CarrierWave::SanitizedFile.include OpenProject::Patches::CarrierwaveSanitizedFile +OpenProject::Patches.patch_gem_version "grape", "3.1.1" do + Grape::DSL::Routing.include OpenProject::Patches::GrapeDslRouting end diff --git a/lib/open_project/ssrf_protection.rb b/lib/open_project/ssrf_protection.rb index fc2f4f2fb5a..c9e786c2129 100644 --- a/lib/open_project/ssrf_protection.rb +++ b/lib/open_project/ssrf_protection.rb @@ -30,35 +30,6 @@ module OpenProject class SsrfProtection < ::SsrfFilter - # ssrf_filter works by resolving the hostname to a public IP, then connecting to that IP - # directly (to prevent DNS-rebinding attacks). For HTTPS, Net::HTTP must set the SNI hostname - # so the server knows which certificate to serve. - # - # However, modern Net::HTTP (net-http gem) explicitly skips setting SNI when the address is - # an IP (correctly per RFC 6066 §3 — IP literals are not valid in SNI). ssrf_filter 1.0.x - # patches SSLSocket#hostname= to inject the original hostname, but that patch never fires - # because Net::HTTP never calls hostname= for IP addresses. - # - # This patch targets SSLSocket#connect instead, which IS called regardless of address type. - # It sets the SNI hostname from ssrf_filter's thread-local just before the TLS handshake, - # making HTTPS work for SNI-dependent servers (e.g. Cloudflare-hosted webhooks). - # - # This patch can be removed once release/17.2 is retired — on dev we use ssrf_filter ~> 1.3 - # which resolves this differently. - ::OpenSSL::SSL::SSLSocket.class_eval do - # Net::Protocol#ssl_socket_connect uses connect_nonblock when a timeout is set (the common - # case), falling back to connect only when timeout is nil. Patch both to be safe. - %i[connect connect_nonblock].each do |meth| - original = instance_method(meth) - define_method(meth) do |**kwargs| - if (hostname = ::Thread.current[::SsrfFilter::FIBER_LOCAL_KEY]) - self.hostname = hostname - end - original.bind(self).call(**kwargs) - end - end - end - class << self ## # Performs an SSRF-safe HTTP POST request to the given URL. diff --git a/lib/open_project/version.rb b/lib/open_project/version.rb index 17808308b57..338b47873cb 100644 --- a/lib/open_project/version.rb +++ b/lib/open_project/version.rb @@ -32,8 +32,8 @@ require "open3" module OpenProject module VERSION # :nodoc: MAJOR = 17 - MINOR = 2 - PATCH = 3 + MINOR = 3 + PATCH = 0 class << self def revision diff --git a/lib/redmine/menu_manager/wiki_menu_helper.rb b/lib/redmine/menu_manager/wiki_menu_helper.rb index 1262b4827fa..6e85ff6d53c 100644 --- a/lib/redmine/menu_manager/wiki_menu_helper.rb +++ b/lib/redmine/menu_manager/wiki_menu_helper.rb @@ -36,10 +36,6 @@ module Redmine::MenuManager::WikiMenuHelper wiki_main_items(project_wiki).reverse_each do |main_item| Redmine::MenuManager.loose :project_menu do |menu| push_wiki_main_menu(menu, main_item, project) - - main_item.children.each do |child| - push_wiki_menu_subitem(menu, main_item, child) - end end end end @@ -60,17 +56,6 @@ module Redmine::MenuManager::WikiMenuHelper main_item.destroy end - def push_wiki_menu_subitem(menu, main_item, child) - menu.push child.menu_identifier, - { controller: "/wiki", action: "show", id: child.slug }, - caption: child.title, - html: { class: "wiki-menu--sub-item" }, - parent: main_item.menu_identifier - rescue ArgumentError => e - Rails.logger.error "Failed to add wiki item #{child.slug} to wiki menu: #{e}. Deleting it." - child.destroy - end - def default_menu_item(page) if (main_item = page.nearest_main_item) main_item diff --git a/lib_static/plugins/acts_as_customizable/lib/acts_as_customizable.rb b/lib_static/plugins/acts_as_customizable/lib/acts_as_customizable.rb index d6001afdb96..1f6b72e7d26 100644 --- a/lib_static/plugins/acts_as_customizable/lib/acts_as_customizable.rb +++ b/lib_static/plugins/acts_as_customizable/lib/acts_as_customizable.rb @@ -48,7 +48,7 @@ module Redmine # N.B. the default for validate should be false, however specs seem to think differently has_many :custom_values, -> { includes(:custom_field) - .order("#{CustomField.table_name}.position") + .order("#{CustomField.table_name}.position", "#{CustomValue.table_name}.id") }, as: :customized, dependent: :delete_all, validate: false, diff --git a/lookbook/previews/op_primer/copy_to_clipboard_component_preview.rb b/lookbook/previews/op_primer/copy_to_clipboard_component_preview.rb index f37618269db..e4766bff810 100644 --- a/lookbook/previews/op_primer/copy_to_clipboard_component_preview.rb +++ b/lookbook/previews/op_primer/copy_to_clipboard_component_preview.rb @@ -36,6 +36,12 @@ module OpPrimer render(OpPrimer::CopyToClipboardComponent.new(value)) end + # @param url text + # @param scheme [Symbol] select [value, link] + def playground(url: "https://example.org", scheme: :value) + render(OpPrimer::CopyToClipboardComponent.new(url, scheme:)) + end + # @param url text def as_link(url: "http://example.org") render(OpPrimer::CopyToClipboardComponent.new(url, scheme: :link)) diff --git a/modules/auth_saml/app/components/saml/providers/side_panel/information_component.html.erb b/modules/auth_saml/app/components/saml/providers/side_panel/information_component.html.erb index 758bde48119..260bede0a4c 100644 --- a/modules/auth_saml/app/components/saml/providers/side_panel/information_component.html.erb +++ b/modules/auth_saml/app/components/saml/providers/side_panel/information_component.html.erb @@ -9,7 +9,7 @@ end collection.with_component( - OpPrimer::CopyToClipboardComponent.new(provider.sp_entity_id, scheme: :input) + OpPrimer::CopyToClipboardComponent.new(provider.sp_entity_id, scheme: :value) ) collection.with_component(Primer::Beta::Heading.new(tag: :h5, mt: 4, mb: 1)) do diff --git a/modules/backlogs/app/components/backlogs/backlog_component.rb b/modules/backlogs/app/components/backlogs/backlog_component.rb index a25366262a3..888de7921d6 100644 --- a/modules/backlogs/app/components/backlogs/backlog_component.rb +++ b/modules/backlogs/app/components/backlogs/backlog_component.rb @@ -73,7 +73,7 @@ module Backlogs { generic_drag_and_drop_target: "container", target_container_accessor: ":scope > ul", - target_id: backlog.sprint_id, + target_id: "version:#{backlog.sprint_id}", target_allowed_drag_type: "story" } end @@ -82,7 +82,7 @@ module Backlogs { draggable_id: story.id, draggable_type: "story", - drop_url: move_backlogs_project_sprint_story_path(project, sprint, story) + drop_url: move_legacy_backlogs_project_sprint_story_path(project, sprint, story) } end end diff --git a/modules/backlogs/app/components/backlogs/backlog_menu_component.html.erb b/modules/backlogs/app/components/backlogs/backlog_menu_component.html.erb index 5926e1aa2e2..944eb46096d 100644 --- a/modules/backlogs/app/components/backlogs/backlog_menu_component.html.erb +++ b/modules/backlogs/app/components/backlogs/backlog_menu_component.html.erb @@ -36,7 +36,7 @@ See COPYRIGHT and LICENSE files for more details. tooltip_direction: :se ) - if user_allowed?(:update_sprints) + if user_allowed?(:create_sprints) menu.with_item( id: dom_target(sprint, :menu, :edit_sprint), label: t(".action_menu.edit_sprint"), @@ -47,7 +47,7 @@ See COPYRIGHT and LICENSE files for more details. end end - if user_allowed?(:add_work_packages) + if user_allowed?(:manage_sprint_items) menu.with_item( id: dom_target(sprint, :menu, :new_story), label: t(".action_menu.new_story"), @@ -62,7 +62,7 @@ See COPYRIGHT and LICENSE files for more details. end end - if user_allowed?(:update_sprints) || user_allowed?(:add_work_packages) + if user_allowed?(:create_sprints) || user_allowed?(:manage_sprint_items) menu.with_divider end @@ -76,15 +76,13 @@ See COPYRIGHT and LICENSE files for more details. end if backlog.sprint_backlog? - if user_allowed?(:view_taskboards) - menu.with_item( - id: dom_target(sprint, :menu, :task_board), - label: t(".action_menu.task_board"), - tag: :a, - href: backlogs_project_sprint_taskboard_path(project, sprint) - ) do |item| - item.with_leading_visual_icon(icon: :"op-view-cards") - end + menu.with_item( + id: dom_target(sprint, :menu, :task_board), + label: t(".action_menu.task_board"), + tag: :a, + href: backlogs_project_sprint_taskboard_path(project, sprint) + ) do |item| + item.with_leading_visual_icon(icon: :"op-view-cards") end menu.with_item( @@ -109,7 +107,7 @@ See COPYRIGHT and LICENSE files for more details. end end - if user_allowed?(:manage_versions) + if user_allowed?(:create_sprints) menu.with_item( id: dom_target(sprint, :menu, :properties), label: t(".action_menu.properties"), diff --git a/modules/backlogs/app/components/backlogs/new_sprint_dialog_component.html.erb b/modules/backlogs/app/components/backlogs/new_sprint_dialog_component.html.erb new file mode 100644 index 00000000000..b56a66a937a --- /dev/null +++ b/modules/backlogs/app/components/backlogs/new_sprint_dialog_component.html.erb @@ -0,0 +1,69 @@ +<%#-- 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. + +++#%> + +<%= + render( + Primer::Alpha::Dialog.new( + title:, + size: :large, + id: DIALOG_ID + ) + ) do |d| + d.with_header(variant: :large) + + d.with_body do + render(Backlogs::NewSprintFormComponent.new(sprint: @sprint)) + end + + d.with_footer do + component_collection do |buttons| + buttons.with_component( + Primer::Beta::Button.new( + data: { + close_dialog_id: DIALOG_ID + } + ) + ) do + t("button_cancel") + end + + buttons.with_component( + Primer::Beta::Button.new( + scheme: :primary, + form: FORM_ID, + data: { turbo: true }, + type: :submit + ) + ) do + button_caption + end + end + end + end +%> diff --git a/modules/backlogs/app/components/backlogs/new_sprint_dialog_component.rb b/modules/backlogs/app/components/backlogs/new_sprint_dialog_component.rb new file mode 100644 index 00000000000..99432014903 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/new_sprint_dialog_component.rb @@ -0,0 +1,65 @@ +# 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 Backlogs + class NewSprintDialogComponent < ApplicationComponent + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + include Primer::FetchOrFallbackHelper + + DIALOG_ID = "new-sprint-dialog" + FORM_ID = "new-sprint-dialog-form" + FOOTER_ID = "new-sprint-dialog-footer" + + STATE_DEFAULT = :create + STATE_OPTIONS = [STATE_DEFAULT, :edit].freeze + + attr_reader :sprint, :state + + delegate :create?, :edit?, to: :state + + def initialize(sprint:, state: STATE_DEFAULT) + super + + @sprint = sprint + @state = ActiveSupport::StringInquirer.new(fetch_or_fallback(STATE_OPTIONS, state, STATE_DEFAULT).to_s) + end + + private + + def title + create? ? t(:label_sprint_new) : t(:label_sprint_edit) + end + + def button_caption + create? ? t(:button_create) : t(:button_save) + end + end +end diff --git a/modules/backlogs/app/components/backlogs/new_sprint_form_component.html.erb b/modules/backlogs/app/components/backlogs/new_sprint_form_component.html.erb new file mode 100644 index 00000000000..db3d8bebb39 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/new_sprint_form_component.html.erb @@ -0,0 +1,56 @@ +<%#-- 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. + +++#%> + +<%= + component_wrapper do + primer_form_with( + id: Backlogs::NewSprintFormComponent::FORM_ID, + model: @sprint, + scope: :sprint, + method: http_verb, + class: "op-sprints--form", + url: form_url, + data: data_attributes + ) do |f| + flex_layout(mb: 2) do |flex| + if @base_errors&.any? + flex.with_row do + render(Primer::Alpha::Banner.new(mb: 3, icon: :stop, scheme: :danger)) { @base_errors.join("\n") } + end + end + flex.with_row(mb: 3) do + render Backlogs::Sprints::DetailsForm.new(f) + end + flex.with_row(classes: "FormControl-horizontalGroup--sm-vertical") do + render Backlogs::Sprints::DatesForm.new(f) + end + end + end + end +%> diff --git a/modules/backlogs/app/components/backlogs/new_sprint_form_component.rb b/modules/backlogs/app/components/backlogs/new_sprint_form_component.rb new file mode 100644 index 00000000000..1b8532e6add --- /dev/null +++ b/modules/backlogs/app/components/backlogs/new_sprint_form_component.rb @@ -0,0 +1,68 @@ +# 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 Backlogs + class NewSprintFormComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + FORM_ID = NewSprintDialogComponent::FORM_ID + + def initialize(sprint:, base_errors: nil) + super + + @sprint = sprint + @base_errors = base_errors + end + + private + + def http_verb + @sprint.new_record? ? :post : :put + end + + def form_url + if @sprint.new_record? + project_sprints_path(@sprint.project_id) + else + update_agile_sprint_project_sprint_path(@sprint.project_id, @sprint.id) + end + end + + def data_attributes + { + controller: "refresh-on-form-changes", + "refresh-on-form-changes-target": "form", + "refresh-on-form-changes-turbo-stream-url-value": refresh_form_project_sprints_path(@sprint.project_id) + } + end + end +end diff --git a/modules/backlogs/app/components/backlogs/sprint_component.html.erb b/modules/backlogs/app/components/backlogs/sprint_component.html.erb new file mode 100644 index 00000000000..fc6fe3ac72b --- /dev/null +++ b/modules/backlogs/app/components/backlogs/sprint_component.html.erb @@ -0,0 +1,63 @@ +<%# -- 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. + +++# %> + +<%= component_wrapper(tag: :section) do %> + <%= render(Primer::Beta::BorderBox.new(**@system_arguments)) do |border_box| %> + <% border_box.with_header(id: dom_target(sprint, :header)) do %> + <%= render(Backlogs::SprintHeaderComponent.new(sprint:, folded: folded?)) %> + <% end %> + <% if stories.empty? %> + <% border_box.with_row(data: { empty_list_item: true }) do %> + <%= + render Primer::Beta::Blankslate.new(role: "status", aria: { live: "polite" }) do |blankslate| + blankslate.with_heading(tag: :h4).with_content(t(".blankslate_title", name: sprint.name)) + blankslate.with_description_content(t(".blankslate_description")) + end + %> + <% end %> + <% end %> + <% stories.each do |story| %> + <% border_box.with_row( + id: dom_id(story), + classes: "Box-row--hover-blue Box-row--focus-gray Box-row--clickable Box-row--draggable", + data: draggable_item_config(story).merge( + story: true, + controller: "backlogs--story", + backlogs__story_id_value: story.id, + backlogs__story_split_url_value: details_backlogs_project_backlogs_path(project, story), + backlogs__story_full_url_value: work_package_path(story), + backlogs__story_selected_class: "Box-row--blue" + ), + tabindex: 0 + ) do %> + <%= render(Backlogs::StoryComponent.new(story:, sprint:, max_position:)) %> + <% end %> + <% end %> + <% end %> +<% end %> diff --git a/modules/backlogs/app/components/backlogs/sprint_component.rb b/modules/backlogs/app/components/backlogs/sprint_component.rb new file mode 100644 index 00000000000..19758de4839 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/sprint_component.rb @@ -0,0 +1,92 @@ +# 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 Backlogs + class SprintComponent < ApplicationComponent + include Primer::AttributesHelper + include OpTurbo::Streamable + include RbCommonHelper + + attr_reader :sprint, :current_user + + delegate :project, to: :sprint + + def initialize(sprint:, current_user: User.current, **system_arguments) + super() + + @sprint = sprint + @current_user = current_user + + @system_arguments = system_arguments + @system_arguments[:id] = dom_id(sprint) + @system_arguments[:list_id] = "#{@system_arguments[:id]}-list" + @system_arguments[:padding] = :condensed + @system_arguments[:data] = merge_data( + @system_arguments, + { data: drop_target_config } + ) + end + + def stories + sprint.work_packages + end + + def wrapper_uniq_by + sprint.id + end + + private + + def folded? + current_user.backlogs_preference(:versions_default_fold_state) == "closed" + end + + def max_position + stories.filter_map(&:position).max + end + + def drop_target_config + { + generic_drag_and_drop_target: "container", + target_container_accessor: ":scope > ul", + target_id: "sprint:#{sprint.id}", + target_allowed_drag_type: "story" + } + end + + def draggable_item_config(story) + { + draggable_id: story.id, + draggable_type: "story", + drop_url: move_project_sprint_story_path(project, sprint, story) + } + end + end +end diff --git a/modules/backlogs/app/components/backlogs/sprint_header_component.html.erb b/modules/backlogs/app/components/backlogs/sprint_header_component.html.erb new file mode 100644 index 00000000000..1c1b3f62326 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/sprint_header_component.html.erb @@ -0,0 +1,77 @@ +<%# -- 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. + +++# %> + +<%= component_wrapper(tag: :header) do %> + <%= grid_layout("op-backlogs-header", tag: :div) do |grid| %> + <% grid.with_area(:collapsible) do %> + <%= + render( + Primer::OpenProject::BorderBox::CollapsibleHeader.new( + collapsible_id: "#{dom_id(sprint)}-list", + collapsed:, + multi_line: false + ) + ) do |collapsible| + collapsible.with_title { sprint.name } + collapsible.with_count( + scheme: :default, + count: story_count, + round: true, + aria: { + label: t(".label_story_count", count: story_count), + live: "polite" + } + ) + collapsible.with_description(role: "group") do + format_date_range(date_range) + end + end + %> + <% end %> + + <% grid.with_area(:points) do %> + <%= + render( + Primer::Beta::Text.new( + color: :subtle, + classes: "velocity", + aria: { live: "polite" } + ) + ) do + %> + <%= story_points %> + <%= t(:"backlogs.points_label", count: story_points) %> + <% end %> + <% end %> + + <% grid.with_area(:menu) do %> + <%= render(Backlogs::SprintMenuComponent.new(sprint:, project:)) %> + <% end %> + <% end %> +<% end %> diff --git a/modules/backlogs/app/components/backlogs/sprint_header_component.rb b/modules/backlogs/app/components/backlogs/sprint_header_component.rb new file mode 100644 index 00000000000..968276e1d50 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/sprint_header_component.rb @@ -0,0 +1,78 @@ +# 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 Backlogs + class SprintHeaderComponent < ApplicationComponent + include OpPrimer::ComponentHelpers + include OpTurbo::Streamable + include Primer::FetchOrFallbackHelper + include Redmine::I18n + include RbCommonHelper + + attr_reader :sprint, :collapsed, :current_user + + delegate :project, to: :sprint + delegate :name, to: :sprint, prefix: :sprint + + def initialize( + sprint:, + folded: false, + current_user: User.current + ) + super() + + @sprint = sprint + @collapsed = folded + @current_user = current_user + end + + def wrapper_uniq_by + sprint.id + end + + def stories + @sprint.work_packages + end + + private + + def story_points + @story_points ||= stories.sum { |story| story.story_points || 0 } + end + + def story_count + @story_count ||= stories.size + end + + def date_range + [sprint.start_date, sprint.finish_date] + end + end +end diff --git a/modules/backlogs/app/components/backlogs/sprint_menu_component.html.erb b/modules/backlogs/app/components/backlogs/sprint_menu_component.html.erb new file mode 100644 index 00000000000..13d99c433c6 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/sprint_menu_component.html.erb @@ -0,0 +1,99 @@ +<%# -- 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. + +++# %> + +<%= + render(Primer::Alpha::ActionMenu.new(**@system_arguments)) do |menu| + menu.with_show_button( + scheme: :invisible, + icon: :"kebab-horizontal", + "aria-label": t(".label_actions"), + tooltip_direction: :se + ) + + if user_allowed?(:create_sprints) + menu.with_item( + id: dom_target(sprint, :menu, :edit_sprint), + label: t(".action_menu.edit_sprint"), + href: edit_dialog_project_sprint_path(project, sprint), + content_arguments: { data: { controller: "async-dialog" } } + ) do |item| + item.with_leading_visual_icon(icon: :pencil) + end + end + + if user_allowed?(:manage_sprint_items) + menu.with_item( + id: dom_target(sprint, :menu, :new_story), + label: t(".action_menu.new_story"), + href: new_project_work_packages_dialog_path( + project, + sprint_id: sprint.id, + type_id: available_story_types.first + ), + content_arguments: { data: { turbo_stream: true } } + ) do |item| + item.with_leading_visual_icon(icon: :compose) + end + end + + if user_allowed?(:create_sprints) || user_allowed?(:add_work_packages) + menu.with_divider + end + + menu.with_item( + # TODO: sprint_id filter does not exist for work packages. Add? + id: dom_target(sprint, :menu, :stories_tasks), + scheme: :danger, + label: t(".action_menu.stories_tasks"), + tag: :a, + href: project_work_packages_path(project, sprint_id: sprint.id) + ) do |item| + item.with_leading_visual_icon(icon: :"op-view-list") + end + + # menu.with_item( + # # TODO: what to do with the task board? + # label: t(".action_menu.task_board"), + # tag: :a, + # href: backlogs_project_sprint_taskboard_path(project, sprint) + # ) do |item| + # item.with_leading_visual_icon(icon: :"op-view-cards") + # end + # + # menu.with_item( + # # TODO: what to do with the burndown chart? + # label: t(".action_menu.burndown_chart"), + # tag: :a, + # href: backlogs_project_sprint_burndown_chart_path(project, sprint), + # disabled: !sprint.has_burndown? + # ) do |item| + # item.with_leading_visual_icon(icon: :graph) + # end + end +%> diff --git a/modules/backlogs/app/components/backlogs/sprint_menu_component.rb b/modules/backlogs/app/components/backlogs/sprint_menu_component.rb new file mode 100644 index 00000000000..91e3dc15436 --- /dev/null +++ b/modules/backlogs/app/components/backlogs/sprint_menu_component.rb @@ -0,0 +1,67 @@ +# 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 Backlogs + class SprintMenuComponent < ApplicationComponent + include RbCommonHelper + + attr_reader :sprint, :project, :current_user + + def initialize(sprint:, project:, current_user: User.current, **system_arguments) + super() + + @sprint = sprint + @project = project + @current_user = current_user + + @system_arguments = system_arguments + @system_arguments[:menu_id] = dom_target(sprint, :menu) + @system_arguments[:anchor_align] = :end + @system_arguments[:classes] = class_names( + @system_arguments[:classes], + "hide-when-print" + ) + end + + def stories + @sprint.work_packages + end + + private + + def user_allowed?(permission) + current_user.allowed_in_project?(permission, project) + end + + def available_story_types + @available_story_types ||= story_types & project.types + end + end +end diff --git a/modules/backlogs/app/components/projects/settings/backlogs/settings_header_component.html.erb b/modules/backlogs/app/components/projects/settings/backlogs/settings_header_component.html.erb new file mode 100644 index 00000000000..985b7d9a585 --- /dev/null +++ b/modules/backlogs/app/components/projects/settings/backlogs/settings_header_component.html.erb @@ -0,0 +1,76 @@ +<%#-- 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. + +++#%> + +<%= + render Primer::OpenProject::PageHeader.new do |header| + header.with_title { t(:label_backlogs) } + header.with_breadcrumbs( + [{ href: project_overview_path(project), text: project.name }, + { href: project_settings_general_path(project), text: I18n.t(:label_project_settings) }, + I18n.t(:label_backlogs)] + ) + + header.with_tab_nav(label: nil) do |tab_nav| + tab_nav.with_tab( + selected: selected_tab?(:done_status), + href: project_settings_backlogs_path(project) + ) do |t| + t.with_text { t("backlogs.done_status") } + end + + if OpenProject::FeatureDecisions.scrum_projects_active? && User.current.allowed_in_project?(:share_sprint, project) + tab_nav.with_tab( + selected: selected_tab?(:sharing), + href: project_settings_backlog_sharing_path(project) + ) do |t| + t.with_text { t("backlogs.sharing") } + end + end + end + + if selected_tab == :done_status + header.with_action_menu( + menu_arguments: { anchor_align: :end }, + button_arguments: { + icon: :"kebab-horizontal", + "aria-label": t(:label_more) + } + ) do |menu| + menu.with_item( + tag: :button, + href: rebuild_positions_project_settings_backlogs_path(@project), + label: t(:"backlogs.rebuild_positions"), + form_arguments: { method: :post, data: { turbo_frame: "_top" } } + ) do |item| + item.with_leading_visual_icon(icon: :sync) + end + end + end + end +%> diff --git a/modules/backlogs/app/components/projects/settings/backlogs/settings_header_component.rb b/modules/backlogs/app/components/projects/settings/backlogs/settings_header_component.rb new file mode 100644 index 00000000000..25892dbb886 --- /dev/null +++ b/modules/backlogs/app/components/projects/settings/backlogs/settings_header_component.rb @@ -0,0 +1,54 @@ +# 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 Projects + module Settings + module Backlogs + class SettingsHeaderComponent < ApplicationComponent + include OpPrimer::ComponentHelpers + + def initialize(project:, selected_tab:) + super + + @project = project + @selected_tab = selected_tab + end + + def selected_tab?(tab_name) + selected_tab == tab_name + end + + private + + attr_reader :project, :selected_tab + end + end + end +end diff --git a/modules/backlogs/app/components/projects/settings/backlogs/sharing_form_component.html.erb b/modules/backlogs/app/components/projects/settings/backlogs/sharing_form_component.html.erb new file mode 100644 index 00000000000..0e5b2c1050a --- /dev/null +++ b/modules/backlogs/app/components/projects/settings/backlogs/sharing_form_component.html.erb @@ -0,0 +1,45 @@ +<%#-- 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. + +++#%> + +<%= + render(Primer::Beta::Text.new(tag: :div, color: :muted, mb: 3)) do + I18n.t("backlogs.sharing_description") + end +%> + +<%= + settings_primer_form_with( + model: project, + url: project_settings_backlog_sharing_path(project), + method: :patch, + data: { turbo_frame: "_top", controller: "show-when-value-selected" } + ) do |f| + render(Projects::Settings::Backlogs::SharingForm.new(f)) + end +%> diff --git a/modules/backlogs/app/components/projects/settings/backlogs/sharing_form_component.rb b/modules/backlogs/app/components/projects/settings/backlogs/sharing_form_component.rb new file mode 100644 index 00000000000..94847d32bca --- /dev/null +++ b/modules/backlogs/app/components/projects/settings/backlogs/sharing_form_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 Projects + module Settings + module Backlogs + class SharingFormComponent < ApplicationComponent + include ApplicationHelper + include OpPrimer::ComponentHelpers + + def initialize(project:) + super + + @project = project + end + + private + + attr_reader :project + end + end + end +end diff --git a/modules/backlogs/app/contracts/projects/backlog_settings_contract.rb b/modules/backlogs/app/contracts/projects/backlog_settings_contract.rb new file mode 100644 index 00000000000..ff6ee065517 --- /dev/null +++ b/modules/backlogs/app/contracts/projects/backlog_settings_contract.rb @@ -0,0 +1,68 @@ +# 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 Projects + class BacklogSettingsContract < ::ModelContract + # The sprint_sharing setting is stored on the settings column. Hence writing + # the settings column should be allowed, but only for the sprint_sharing setting. + attribute :settings, writable: -> do + model.settings_change&.none? { it.except("sprint_sharing").any? } + end + attribute :sprint_sharing + + validate :validate_permissions + validate :validate_global_sprint_sharer_uniqueness + validates :sprint_sharing, presence: true + validates :sprint_sharing, inclusion: { in: Project::SPRINT_SHARING_MODES }, allow_blank: true + + def validate_model? = false + + protected + + def validate_permissions + unless user.allowed_in_project?(:share_sprint, model) + errors.add :base, :error_unauthorized + end + end + + def validate_global_sprint_sharer_uniqueness + if model.share_sprints_with_all_projects? && + (sharer = Project.global_sprint_sharer) && + sharer != model + + if user.allowed_in_project?(:view_project, sharer) + errors.add :sprint_sharing, :share_all_projects_already_taken, name: sharer.name + else + errors.add :sprint_sharing, :share_all_projects_already_taken_anonymous + end + end + end + end +end diff --git a/modules/backlogs/app/contracts/sprints/base_contract.rb b/modules/backlogs/app/contracts/sprints/base_contract.rb new file mode 100644 index 00000000000..cf014093c71 --- /dev/null +++ b/modules/backlogs/app/contracts/sprints/base_contract.rb @@ -0,0 +1,42 @@ +# 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 Sprints + class BaseContract < ::ModelContract + def self.model + Agile::Sprint + end + + attribute :name + attribute :project_id + attribute :start_date + attribute :finish_date + end +end diff --git a/modules/backlogs/app/contracts/sprints/create_contract.rb b/modules/backlogs/app/contracts/sprints/create_contract.rb new file mode 100644 index 00000000000..c80ecedac04 --- /dev/null +++ b/modules/backlogs/app/contracts/sprints/create_contract.rb @@ -0,0 +1,45 @@ +# 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 Sprints + class CreateContract < BaseContract + validate :user_allowed_to_create + + private + + def user_allowed_to_create + return if model.project.nil? + + unless user.allowed_in_project?(:create_sprints, model.project) + errors.add :base, :error_unauthorized + end + end + end +end diff --git a/modules/backlogs/app/contracts/sprints/update_contract.rb b/modules/backlogs/app/contracts/sprints/update_contract.rb new file mode 100644 index 00000000000..e2f922d79a9 --- /dev/null +++ b/modules/backlogs/app/contracts/sprints/update_contract.rb @@ -0,0 +1,45 @@ +# 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 Sprints + class UpdateContract < BaseContract + validate :user_allowed_to_update + + private + + def user_allowed_to_update + return if model.project.nil? + + unless user.allowed_in_project?(:create_sprints, model.project) + errors.add :base, :error_unauthorized + end + end + end +end diff --git a/spec/lib/open_project/patches/carrierwave_sanitized_file_spec.rb b/modules/backlogs/app/controllers/projects/settings/backlog_sharings_controller.rb similarity index 55% rename from spec/lib/open_project/patches/carrierwave_sanitized_file_spec.rb rename to modules/backlogs/app/controllers/projects/settings/backlog_sharings_controller.rb index 14e6fb5d0b7..dc547958a58 100644 --- a/spec/lib/open_project/patches/carrierwave_sanitized_file_spec.rb +++ b/modules/backlogs/app/controllers/projects/settings/backlog_sharings_controller.rb @@ -27,33 +27,35 @@ # # See COPYRIGHT and LICENSE files for more details. #++ -require "spec_helper" -# Adapt the carrierwave sanitized file tests to the content type detector -RSpec.describe OpenProject::Patches::CarrierwaveSanitizedFile do - let(:file) { FileHelpers.mock_uploaded_file(name: "original-filename.txt") } +class Projects::Settings::BacklogSharingsController < Projects::SettingsController + menu_item :settings_backlogs - it "uses the first one when multiple mime types are given using a semicolon" do - allow(file).to receive(:content_type).and_return("image/png; text/html") + before_action :check_scrum_projects_feature_flag - sanitized_file = CarrierWave::SanitizedFile.new(file) + def show; end - expect(sanitized_file.content_type).to eq("image/png") + def update + call = Projects::UpdateService + .new(model: @project, user: current_user, contract_class: Projects::BacklogSettingsContract) + .call(backlog_settings_params) + + if call.success? + flash[:notice] = I18n.t(:notice_successful_update) + redirect_to project_settings_backlog_sharing_path(@project) + else + flash.now[:error] = I18n.t(:notice_unsuccessful_update_with_reason, reason: call.message) + render action: :show, status: :unprocessable_entity + end end - it "uses the first one when multiple mime types are given using a comma" do - allow(file).to receive(:content_type).and_return("image/png, text/html") + private - sanitized_file = CarrierWave::SanitizedFile.new(file) - - expect(sanitized_file.content_type).to eq("image/png") + def check_scrum_projects_feature_flag + render_404 unless OpenProject::FeatureDecisions.scrum_projects_active? end - it "drops content type parameters" do - allow(file).to receive(:content_type).and_return("text/html; charset=utf-8") - - sanitized_file = CarrierWave::SanitizedFile.new(file) - - expect(sanitized_file.content_type).to eq("text/html") + def backlog_settings_params + params.expect(project: %i[sprint_sharing]) end end diff --git a/modules/backlogs/app/controllers/projects/settings/backlogs_controller.rb b/modules/backlogs/app/controllers/projects/settings/backlogs_controller.rb index b53f8be524b..a59b335f055 100644 --- a/modules/backlogs/app/controllers/projects/settings/backlogs_controller.rb +++ b/modules/backlogs/app/controllers/projects/settings/backlogs_controller.rb @@ -31,17 +31,10 @@ class Projects::Settings::BacklogsController < Projects::SettingsController menu_item :settings_backlogs - def show - @statuses_done_for_project = @project.done_statuses.pluck(:id) - end + def show; end def update - selected_statuses = (params[:statuses] || []).filter_map do |work_package_status| - Status.find(work_package_status[:status_id].to_i) - end - - @project.done_statuses = selected_statuses - @project.save! + @project.update!(params.expect(project: { done_status_ids: [] })) flash[:notice] = I18n.t(:notice_successful_update) diff --git a/modules/backlogs/app/controllers/rb_application_controller.rb b/modules/backlogs/app/controllers/rb_application_controller.rb index 83906ae31a7..5efcf887455 100644 --- a/modules/backlogs/app/controllers/rb_application_controller.rb +++ b/modules/backlogs/app/controllers/rb_application_controller.rb @@ -41,7 +41,7 @@ class RbApplicationController < ApplicationController # Loads the project to be used by the authorize filter to determine if # User.current has permission to invoke the method in question. def load_sprint_and_project - @project = Project.visible.find(params[:project_id]) + load_project # because of strong params, we want to pluck this variable out right now, # otherwise it causes issues where we are doing `attributes=`. @@ -50,6 +50,10 @@ class RbApplicationController < ApplicationController end end + def load_project + @project = Project.visible.find(params[:project_id]) + end + def check_if_plugin_is_configured settings = Setting.plugin_openproject_backlogs if settings["story_types"].blank? || settings["task_type"].blank? diff --git a/modules/backlogs/app/controllers/rb_master_backlogs_controller.rb b/modules/backlogs/app/controllers/rb_master_backlogs_controller.rb index dd6838e553b..c9062c36dae 100644 --- a/modules/backlogs/app/controllers/rb_master_backlogs_controller.rb +++ b/modules/backlogs/app/controllers/rb_master_backlogs_controller.rb @@ -36,10 +36,20 @@ class RbMasterBacklogsController < RbApplicationController before_action :load_backlogs, only: :index def index - if turbo_frame_request? - render partial: "list", layout: false + if OpenProject::FeatureDecisions.scrum_projects_active? + # Feature flag is active, render the new views + if turbo_frame_request? + render partial: "agile_list", layout: false + else + render :agile_index + end else - render :index + # Feature flag is not active, render legacy views + if turbo_frame_request? # rubocop:disable Style/IfInsideElse + render partial: "list", layout: false + else + render :index + end end end @@ -48,7 +58,12 @@ class RbMasterBacklogsController < RbApplicationController render "work_packages/split_view", layout: false else load_backlogs - render :index + + if OpenProject::FeatureDecisions.scrum_projects_active? + render :agile_index + else + render :index + end end end @@ -58,6 +73,11 @@ class RbMasterBacklogsController < RbApplicationController def load_backlogs @owner_backlogs = Backlog.owner_backlogs(@project) - @sprint_backlogs = Backlog.sprint_backlogs(@project) + + if OpenProject::FeatureDecisions.scrum_projects_active? + @sprints = Agile::Sprint.for_project(@project).not_completed.order_by_date + else + @sprint_backlogs = Backlog.sprint_backlogs(@project) + end end end diff --git a/modules/backlogs/app/controllers/rb_sprints_controller.rb b/modules/backlogs/app/controllers/rb_sprints_controller.rb index 9107a003bfb..2978ea374b9 100644 --- a/modules/backlogs/app/controllers/rb_sprints_controller.rb +++ b/modules/backlogs/app/controllers/rb_sprints_controller.rb @@ -31,6 +31,81 @@ class RbSprintsController < RbApplicationController include OpTurbo::ComponentStream + NEW_SPRINT_ACTIONS = %i[new_dialog + edit_dialog + create + refresh_form + update_agile_sprint].freeze + + skip_before_action :load_sprint_and_project, only: NEW_SPRINT_ACTIONS + + before_action :not_authorized_on_feature_flag_inactive, + :load_project, + only: NEW_SPRINT_ACTIONS + + def new_dialog + call = Sprints::SetAttributesService.new( + user: current_user, + model: Agile::Sprint.new, + contract_class: EmptyContract + ).call(attributes: converted_agile_sprint_params) + + respond_with_dialog Backlogs::NewSprintDialogComponent.new(sprint: call.result) + end + + def edit_dialog + @sprint = Agile::Sprint.for_project(@project).visible.find(params[:id]) + + respond_with_dialog Backlogs::NewSprintDialogComponent.new(sprint: @sprint, state: :edit) + end + + def refresh_form + id = edit_agile_sprint_params.dig(:sprint, :id) + sprint = id.present? ? Agile::Sprint.for_project(@project).visible.find(id) : Agile::Sprint.new + + call = Sprints::SetAttributesService.new( + user: current_user, + model: sprint, + contract_class: EmptyContract + ).call(attributes: converted_agile_sprint_params) + + update_via_turbo_stream(component: Backlogs::NewSprintFormComponent.new(sprint: call.result)) + + respond_with_turbo_streams + end + + def create # rubocop:disable Metrics/AbcSize + call = Sprints::CreateService + .new(user: current_user) + .call(attributes: converted_agile_sprint_params) + + if call.success? + flash[:notice] = I18n.t(:notice_successful_create) + render turbo_stream: turbo_stream.redirect_to(backlogs_project_backlogs_path(@project)) + else + update_new_sprint_form_component_via_turbo_stream(sprint: call.result, base_errors: call.errors[:base]) + respond_with_turbo_streams + end + end + + # Called like this due to `update` being taken by legacy sprints. + def update_agile_sprint # rubocop:disable Metrics/AbcSize + @sprint = Agile::Sprint.for_project(@project).visible.find(params[:id]) + + call = Sprints::UpdateService + .new(user: current_user, model: @sprint) + .call(attributes: agile_sprint_params[:sprint]) + + if call.success? + render_success_flash_message_via_turbo_stream(message: I18n.t(:notice_successful_update)) + update_sprint_header_component_via_turbo_stream(sprint: call.result) + else + update_new_sprint_form_component_via_turbo_stream(sprint: call.result, base_errors: call.errors[:base]) + end + + respond_with_turbo_streams + end + def edit_name update_header_component_via_turbo_stream(state: :edit) respond_with_turbo_streams @@ -78,15 +153,50 @@ class RbSprintsController < RbApplicationController ) end + def update_sprint_header_component_via_turbo_stream(sprint:) + update_via_turbo_stream( + component: Backlogs::SprintHeaderComponent.new(sprint:), + method: :morph + ) + end + + def update_new_sprint_form_component_via_turbo_stream(sprint:, base_errors: nil) + update_via_turbo_stream( + component: Backlogs::NewSprintFormComponent.new( + sprint:, + base_errors: + ), + status: :bad_request + ) + end + # Overrides load_sprint_and_project to load the sprint from :id instead of :sprint_id def load_sprint_and_project @sprint = Sprint.visible.find(params[:id]) - @project = @sprint.project - # This overrides sprint's project if we set another project, say a subproject - @project = Project.visible.find(params[:project_id]) + load_project end def sprint_params params.expect(sprint: %i[name start_date effective_date]) end + + def agile_sprint_params + params.permit(sprint: %i[name start_date finish_date]) + end + + def edit_agile_sprint_params + params.permit(sprint: %i[id name start_date finish_date]) + end + + def converted_agile_sprint_params + # Do some preprocessing to make the params easier to use + converted_sprint_params = agile_sprint_params[:sprint].to_h + converted_sprint_params[:project] = @project + + converted_sprint_params + end + + def not_authorized_on_feature_flag_inactive + render_403 unless OpenProject::FeatureDecisions.scrum_projects_active? + end end diff --git a/modules/backlogs/app/controllers/rb_stories_controller.rb b/modules/backlogs/app/controllers/rb_stories_controller.rb index 6386fccd5af..c9ab6c288b2 100644 --- a/modules/backlogs/app/controllers/rb_stories_controller.rb +++ b/modules/backlogs/app/controllers/rb_stories_controller.rb @@ -31,36 +31,48 @@ class RbStoriesController < RbApplicationController include OpTurbo::ComponentStream - before_action :load_story + NEW_SPRINT_ACTIONS = %i[move].freeze - def move # rubocop:disable Metrics/AbcSize + skip_before_action :load_sprint_and_project, only: NEW_SPRINT_ACTIONS + + before_action :legacy_load_story, except: NEW_SPRINT_ACTIONS + prepend_before_action :load_sprint, :load_project, :load_story, only: NEW_SPRINT_ACTIONS + + # Move a story from a Sprint to another Sprint or an Agile::Sprint. + def move_legacy # The update service reloads the story internally (via #move_after), # so we memoize the previous version_id before the call. version_id_was = @story.version_id - call = Stories::UpdateService - .new(user: current_user, story: @story) - .call( - attributes: { version_id: move_params[:target_id] }, - position: move_params[:position].to_i - ) - - unless call.success? - render_error_flash_message_via_turbo_stream( - message: I18n.t(:notice_unsuccessful_update_with_reason, reason: call.message) - ) + move_attributes = infer_attributes_from_target + unless move_story(move_attributes).success? return respond_with_turbo_streams(status: :unprocessable_entity) end - replace_backlog_component_via_turbo_stream(sprint: @sprint) + if target_sprint?(move_attributes) + moved_to_sprint + elsif target_version?(move_attributes) && @story.version_id != version_id_was + moved_to_version + end - if @story.version_id != version_id_was - new_sprint = @story.version.becomes(Sprint) + respond_with_turbo_streams + end - render_success_flash_message_via_turbo_stream( - message: I18n.t(:notice_successful_move, from: @sprint.name, to: new_sprint.name) - ) - replace_backlog_component_via_turbo_stream(sprint: new_sprint) + # Move a story from an Agile::Sprint to another Agile::Sprint or a Sprint. + def move + # The update service reloads the story internally (via #move_after), + # so we memoize the previous sprint_id before the call. + sprint_id_was = @story.sprint_id + + move_attributes = infer_attributes_from_target + unless move_story(move_attributes).success? + return respond_with_turbo_streams(status: :unprocessable_entity) + end + + if target_version?(move_attributes) + moved_to_version + elsif target_sprint?(move_attributes) && @story.sprint_id != sprint_id_was + moved_to_sprint end respond_with_turbo_streams @@ -85,6 +97,84 @@ class RbStoriesController < RbApplicationController private + def move_story(move_attributes) + call = update_story_with_target_and_position(attributes: move_attributes) + + if call.success? + # Update source component so that the moved story disappears + replace_typed_component_via_turbo_stream(sprint: @sprint) + else + render_error_flash_message_via_turbo_stream( + message: I18n.t(:notice_unsuccessful_update_with_reason, reason: call.message) + ) + end + + call + end + + def update_story_with_target_and_position(attributes:) + Stories::UpdateService + .new(user: current_user, story: @story) + .call( + attributes:, + position: move_params[:position].to_i + ) + end + + def replace_typed_component_via_turbo_stream(sprint:) + if sprint.is_a?(Agile::Sprint) + replace_sprint_component_via_turbo_stream(sprint:) + else + replace_backlog_component_via_turbo_stream(sprint:) + end + end + + def moved_to_version + moved_to(new_sprint: @story.version.becomes(Sprint)) + end + + def moved_to_sprint + moved_to(new_sprint: @story.sprint.becomes(Agile::Sprint)) + end + + def moved_to(new_sprint:) + render_success_flash_message_via_turbo_stream( + message: I18n.t(:notice_successful_move, from: @sprint.name, to: new_sprint.name) + ) + + # Update the target component so that the moved story shows up + replace_typed_component_via_turbo_stream(sprint: new_sprint) + end + + def infer_attributes_from_target + target_type, target_id = move_params[:target_id].split(":") + + case target_type + when "version" + { version_id: target_id, sprint_id: nil } + when "sprint" + # If the story is assigned to a version, we will only nullify the version + # if it is used as a backlog. We will keep a "regular" version reference. + # Otherwise, moving a story to a sprint would delete it from any version it is + # assigned to. + if @story.version&.used_as_backlog? + { version_id: nil, sprint_id: target_id } + else + { sprint_id: target_id } + end + else + raise ArgumentError, "target_type must include one of: version, sprint." + end + end + + def target_version?(move_attributes) + move_attributes[:version_id].present? + end + + def target_sprint?(move_attributes) + move_attributes[:sprint_id].present? + end + def replace_backlog_component_via_turbo_stream(sprint:) @backlog = Backlog.for(sprint:, project: @project) replace_via_turbo_stream( @@ -93,10 +183,22 @@ class RbStoriesController < RbApplicationController ) end - def load_story + def replace_sprint_component_via_turbo_stream(sprint:) + replace_via_turbo_stream(component: Backlogs::SprintComponent.new(sprint: sprint)) + end + + def legacy_load_story @story = Story.visible.find(params[:id]) end + def load_story + @story = WorkPackage.visible.find(params[:id]) + end + + def load_sprint + @sprint = Agile::Sprint.for_project(@project).visible.find(params[:sprint_id]) + end + def move_params params.require(%i[position target_id]) params.permit(:position, :target_id) diff --git a/modules/backlogs/app/forms/backlogs/sprints/dates_form.rb b/modules/backlogs/app/forms/backlogs/sprints/dates_form.rb new file mode 100644 index 00000000000..8104e6929f3 --- /dev/null +++ b/modules/backlogs/app/forms/backlogs/sprints/dates_form.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 Backlogs + module Sprints + class DatesForm < ApplicationForm + form do |f| + f.group(layout: :horizontal) do |dates| + dates.text_field( + name: :start_date, + type: :date, + label: attribute_name(:start_date), + placeholder: attribute_name(:start_date), + required: true, + input_width: :small, + data: { + action: "change->refresh-on-form-changes#triggerTurboStream" + } + ) + dates.text_field( + name: :finish_date, + type: :date, + label: attribute_name(:finish_date), + placeholder: attribute_name(:finish_date), + required: true, + input_width: :small, + data: { + action: "change->refresh-on-form-changes#triggerTurboStream" + } + ) + dates.text_field( + name: :duration, + label: attribute_name(:duration), + input_width: :xsmall, + readonly: true, + value: display_duration + ) + end + end + + def display_duration + if model.duration.present? + [model.duration, I18n.t("datetime.units.day", count: model.duration)].join(" ") + else + "" + end + end + end + end +end diff --git a/modules/backlogs/app/forms/backlogs/sprints/details_form.rb b/modules/backlogs/app/forms/backlogs/sprints/details_form.rb new file mode 100644 index 00000000000..d49b9ccc3c2 --- /dev/null +++ b/modules/backlogs/app/forms/backlogs/sprints/details_form.rb @@ -0,0 +1,55 @@ +# 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 Backlogs + module Sprints + class DetailsForm < ApplicationForm + form do |f| + f.hidden(name: :id) + + f.text_field( + label: attribute_name(:name), + name: :name, + required: true, + autofocus: true, + w: :full + ) + + # f.text_area( + # label: attribute_name(:goal), + # name: :goal, + # required: false, + # w: :full, + # rows: 3 + # ) + end + end + end +end diff --git a/modules/backlogs/app/forms/projects/settings/backlogs/sharing_form.rb b/modules/backlogs/app/forms/projects/settings/backlogs/sharing_form.rb new file mode 100644 index 00000000000..38eac5f43c5 --- /dev/null +++ b/modules/backlogs/app/forms/projects/settings/backlogs/sharing_form.rb @@ -0,0 +1,123 @@ +# 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 Projects + module Settings + module Backlogs + class SharingForm < ApplicationForm + form do |sharing_form| + # TODO: Remove this hidden field, once the `radio_button_group` supports rendering + # the hidden empty field. + # The purpose of the hidden field is to ensure we submit the `sprint_sharing` field + # even if no radio button is chosen. Otherwise, the submitted form will not include + # the field at all and the save request will return success when in fact no setting + # is saved. + # Ideally the hidden field should automatically be rendered by the `radio_button_group` + # helper, similar to how the `collection_radio_buttons` rails helper does. + sharing_form.hidden(name: :sprint_sharing, value: "") + + sharing_form.radio_button_group( + name: :sprint_sharing, + label: I18n.t("projects.settings.backlog_sharing.sprint_sharing") + ) do |group| + Project::SPRINT_SHARING_MODES.each do |option| + group.radio_button( + label: sharing_option_text(option, :label), + value: option, + checked: checked?(option), + disabled: disabled?(option), + caption: caption_for(option), + data: { "show-when-value-selected-target": "cause" } + ) + end + end + + sharing_form.html_content { banner_for(Project::SHARE_SUBPROJECTS, type: :info) } + sharing_form.html_content { banner_for(Project::RECEIVE_SHARED, type: :warning) } + + sharing_form.submit( + name: :submit, + label: I18n.t("button_save"), + scheme: :primary + ) + end + + private + + def checked?(option) + option == model.sprint_sharing + end + + def disabled?(option) + option == Project::SHARE_ALL_PROJECTS && share_all_projects_disabled? + end + + def sharing_option_text(option, key, **) + I18n.t("projects.settings.backlog_sharing.options.#{option}.#{key}", **) + end + + def caption_for(option) + if disabled?(option) + if User.current.allowed_in_project?(:view_project, global_sprint_sharer) + sharing_option_text(option, :disabled_caption, name: global_sprint_sharer.name) + else + sharing_option_text(option, :disabled_caption_anonymous) + end + else + sharing_option_text(option, :caption) + end + end + + def share_all_projects_disabled? + global_sprint_sharer && global_sprint_sharer != model + end + + def global_sprint_sharer + @global_sprint_sharer ||= Project.global_sprint_sharer + end + + def banner_for(option, type: :info) + banner_arguments = + type == :warning ? { scheme: :warning } : { icon: :info } + + render(Primer::BaseComponent.new( + tag: :div, + hidden: model.sprint_sharing != option, + data: { value: option, "show-when-value-selected-target": "effect" } + )) do + render(Primer::Alpha::Banner.new(**banner_arguments)) do + sharing_option_text(option, type) + end + end + end + end + end + end +end diff --git a/modules/backlogs/app/forms/projects/settings/backlogs_settings_form.rb b/modules/backlogs/app/forms/projects/settings/backlogs_settings_form.rb new file mode 100644 index 00000000000..316380b7529 --- /dev/null +++ b/modules/backlogs/app/forms/projects/settings/backlogs_settings_form.rb @@ -0,0 +1,70 @@ +# 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 Projects + module Settings + class BacklogsSettingsForm < ApplicationForm + form do |f| + f.autocompleter( + name: :done_status_ids, + label: I18n.t(:"backlogs.definition_of_done"), + caption: I18n.t(:"backlogs.definition_of_done_caption"), + autocomplete_options: { + multiple: true, + closeOnSelect: false, + clearable: false, + decorated: true, + data: { + test_selector: "done_status_ids_autocomplete" + } + } + ) do |list| + available_statuses.each do |label, value| + active = value.in?(model.done_status_ids) + + list.option( + label:, + value:, + selected: active + ) + end + end + + f.submit(scheme: :primary, name: :apply, label: I18n.t(:button_save)) + end + + private + + def available_statuses + Status.pluck(:name, :id) + end + end + end +end diff --git a/modules/backlogs/app/helpers/rb_common_helper.rb b/modules/backlogs/app/helpers/rb_common_helper.rb index b28a5891b70..7b3dc947d54 100644 --- a/modules/backlogs/app/helpers/rb_common_helper.rb +++ b/modules/backlogs/app/helpers/rb_common_helper.rb @@ -132,6 +132,14 @@ module RbCommonHelper item.remaining_hours.blank? || item.remaining_hours == 0 ? "" : item.remaining_hours end + def scrum_projects_enabled? + OpenProject::FeatureDecisions.scrum_projects_active? + end + + def allow_sprint_creation?(project) + scrum_projects_enabled? && current_user.allowed_in_project?(:create_sprints, project) + end + private def work_package_status_for_id(id) diff --git a/modules/backlogs/app/models/agile/sprint.rb b/modules/backlogs/app/models/agile/sprint.rb index d236ca0bc74..cef4d7f6f34 100644 --- a/modules/backlogs/app/models/agile/sprint.rb +++ b/modules/backlogs/app/models/agile/sprint.rb @@ -35,9 +35,16 @@ module Agile class Sprint < ApplicationRecord self.table_name = "sprints" + include ::Scopes::Scoped + belongs_to :project has_many :work_packages, dependent: :nullify + scopes :for_project, + :not_completed, + :order_by_date, + :visible + enum :status, { in_planning: "in_planning", @@ -47,27 +54,25 @@ module Agile default: "in_planning", validate: true - enum :sharing, - { - none: "none", - descendants: "descendants", - system: "system" - }, - default: "none", - prefix: :sharing_with, - validate: true - validates :name, presence: true validates :project, presence: true validates :start_date, presence: true + validates :finish_date, presence: true validates :finish_date, - presence: true, - comparison: { greater_than_or_equal_to: :start_date } + comparison: { greater_than_or_equal_to: :start_date }, + if: :start_date? validate :validate_only_one_active_sprint_per_project - # TODO: validate sharing is set to an allowed value, e.g. only admins may share systemwide (#71374, #71253) - # TODO: implement sharing logic once it has been defined (#71374) + def date_range_set? + start_date? && finish_date? + end + + def duration + return nil unless date_range_set? + + Day.working.from_range(from: start_date, to: finish_date).count + end private diff --git a/modules/backlogs/app/models/agile/sprints/scopes/for_project.rb b/modules/backlogs/app/models/agile/sprints/scopes/for_project.rb new file mode 100644 index 00000000000..1b0a1c65d72 --- /dev/null +++ b/modules/backlogs/app/models/agile/sprints/scopes/for_project.rb @@ -0,0 +1,44 @@ +# 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 Agile::Sprints::Scopes::ForProject + extend ActiveSupport::Concern + + class_methods do + def for_project(project) + # Ideally the project.work_packages scope would be used, but unfortunately + # it has some extra includes that are not necessary in this case. + from_work_packages = WorkPackage.where(project:).where.not(sprint_id: nil) + + where(project: project.sprint_source) + .or(where(id: from_work_packages.select(:sprint_id))) + end + end +end diff --git a/modules/backlogs/app/models/agile/sprints/scopes/not_completed.rb b/modules/backlogs/app/models/agile/sprints/scopes/not_completed.rb new file mode 100644 index 00000000000..2e68e7f1230 --- /dev/null +++ b/modules/backlogs/app/models/agile/sprints/scopes/not_completed.rb @@ -0,0 +1,37 @@ +# 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 Agile::Sprints::Scopes::NotCompleted + extend ActiveSupport::Concern + + def not_completed # rubocop:disable Naming/PredicateMethod + !completed + end +end diff --git a/modules/backlogs/app/models/agile/sprints/scopes/order_by_date.rb b/modules/backlogs/app/models/agile/sprints/scopes/order_by_date.rb new file mode 100644 index 00000000000..b6aed82b9a1 --- /dev/null +++ b/modules/backlogs/app/models/agile/sprints/scopes/order_by_date.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. +#++ + +module Agile::Sprints::Scopes::OrderByDate + extend ActiveSupport::Concern + + class_methods do + def order_by_date + order(arel_table[:start_date].asc.nulls_last, + arel_table[:finish_date].asc.nulls_last) + end + end +end diff --git a/modules/backlogs/app/models/agile/sprints/scopes/visible.rb b/modules/backlogs/app/models/agile/sprints/scopes/visible.rb new file mode 100644 index 00000000000..b9d214d8680 --- /dev/null +++ b/modules/backlogs/app/models/agile/sprints/scopes/visible.rb @@ -0,0 +1,38 @@ +# 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 Agile::Sprints::Scopes::Visible + extend ActiveSupport::Concern + + class_methods do + # FIXME: replace this stub with a meaningful implementation. + def visible = all + end +end diff --git a/modules/backlogs/app/models/projects/scopes/not_sharing_sprints.rb b/modules/backlogs/app/models/projects/scopes/not_sharing_sprints.rb new file mode 100644 index 00000000000..38fab4571bd --- /dev/null +++ b/modules/backlogs/app/models/projects/scopes/not_sharing_sprints.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. +#++ + +module Projects::Scopes::NotSharingSprints + extend ActiveSupport::Concern + + class_methods do + def not_sharing_sprints + with_settings(sprint_sharing: Projects::SprintSharing::NO_SHARING) + .or(with_settings(sprint_sharing: "")) + .or(with_settings(sprint_sharing: nil)) + end + end +end diff --git a/modules/backlogs/app/models/projects/scopes/receive_shared_sprints.rb b/modules/backlogs/app/models/projects/scopes/receive_shared_sprints.rb new file mode 100644 index 00000000000..0e023d90da1 --- /dev/null +++ b/modules/backlogs/app/models/projects/scopes/receive_shared_sprints.rb @@ -0,0 +1,39 @@ +# 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 Projects::Scopes::ReceiveSharedSprints + extend ActiveSupport::Concern + + class_methods do + def receive_shared_sprints + with_settings(sprint_sharing: Projects::SprintSharing::RECEIVE_SHARED) + end + end +end diff --git a/modules/backlogs/app/models/projects/scopes/share_sprints_with_all_projects.rb b/modules/backlogs/app/models/projects/scopes/share_sprints_with_all_projects.rb new file mode 100644 index 00000000000..c0c1eb8503b --- /dev/null +++ b/modules/backlogs/app/models/projects/scopes/share_sprints_with_all_projects.rb @@ -0,0 +1,39 @@ +# 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 Projects::Scopes::ShareSprintsWithAllProjects + extend ActiveSupport::Concern + + class_methods do + def share_sprints_with_all_projects + with_settings(sprint_sharing: Projects::SprintSharing::SHARE_ALL_PROJECTS) + end + end +end diff --git a/modules/backlogs/app/models/projects/scopes/share_sprints_with_subprojects.rb b/modules/backlogs/app/models/projects/scopes/share_sprints_with_subprojects.rb new file mode 100644 index 00000000000..c4dce1be4c8 --- /dev/null +++ b/modules/backlogs/app/models/projects/scopes/share_sprints_with_subprojects.rb @@ -0,0 +1,39 @@ +# 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 Projects::Scopes::ShareSprintsWithSubprojects + extend ActiveSupport::Concern + + class_methods do + def share_sprints_with_subprojects + with_settings(sprint_sharing: Projects::SprintSharing::SHARE_SUBPROJECTS) + end + end +end diff --git a/modules/backlogs/app/models/projects/sprint_sharing.rb b/modules/backlogs/app/models/projects/sprint_sharing.rb new file mode 100644 index 00000000000..547c28808e1 --- /dev/null +++ b/modules/backlogs/app/models/projects/sprint_sharing.rb @@ -0,0 +1,108 @@ +# 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 Projects::SprintSharing + extend ActiveSupport::Concern + + NO_SHARING = "no_sharing" + SHARE_ALL_PROJECTS = "share_all_projects" + SHARE_SUBPROJECTS = "share_subprojects" + RECEIVE_SHARED = "receive_shared" + + SPRINT_SHARING_MODES = [NO_SHARING, SHARE_ALL_PROJECTS, SHARE_SUBPROJECTS, RECEIVE_SHARED].freeze + + included do + store_attribute :settings, :sprint_sharing, :string + + scopes :share_sprints_with_all_projects, + :share_sprints_with_subprojects, + :receive_shared_sprints, + :not_sharing_sprints + end + + class_methods do + def global_sprint_sharer + global_sprint_sharer_relation.first + end + + def global_sprint_sharer_relation + share_sprints_with_all_projects.active.limit(1) + end + end + + # `default:` cannot be reliably used on the store_attribute declaration, + # see config/initializers/store_attribute.rb for more details. + def sprint_sharing + super || NO_SHARING + end + + def share_sprints_with_all_projects? + sprint_sharing == SHARE_ALL_PROJECTS + end + + def share_sprints_with_subprojects? + sprint_sharing == SHARE_SUBPROJECTS + end + + def receive_shared_sprints? + sprint_sharing == RECEIVE_SHARED + end + + def not_sharing_sprints? + sprint_sharing == NO_SHARING + end + + def not_sharing_sprints! + return if not_sharing_sprints? + + update_column(:settings, settings.merge("sprint_sharing" => NO_SHARING)) + end + + def sprint_source + # Senders and non-sharing projects only see their own sprints. + # Receivers see external sprints from the closest ancestor sharing + # subprojects, falling back to the global sharer. + if receive_shared_sprints? + closest_sharing_ancestor_or_global_sharer + else + self.class.where(id:) + end + end + + private + + def closest_sharing_ancestor_or_global_sharer + closest_ancestor = ancestors.share_sprints_with_subprojects.reorder(lft: :desc).limit(1) + + self.class + .where(id: closest_ancestor).limit(1) # Both sides of `or` must be structurally identical + .or(self.class.global_sprint_sharer_relation.where.not(closest_ancestor.arel.exists)) + end +end diff --git a/modules/backlogs/app/seeders/common.yml b/modules/backlogs/app/seeders/common.yml index 1a0213b5753..f3146e87aec 100644 --- a/modules/backlogs/app/seeders/common.yml +++ b/modules/backlogs/app/seeders/common.yml @@ -30,9 +30,8 @@ modules_permissions: backlogs: - role: :default_role_member add: - - :view_master_backlog - - :view_taskboards + - :view_sprints + - :manage_sprint_items - role: :default_role_reader add: - - :view_master_backlog - - :view_taskboards + - :view_sprints diff --git a/modules/backlogs/app/services/sprints/create_service.rb b/modules/backlogs/app/services/sprints/create_service.rb new file mode 100644 index 00000000000..0feb2e40ad2 --- /dev/null +++ b/modules/backlogs/app/services/sprints/create_service.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 Sprints::CreateService < BaseServices::Create + def instance_class + Agile::Sprint + end +end diff --git a/modules/backlogs/app/services/sprints/set_attributes_service.rb b/modules/backlogs/app/services/sprints/set_attributes_service.rb new file mode 100644 index 00000000000..2ed6d5974c2 --- /dev/null +++ b/modules/backlogs/app/services/sprints/set_attributes_service.rb @@ -0,0 +1,73 @@ +# 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 Sprints + class SetAttributesService < ::BaseServices::SetAttributes + private + + def sprint_name_from_predecessor + return model.name unless model.new_record? + + predecessor = model.project.sprints.last + next_name_in_succession(predecessor) + end + + def set_default_attributes(_params) + set_sprint_name + set_default_status + end + + def set_sprint_name + model.name ||= sprint_name_from_predecessor + end + + def set_default_status + model.status ||= "in_planning" + end + + def next_name_in_succession(predecessor) + if predecessor.nil? + default_sprint_name + elsif (match = predecessor.name.match(/\A(.*)\s(\d+)\z/)) + # If the predecessor's name ends with a number, increment that number for the new sprint's name. + # E.g., if the previous sprint was called "Be ambitious 42", the next one will be "Be ambitious 43". + [match[1], match[2].to_i + 1].join(" ") + else + # The predecessor's name doesn't end with a number. The user has chosen a custom name. Do not assume + # how the next sprint should be called. Return an empty string and let the user choose. + "" + end + end + + def default_sprint_name + [I18n.t("activerecord.models.sprint"), 1].join(" ") + end + end +end diff --git a/modules/backlogs/app/services/sprints/update_service.rb b/modules/backlogs/app/services/sprints/update_service.rb new file mode 100644 index 00000000000..d6f618cc6e1 --- /dev/null +++ b/modules/backlogs/app/services/sprints/update_service.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 Sprints::UpdateService < BaseServices::Update + def instance_class + Agile::Sprint + end +end diff --git a/modules/backlogs/app/views/projects/settings/backlog_sharings/show.html.erb b/modules/backlogs/app/views/projects/settings/backlog_sharings/show.html.erb new file mode 100644 index 00000000000..9bd54844005 --- /dev/null +++ b/modules/backlogs/app/views/projects/settings/backlog_sharings/show.html.erb @@ -0,0 +1,39 @@ +<%#-- 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. + +++#%> + +<%= turbo_frame_tag "backlogs-settings" do %> + <%= render( + Projects::Settings::Backlogs::SettingsHeaderComponent.new( + project: @project, + selected_tab: :sharing + ) + ) %> + + <%= render(Projects::Settings::Backlogs::SharingFormComponent.new(project: @project)) %> +<% end %> diff --git a/modules/backlogs/app/views/projects/settings/backlogs/show.html.erb b/modules/backlogs/app/views/projects/settings/backlogs/show.html.erb index bb32c332dbf..9b6c15a85f3 100644 --- a/modules/backlogs/app/views/projects/settings/backlogs/show.html.erb +++ b/modules/backlogs/app/views/projects/settings/backlogs/show.html.erb @@ -27,77 +27,22 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= - render Primer::OpenProject::PageHeader.new do |header| - header.with_title { t("backlogs.definition_of_done") } - header.with_breadcrumbs( - [{ href: project_overview_path(@project), text: @project.name }, - { href: project_settings_general_path(@project), text: I18n.t(:label_project_settings) }, - t("backlogs.definition_of_done")] - ) - end -%> +<%= turbo_frame_tag "backlogs-settings" do %> + <%= render( + Projects::Settings::Backlogs::SettingsHeaderComponent.new( + project: @project, + selected_tab: :done_status + ) + ) %> -<%= styled_form_tag( - project_settings_backlogs_path(@project), + <%= + settings_primer_form_with( + url: project_settings_backlogs_path(@project), + model: @project, method: :patch, - id: "edit_project_#{@project.id}" - ) do %> - -

-
- - - - - - - - - - - - - <% for status in (Status.all || []) %> - - - - - <% end %> - -
-
-
- - <%= Status.model_name.human %> - -
-
-
-
-
- - <%= t("backlogs.work_package_is_closed") %> - -
-
-
- <%= status.name %> - - <% checkbox_id = status.name.parameterize.underscore %> - <%= styled_label_tag checkbox_id, t("backlogs.label_is_done_status", status_name: status.name), class: "sr-only" %> - <%= (styled_check_box_tag "statuses[][status_id]", status.id.to_s, @statuses_done_for_project.include?(status.id), id: checkbox_id) %> -
-
-
-
- <%= styled_button_tag t(:button_save), class: "-primary -with-icon icon-checkmark" %> -
- -<% end %> - -

<%= t("backlogs.rebuild_positions") %>

- -<%= styled_form_tag(controller: "/projects/settings/backlogs", action: "rebuild_positions", id: @project) do %> -

<%= styled_button_tag t("backlogs.rebuild"), class: "-primary" %>

+ data: { turbo_frame: "_top" } + ) do |f| + %> + <%= render Projects::Settings::BacklogsSettingsForm.new(f) %> + <% end %> <% end %> diff --git a/modules/backlogs/app/views/rb_master_backlogs/_agile_list.html.erb b/modules/backlogs/app/views/rb_master_backlogs/_agile_list.html.erb new file mode 100644 index 00000000000..412df5cf8ff --- /dev/null +++ b/modules/backlogs/app/views/rb_master_backlogs/_agile_list.html.erb @@ -0,0 +1,52 @@ +<%# -- 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. + +++# %> + + + <% if @owner_backlogs.empty? && @sprints.empty? %> + <%= + render(Primer::Beta::Blankslate.new(border: true, spacious: true)) do |blankslate| + blankslate.with_visual_icon(icon: :versions) + blankslate.with_heading(tag: :h2).with_content(t(:backlogs_empty_title)) + + if current_user.allowed_in_project?(:manage_versions, @project) + blankslate.with_description_content(t(:backlogs_empty_action_text)) + end + end + %> + <% else %> +
+
+ <%= render(Backlogs::SprintComponent.with_collection(@sprints)) %> +
+
+ <%= render(Backlogs::BacklogComponent.with_collection(@owner_backlogs, project: @project)) %> +
+
+ <% end %> +
diff --git a/modules/backlogs/app/views/rb_master_backlogs/_list.html.erb b/modules/backlogs/app/views/rb_master_backlogs/_list.html.erb index ecc41d09447..78341f2997c 100644 --- a/modules/backlogs/app/views/rb_master_backlogs/_list.html.erb +++ b/modules/backlogs/app/views/rb_master_backlogs/_list.html.erb @@ -1,3 +1,32 @@ +<%# -- 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. + +++# %> + <%= turbo_frame_tag :backlogs_container, refresh: :morph, class: "op-backlogs-page" do %> <% if @owner_backlogs.empty? && @sprint_backlogs.empty? %> <%= @@ -5,7 +34,7 @@ blankslate.with_visual_icon(icon: :versions) blankslate.with_heading(tag: :h2).with_content(t(:backlogs_empty_title)) - if current_user.allowed_in_project?(:manage_versions, @project) + if current_user.allowed_in_project?(:create_sprints, @project) blankslate.with_description_content(t(:backlogs_empty_action_text)) end end diff --git a/modules/backlogs/app/views/rb_master_backlogs/agile_index.html.erb b/modules/backlogs/app/views/rb_master_backlogs/agile_index.html.erb new file mode 100644 index 00000000000..16194aa9c77 --- /dev/null +++ b/modules/backlogs/app/views/rb_master_backlogs/agile_index.html.erb @@ -0,0 +1,95 @@ +<%#-- 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. + +++#%> + +<% html_title t(:label_backlogs) %> + +<% content_controller "backlogs", + "backlogs-list-url-value": backlogs_project_backlogs_path(@project), + "backlogs-backlogs--story-outlet": "li[data-story]" %> + +<% content_for :content_header do %> + <%= + render Primer::OpenProject::PageHeader.new do |header| + header.with_title { t(:label_backlogs) } + header.with_breadcrumbs( + [{ href: project_overview_path(@project), text: @project.name }, + t(:label_backlogs)] + ) + end + %> + + <%= + render(Primer::OpenProject::SubHeader.new) do |subheader| + if allow_sprint_creation?(@project) + # Scrum sprints are available, show an action menu to create Sprints and Versions + subheader.with_action_menu( + leading_icon: :plus, + trailing_icon: :"triangle-down", + label: t(:button_create), + button_arguments: { scheme: :primary } + ) do |menu| + menu.with_item( + label: Version.human_model_name, + href: new_project_version_path(@project) + ) + + menu.with_item( + label: Agile::Sprint.human_model_name, + href: new_dialog_project_sprints_path(@project), + content_arguments: { + data: { + controller: "async-dialog", + test_selector: "op-sprints--new-sprint-button" + } + } + ) + end + else + # No scrum sprints, we only show a "+ Version" button + subheader.with_action_button( + scheme: :primary, + leading_icon: :plus, + label: I18n.t(:label_version_new), + tag: :a, + href: new_project_version_path(@project) + ) do + Version.human_model_name + end + end + end + %> +<% end %> + +<% content_for :content_body do %> + <%= render partial: "agile_list" %> +<% end %> + +<% content_for :content_body_right do %> + <%= render(split_view_instance) if render_work_package_split_view? %> +<% end %> diff --git a/modules/backlogs/app/views/rb_master_backlogs/index.html.erb b/modules/backlogs/app/views/rb_master_backlogs/index.html.erb index f35c81f31a6..f6d59c4d4a7 100644 --- a/modules/backlogs/app/views/rb_master_backlogs/index.html.erb +++ b/modules/backlogs/app/views/rb_master_backlogs/index.html.erb @@ -43,17 +43,46 @@ See COPYRIGHT and LICENSE files for more details. end %> - <%= render(Primer::OpenProject::SubHeader.new) do |subheader| + <%= + render(Primer::OpenProject::SubHeader.new) do |subheader| + if allow_sprint_creation?(@project) + # Scrum sprints are available, show an action menu to create Sprints and Versions + subheader.with_action_menu( + leading_icon: :plus, + trailing_icon: :"triangle-down", + label: t(:button_create), + button_arguments: { scheme: :primary } + ) do |menu| + menu.with_item( + label: Version.human_model_name, + href: new_project_version_path(@project) + ) + + menu.with_item( + label: Agile::Sprint.human_model_name, + href: new_dialog_project_sprints_path(@project), + content_arguments: { + data: { + controller: "async-dialog", + test_selector: "op-sprints--new-sprint-button" + } + } + ) + end + else + # No scrum sprints, we only show a "+ Version" button subheader.with_action_button( scheme: :primary, leading_icon: :plus, label: I18n.t(:label_version_new), tag: :a, - href: url_for({ controller: "/versions", action: "new", project_id: @project }) + href: new_project_version_path(@project) ) do - t("activerecord.models.version") + Version.human_model_name end - end %> + end + end + %> <% end %> <% content_for :content_body do %> diff --git a/modules/backlogs/config/locales/crowdin/af.yml b/modules/backlogs/config/locales/crowdin/af.yml index 20d24018589..1e362abd74c 100644 --- a/modules/backlogs/config/locales/crowdin/af.yml +++ b/modules/backlogs/config/locales/crowdin/af.yml @@ -25,6 +25,14 @@ af: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ af: backlogs_work_package_type: "Agterstand tipe" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ af: task_version_must_be_the_same_as_story_version: "moet dieselfde wees as die ouer storie se weergawe" sprint: cannot_end_before_it_starts: "sprint kan nie uinding voor dit begin het nie" + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "eenige" column_width: "Column width" definition_of_done: "Definisie van Gedoen" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Belemmering" label_versions_default_fold_state: "Wys weergawe gevou" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ af: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ af: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ af: label_backlogs_unconfigured: "Jy het nog nie Agterstandes opgestel nie. Gaan asseblief na %{administration} > %{plugins}, klik dan op die %{configure}-skakel vir hierdie plugin. Sodra jy die velde gestel het, kom terug na hierdie bladsy om die instrument te begin gebruik." label_blocks_ids: "ID's van geblokkeerde werkspakkette" label_column_in_backlog: "Kolom in agterstand" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Af" label_points_burn_up: "Op" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint belemmerings" + label_sprint_new: "New sprint" label_task_board: "Taak bord" - permission_view_master_backlog: "Kyk na meester-agterstand" - permission_view_taskboards: "Kyk na taakborde" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Kies gedoen statusse" - permission_update_sprints: "Opdateer sprinte" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Agterstandes" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ar.yml b/modules/backlogs/config/locales/crowdin/ar.yml index a00c06b69cd..e2de45637d6 100644 --- a/modules/backlogs/config/locales/crowdin/ar.yml +++ b/modules/backlogs/config/locales/crowdin/ar.yml @@ -25,6 +25,14 @@ ar: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ar: backlogs_work_package_type: "نوع الأعمال المتراكمة غير المنجزة" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ar: task_version_must_be_the_same_as_story_version: "يجب أن يكون نفس نسخة القصة الأصلية." sprint: cannot_end_before_it_starts: "لا يمكن أن ينتهي السباق قبل أن يبدأ." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "أي" column_width: "Column width" definition_of_done: "تعريف ما تم" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "عائق" label_versions_default_fold_state: "إظهار الإصدارات مطوية" caption_versions_default_fold_state: "" @@ -83,12 +102,21 @@ ar: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -99,6 +127,14 @@ ar: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -126,15 +162,41 @@ ar: label_backlogs_unconfigured: "لم تقم بإنشاء الأعمال المتراكمة غير المنجزة بعد. من فضلك اذهب إلى %{administration} > %{plugins}، ثم اضغط على رابط %{configure} لهذا البرنامج المساعد. عندما تنتهي من تعيين الحقول، ارجع إلى هذه الصفحة لتبدأ باستخدام الأداة." label_blocks_ids: "الهويات المعرِّفة لمجموعات العمل المحظورة" label_column_in_backlog: "عمود في العمل المتراكم غير المنجز" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "الأسفل" label_points_burn_up: "الأعلى" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "عوائق السباق" + label_sprint_new: "New sprint" label_task_board: "لوحة المهمة" - permission_view_master_backlog: "عرض العمل الرئيسي المتراكم غير المنجز" - permission_view_taskboards: "شاهد لوحات المهمات" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "حدد حالات الاتمام" - permission_update_sprints: "قم بتحديث السباقات" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "الأعمال المتراكمة غير المنجزة" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/az.yml b/modules/backlogs/config/locales/crowdin/az.yml index 309b5b19a3f..5279a18085e 100644 --- a/modules/backlogs/config/locales/crowdin/az.yml +++ b/modules/backlogs/config/locales/crowdin/az.yml @@ -25,6 +25,14 @@ az: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ az: backlogs_work_package_type: "" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ az: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ az: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ az: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ az: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/be.yml b/modules/backlogs/config/locales/crowdin/be.yml index 12c334f48a8..c80bdab0446 100644 --- a/modules/backlogs/config/locales/crowdin/be.yml +++ b/modules/backlogs/config/locales/crowdin/be.yml @@ -25,6 +25,14 @@ be: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ be: backlogs_work_package_type: "Тып бэклогу" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ be: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "любы" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -79,12 +98,21 @@ be: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -95,6 +123,14 @@ be: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -122,15 +158,41 @@ be: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/bg.yml b/modules/backlogs/config/locales/crowdin/bg.yml index e16dce07c86..1ec8a64ba55 100644 --- a/modules/backlogs/config/locales/crowdin/bg.yml +++ b/modules/backlogs/config/locales/crowdin/bg.yml @@ -25,6 +25,14 @@ bg: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ bg: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ bg: task_version_must_be_the_same_as_story_version: "трябва да бъде същата като версията на родителската история." sprint: cannot_end_before_it_starts: "Спринтът не може да приключи, преди да започне." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "всяко" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Препядствие" label_versions_default_fold_state: "Показване на сгънати версиите " caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ bg: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ bg: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ bg: label_backlogs_unconfigured: "Все още не сте конфигурирали Неизпълнени задачи. Моля, отидете на %{administration} > %{plugins}, след което щракнете върху връзката %{configure} за този плъгин. След като зададете полетата, върнете се на тази страница, за да започнете да използвате инструмента." label_blocks_ids: "ID на блокирани работни пакети" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Надолу" label_points_burn_up: "Нагоре" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Пречки за спринт" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Изберете готови състояния" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Назад" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ca.yml b/modules/backlogs/config/locales/crowdin/ca.yml index 4d52aad8369..b88ca17f7e3 100644 --- a/modules/backlogs/config/locales/crowdin/ca.yml +++ b/modules/backlogs/config/locales/crowdin/ca.yml @@ -25,6 +25,14 @@ ca: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ca: backlogs_work_package_type: "Tipus de backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ca: task_version_must_be_the_same_as_story_version: "ha de ser la mateixa que la versió de la història pare." sprint: cannot_end_before_it_starts: "El sprint no pot acabar abans de que comenci." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "qualsevol" column_width: "Column width" definition_of_done: "Definició de fet" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Mostra les versions contretes" caption_versions_default_fold_state: "Les versions no s'expandiran per defecte quan es visualitzin els registres enrere. Cada un ha d'ampliar-se manualment." @@ -75,12 +94,21 @@ ca: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ ca: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ ca: label_backlogs_unconfigured: "No has configurat les llistes de pendents encara. Si us plau, ves a %{administration} > %{plugins}, a continuació, fes clic a l'enllaç de %{configure} per a aquest plugin. Una vegada hagis omplert els camps, torna a aquesta pàgina per començar a utilitzar l'eina." label_blocks_ids: "Identificadors dels paquets de treball bloquejats" label_column_in_backlog: "Columna al backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "A baix" label_points_burn_up: "Amunt" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Impediments de sprint" + label_sprint_new: "New sprint" label_task_board: "Tauler de tasques" - permission_view_master_backlog: "Visualitza el backlog mestre" - permission_view_taskboards: "Visualitza els taulers de tasques" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Selecciona els estats acabats" - permission_update_sprints: "Actualitza els sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ckb-IR.yml b/modules/backlogs/config/locales/crowdin/ckb-IR.yml index 3bc24142a22..19639bd81e4 100644 --- a/modules/backlogs/config/locales/crowdin/ckb-IR.yml +++ b/modules/backlogs/config/locales/crowdin/ckb-IR.yml @@ -25,6 +25,14 @@ ckb-IR: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ckb-IR: backlogs_work_package_type: "جۆری Backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ckb-IR: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ ckb-IR: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ ckb-IR: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ ckb-IR: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/cs.yml b/modules/backlogs/config/locales/crowdin/cs.yml index f5a7fbc7a36..1ec2ddc71ff 100644 --- a/modules/backlogs/config/locales/crowdin/cs.yml +++ b/modules/backlogs/config/locales/crowdin/cs.yml @@ -25,6 +25,14 @@ cs: description: "Tento modul přidává funkce umožňující agilním týmům pracovat s OpenProject v Scrum projektech." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Doba trvání sprintu" work_package: @@ -33,6 +41,11 @@ cs: backlogs_work_package_type: "Typ nevyřízené položky" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ cs: task_version_must_be_the_same_as_story_version: "musí být stejná jako verze nadřazeného příběhu." sprint: cannot_end_before_it_starts: "Sprint nemůže končit dříve, než začne." + models: + sprint: "Sprint" attributes: task_type: "Typ úlohy" backlogs: any: "jakákoliv" column_width: "Column width" definition_of_done: "Definice dokončena" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: " Verze zobrazit srolovaně" caption_versions_default_fold_state: "Verze se při prohlížení nevyřízených žádostí ve výchozím nastavení nerozbalují. Každou z nich je třeba rozbalit ručně." @@ -79,12 +98,21 @@ cs: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -95,6 +123,14 @@ cs: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -122,15 +158,41 @@ cs: label_backlogs_unconfigured: "Zatím nemáte nakonfigurované nevyřízené záznamy. Přejděte na %{administration} > %{plugins}a poté klikněte na odkaz %{configure} pro tento plugin. Jakmile nastavíte pole, vraťte se na tuto stránku a začněte používat nástroj." label_blocks_ids: "ID blokovaných pracovních balíčků" label_column_in_backlog: "Sloupec v nevyřízené pozici" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Dolů" label_points_burn_up: "Nahoru" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Běh impedimenty" + label_sprint_new: "New sprint" label_task_board: "Tabule úkolů" - permission_view_master_backlog: "Zobrazit hlavní nevyřízené položky" - permission_view_taskboards: "Zobrazit tabuly úkolů" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Vybrat stavy dokončených" - permission_update_sprints: "Aktualizovat běhy" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Nevyřízené položky" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/da.yml b/modules/backlogs/config/locales/crowdin/da.yml index b903386f5d8..25c50c99587 100644 --- a/modules/backlogs/config/locales/crowdin/da.yml +++ b/modules/backlogs/config/locales/crowdin/da.yml @@ -25,6 +25,14 @@ da: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ da: backlogs_work_package_type: "Backlog-type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ da: task_version_must_be_the_same_as_story_version: "skal være identisk med hovedhistorieversionen." sprint: cannot_end_before_it_starts: "Sprint kan ikke afsluttes før det starter." + models: + sprint: "Sprint" attributes: task_type: "Opgavetype" backlogs: any: "alle" column_width: "Column width" definition_of_done: "Definition af Udført" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Hindring" label_versions_default_fold_state: "Vis versioner sammenfoldet" caption_versions_default_fold_state: "Versioner vil ikke blive udvidet som standard, når man ser på backlogs. Hver enkelt version skal udvides manuelt." @@ -75,12 +94,21 @@ da: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ da: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ da: label_backlogs_unconfigured: "Man har ikke opsat Backlogs endnu. Gå til %{administration} > %{plugins}, og klik dernæst på linket %{configure} til dette plugin. Når man har indstillet feltindhold, vend tilbage til denne side for at begynde at bruge værktøjet." label_blocks_ids: "ID'er for blokerede arbejdspakker" label_column_in_backlog: "Kolonne i backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Ned" label_points_burn_up: "Op" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint-hindringer" + label_sprint_new: "New sprint" label_task_board: "Opgaveoversigt" - permission_view_master_backlog: "Se hoved-backlog" - permission_view_taskboards: "Vis opgaveoversigter" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Vælg udført-statusser" - permission_update_sprints: "Opdatere sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/de.yml b/modules/backlogs/config/locales/crowdin/de.yml index ef0b991f0af..7f354321296 100644 --- a/modules/backlogs/config/locales/crowdin/de.yml +++ b/modules/backlogs/config/locales/crowdin/de.yml @@ -25,6 +25,14 @@ de: description: "Dieses Modul fügt Funktionen hinzu, die es agilen Teams ermöglichen, mit OpenProject in Scrum-Projekten zu arbeiten." activerecord: attributes: + agile/sprint: + duration: "Dauer" + finish_date: "Endtermin" + goal: "Sprint-Ziel" + name: "Sprint-Name" + sharing: "Teilen" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint-Dauer" work_package: @@ -33,6 +41,11 @@ de: backlogs_work_package_type: "Backlog Typ" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ de: task_version_must_be_the_same_as_story_version: "muss der Version der übergeordneten Story entsprechen." sprint: cannot_end_before_it_starts: "Sprint kann nicht enden, bevor er begonnen hat." + models: + sprint: "Sprint" attributes: task_type: "Aufgaben-Typ" backlogs: any: "beliebig" column_width: "Spaltenbreite" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Hindernis" label_versions_default_fold_state: "Versionen eingeklappt anzeigen" caption_versions_default_fold_state: "Versionen werden beim Anzeigen des Backlogs standardmäßig nicht aufgeklappt. Sie müssen manuell geöffnet werden." @@ -75,12 +94,21 @@ de: backlog_component: blankslate_title: "%{name} ist leer" blankslate_description: "Noch keine Arbeitspakete geplant. Ziehen Sie Arbeitspakete hierher, um sie hinzuzufügen." + sprint_component: + blankslate_title: "%{name} ist leer" + blankslate_description: "Noch keine Arbeitspakete geplant. Ziehen Sie Arbeitspakete hierher, um sie hinzuzufügen." backlog_header_component: label_toggle_backlog: "%{name} ein-/ausklappen" label_story_count: zero: "Keine Arbeitspakete im Backlog" one: "%{count} Arbeitspakete im Backlog" other: "%{count} Geschichten im Backlog" + sprint_header_component: + label_toggle_backlog: "%{name} ein-/ausklappen" + label_story_count: + zero: "Keine Stories im Sprint" + one: "%{count} Stories im Sprint" + other: "%{count} Stories im Sprint" backlog_menu_component: label_actions: "Backlog-Aktionen" action_menu: @@ -91,6 +119,14 @@ de: burndown_chart: "Burndown-Diagramm" wiki: "Wiki" properties: "Eigenschaften" + sprint_menu_component: + label_actions: "Sprint-Aktionen" + action_menu: + edit_sprint: "Sprint bearbeiten" + new_story: "Neue Story" + stories_tasks: "Stories/Aufgaben" + task_board: "Taskboard" + burndown_chart: "Burndown-Diagramm" story_component: label_drag_story: "%{name} verschieben" story_menu_component: @@ -118,15 +154,41 @@ de: label_backlogs_unconfigured: "Sie haben noch keine Backlogs konfiguriert. Bitte gehen Sie auf %{administration} > %{plugins}, klicken Sie dann auf den %{configure} Link für dieses Plugin. Kommen Sie hierher zurück, sobald sie die Felder konfiguriert haben." label_blocks_ids: "IDs der blockierten Arbeitspakete" label_column_in_backlog: "Spalte im Backlog" + label_used_as_backlog: "Als Backlog verwendet" label_points_burn_down: "Runter" label_points_burn_up: "Hoch" + label_sprint_edit: "Sprint bearbeiten" label_sprint_impediments: "Sprint Hindernisse" + label_sprint_new: "Neuer Sprint" label_task_board: "Taskboard" - permission_view_master_backlog: "Master Backlog ansehen" - permission_view_taskboards: "Taskboard ansehen" + permission_create_sprints: "Sprints erstellen" + permission_manage_sprint_items: "Sprint-Elemente verwalten" permission_select_done_statuses: "Abgeschlossene Status auswählen" - permission_update_sprints: "Sprints bearbeiten" + permission_share_sprint: "Sprint teilen" + permission_start_complete_sprint: "Sprint starten/abschließen" + permission_view_sprints: "Sprints ansehen" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Keine Burndown-Daten verfügbar" diff --git a/modules/backlogs/config/locales/crowdin/el.yml b/modules/backlogs/config/locales/crowdin/el.yml index 53021e93381..a1770cd1ae8 100644 --- a/modules/backlogs/config/locales/crowdin/el.yml +++ b/modules/backlogs/config/locales/crowdin/el.yml @@ -25,6 +25,14 @@ el: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ el: backlogs_work_package_type: "Τύπος backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ el: task_version_must_be_the_same_as_story_version: "πρέπει να είναι το ίδιο με την έκδοση ιστορίας του γονέα." sprint: cannot_end_before_it_starts: "Το sprint δεν μπορεί να λήξει πριν αρχίσει." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "οποιoδήποτε" column_width: "Column width" definition_of_done: "Ορισμός των Ολοκληρωμένων" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Εμπόδιο" label_versions_default_fold_state: "Εμφάνιση συμπτυγμένων εκδόσεων" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ el: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ el: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ el: label_backlogs_unconfigured: "Δεν έχετε διαμορφώσει τα Backlogs ακόμη. Παρακαλούμε πηγαίνετε στο %{administration} > %{plugins}, έπειτα κάντε κλικ στον σύνδεσμο %{configure} για αυτό το πρόσθετο. Μόλις έχετε ορίσει τα πεδία, επιστρέψτε σε αυτή την σελίδα για να αρχίσετε να χρησιμοποιείτε το εργαλείο." label_blocks_ids: "Ταυτότητες μπλοκαρισμένων πακέτων εργασίας" label_column_in_backlog: "Στήλη στο backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Κάτω" label_points_burn_up: "Πάνω" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Εμπόδια Sprint" + label_sprint_new: "New sprint" label_task_board: "Πίνακας εργασιών" - permission_view_master_backlog: "Εμφάνιση του κύριου backlog" - permission_view_taskboards: "Εμφάνιση πινάκων εργασίας" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Ενημέρωση των sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/eo.yml b/modules/backlogs/config/locales/crowdin/eo.yml index 9823ba39aec..6a9fcac0830 100644 --- a/modules/backlogs/config/locales/crowdin/eo.yml +++ b/modules/backlogs/config/locales/crowdin/eo.yml @@ -25,6 +25,14 @@ eo: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ eo: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ eo: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "ajna" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ eo: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ eo: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ eo: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "ID de baritaj laborpakaĵoj" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Malsupren" label_points_burn_up: "Supren" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/es.yml b/modules/backlogs/config/locales/crowdin/es.yml index f019f9814a1..4acfd170279 100644 --- a/modules/backlogs/config/locales/crowdin/es.yml +++ b/modules/backlogs/config/locales/crowdin/es.yml @@ -25,6 +25,14 @@ es: description: "Este módulo añade funciones que permiten a los equipos Agile trabajar con OpenProject en proyectos Scrum." activerecord: attributes: + agile/sprint: + duration: "Duración" + finish_date: "Fecha de finalización" + goal: "Objetivo del sprint" + name: "Nombre del sprint" + sharing: "Uso compartido" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Duración del sprint" work_package: @@ -33,6 +41,11 @@ es: backlogs_work_package_type: "Tipo de Backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ es: task_version_must_be_the_same_as_story_version: "debe coincidir con la versión de la historia principal." sprint: cannot_end_before_it_starts: "El sprint no puede terminar antes del inicio." + models: + sprint: "Sprint" attributes: task_type: "Tipo de tarea" backlogs: any: "cualquiera" column_width: "Ancho de columna" definition_of_done: "Criterio de Aceptación" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impedimento" label_versions_default_fold_state: "Mostrar versiones colapsadas" caption_versions_default_fold_state: "Las versiones no se expandirán por defecto al visualizar los trabajos pendientes. Cada una deberá expandirse manualmente." @@ -75,12 +94,21 @@ es: backlog_component: blankslate_title: "%{name} está vacío" blankslate_description: "Aún no hay elementos previstos. Arrastre los elementos aquí para añadirlos." + sprint_component: + blankslate_title: "%{name} está vacío" + blankslate_description: "Aún no hay elementos previstos. Arrastre los elementos aquí para añadirlos." backlog_header_component: label_toggle_backlog: "Contraer/expandir %{name}" label_story_count: zero: "No hay historias en backlog" one: "%{count} historia en backlog" other: "%{count} historias en backlog" + sprint_header_component: + label_toggle_backlog: "Contraer/expandir %{name}" + label_story_count: + zero: "No hay historias en sprint" + one: "%{count} historia en sprint" + other: "%{count} historias en sprint" backlog_menu_component: label_actions: "Acciones de backlog" action_menu: @@ -91,6 +119,14 @@ es: burndown_chart: "Diagrama Burndown" wiki: "Wiki" properties: "Propiedades" + sprint_menu_component: + label_actions: "Acciones de sprint" + action_menu: + edit_sprint: "Editar sprint" + new_story: "Nueva historia" + stories_tasks: "Historias/tareas" + task_board: "Panel de tareas" + burndown_chart: "Diagrama Burndown" story_component: label_drag_story: "Mover %{name}" story_menu_component: @@ -118,15 +154,41 @@ es: label_backlogs_unconfigured: "Todavía no ha configurado backlogs. Por favor, visite %{administration} > %{plugins}, luego haga clic en el enlace de %{configure} para esta extensión. Cuando haya establecido los campos, vuelva a esta página para empezar a usar la herramienta." label_blocks_ids: "ID de los paquetes de trabajo bloqueados" label_column_in_backlog: "Columna en backlog" + label_used_as_backlog: "Se usa como backlog" label_points_burn_down: "Abajo" label_points_burn_up: "Arriba" + label_sprint_edit: "Editar sprint" label_sprint_impediments: "Impedimentos de sprint" + label_sprint_new: "Nuevo sprint" label_task_board: "Tablero de tareas" - permission_view_master_backlog: "Ver backlog maestro" - permission_view_taskboards: "Ver tablero de tareas" + permission_create_sprints: "Crear sprints" + permission_manage_sprint_items: "Gestionar elementos de sprint" permission_select_done_statuses: "Seleccionar estados de finalización" - permission_update_sprints: "Actualizar sprints" + permission_share_sprint: "Compartir sprint" + permission_start_complete_sprint: "Iniciar/completar sprint" + permission_view_sprints: "Ver sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No hay datos de trabajo pendiente disponibles" diff --git a/modules/backlogs/config/locales/crowdin/et.yml b/modules/backlogs/config/locales/crowdin/et.yml index 70ca6ef2f2f..40e46393441 100644 --- a/modules/backlogs/config/locales/crowdin/et.yml +++ b/modules/backlogs/config/locales/crowdin/et.yml @@ -25,6 +25,14 @@ et: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ et: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ et: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ et: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ et: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ et: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Alla" label_points_burn_up: "Üles" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Vali valmis staatused" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/eu.yml b/modules/backlogs/config/locales/crowdin/eu.yml index aee74542e1a..fab65bd9e43 100644 --- a/modules/backlogs/config/locales/crowdin/eu.yml +++ b/modules/backlogs/config/locales/crowdin/eu.yml @@ -25,6 +25,14 @@ eu: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ eu: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ eu: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "edozein" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ eu: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ eu: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ eu: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Behera" label_points_burn_up: "Gora" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/fa.yml b/modules/backlogs/config/locales/crowdin/fa.yml index f6111ae7bb9..6b038e250a1 100644 --- a/modules/backlogs/config/locales/crowdin/fa.yml +++ b/modules/backlogs/config/locales/crowdin/fa.yml @@ -25,6 +25,14 @@ fa: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "مدت زمان اسپرینت" work_package: @@ -33,6 +41,11 @@ fa: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ fa: task_version_must_be_the_same_as_story_version: "باید از همان نگارش داستان زادآوران باشد." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "نوع وظیفه" backlogs: any: "any" column_width: "عرض ستون" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "در هنگام مشاهده backlog ها، نسخه‌ها به صورت پیش‌فرض بازنخواهندشد. هر کدام باید به صورت دستی باز شوند." @@ -75,12 +94,21 @@ fa: backlog_component: blankslate_title: "%{name} خالی است" blankslate_description: "هنوز موردی برنامه ریزی نشده است. موارد را برای اضافه کردن به اینجا بکشید." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "جمع کردن/باز کردن %{name}" label_story_count: zero: "هیچ داستانی در لیست انتظار نیست" one: "%{count} داستان در لیست کار های عقب افتاده" other: "%{count} داستان های در لیست کارهای عقب افتاده" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "اقدامات عقب افتاده" action_menu: @@ -91,6 +119,14 @@ fa: burndown_chart: "نمودار خلاصه" wiki: "ویکی" properties: "ویژگی ها" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "انتقال %{name}" story_menu_component: @@ -118,15 +154,41 @@ fa: label_backlogs_unconfigured: "شما هنوز پس‌افت‌ها را پیکربندی نکرده‌اید. لطفاً به %{administration} > %{plugins} بروید، سپس برای این افزونه روی %{configure} کلیک کنید. وقتی همۀ قسمت‌ها را تکمیل کردید، برای استفاده از این ابزار به همین صفحه برگردید. " label_blocks_ids: "شناسه‌های مسدود کاربسته‌ها" label_column_in_backlog: "ستون در پس‌افت" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "پایین" label_points_burn_up: "بالا" + label_sprint_edit: "Edit sprint" label_sprint_impediments: " موانع تاخت" + label_sprint_new: "New sprint" label_task_board: "تابلوی وظیفه" - permission_view_master_backlog: "نمایش بک لاگ اصلی" - permission_view_taskboards: "مشاهده تابلو وظایف" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "انتخاب وضعیت های انجام شده" - permission_update_sprints: "بروزرسانی دوره‌ها" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "وظایف" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/fi.yml b/modules/backlogs/config/locales/crowdin/fi.yml index b2b927ccdc8..7139bf3ee61 100644 --- a/modules/backlogs/config/locales/crowdin/fi.yml +++ b/modules/backlogs/config/locales/crowdin/fi.yml @@ -25,6 +25,14 @@ fi: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ fi: backlogs_work_package_type: "Työjonon tyyppi" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ fi: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprintti ei voi päättyä, ennen kuin se alkaa." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "kaikki" column_width: "Column width" definition_of_done: "Valmiin määritelmä" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Este" label_versions_default_fold_state: "Näytä versiot ryhmiteltyinä" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ fi: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ fi: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ fi: label_backlogs_unconfigured: "Et ole määrittänyt vielä työjonoja. Siirry menuun %{administration} > %{plugins}, sitten klikkaa %{configure} linkkiä tälle liitännäiselle. Kun olet määrittänyt kentät, tule takaisin tälle sivulle aloittaaksesi työkalun käytön." label_blocks_ids: "Estettyjen työpakettien tunnukset" label_column_in_backlog: "Sarake työjonossa" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Alas" label_points_burn_up: "Ylös" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprintin esteet" + label_sprint_new: "New sprint" label_task_board: "Tehtävätaulu" - permission_view_master_backlog: "Näytä pääasiallinen työjono" - permission_view_taskboards: "Näytä työtaulut" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Päivitä sprinttejä" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Työjonot" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/fil.yml b/modules/backlogs/config/locales/crowdin/fil.yml index 6f0109336b2..2ef0247f2c6 100644 --- a/modules/backlogs/config/locales/crowdin/fil.yml +++ b/modules/backlogs/config/locales/crowdin/fil.yml @@ -25,6 +25,14 @@ fil: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ fil: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ fil: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ fil: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ fil: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ fil: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "Mga ID ng naka-block na mga pakete sa gumagawa" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/fr.yml b/modules/backlogs/config/locales/crowdin/fr.yml index 16aacdfeab4..14b3d5ce931 100644 --- a/modules/backlogs/config/locales/crowdin/fr.yml +++ b/modules/backlogs/config/locales/crowdin/fr.yml @@ -25,6 +25,14 @@ fr: description: "Ce module ajoute des fonctionnalités permettant aux équipes agiles de travailler avec OpenProject dans le cadre de projets Scrum." activerecord: attributes: + agile/sprint: + duration: "Durée" + finish_date: "Date de fin" + goal: "Objectif du sprint" + name: "Nom du sprint" + sharing: "Partage" + project: + sprint_sharing: "Partage de sprint" sprint: duration: "Durée du sprint" work_package: @@ -33,6 +41,11 @@ fr: backlogs_work_package_type: "Type de backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "ne peut pas être défini car le projet \"%{name}\" est déjà partagé avec tous les projets." + share_all_projects_already_taken_anonymous: "ne peut pas être défini parce qu'un autre projet est déjà partagé avec tous les projets." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ fr: task_version_must_be_the_same_as_story_version: "doit être identique à la version de l'histoire parente." sprint: cannot_end_before_it_starts: "Un sprint ne peut pas se terminer avant d'avoir débuté." + models: + sprint: "Sprint" attributes: task_type: "Type de tâche" backlogs: any: "tout" column_width: "Largeur de colonne" definition_of_done: "Définition de Fait" + definition_of_done_caption: "Les work packages ayant ces statuts sont traités comme terminés dans les vues du carnet de commandes et dans les rapports." + done_status: "État terminé" + sharing_description: "Ce projet peut soit partager ses propres sprints, soit recevoir des sprints partagés, soit gérer les sprints de manière indépendante (sans partage)." + sharing: "Partage" impediment: "Obstacle" label_versions_default_fold_state: "Afficher les versions de manière repliée" caption_versions_default_fold_state: "Les versions ne seront pas développées par défaut lors de l'affichage des backlogs. Chacune devra être développée manuellement." @@ -75,12 +94,21 @@ fr: backlog_component: blankslate_title: "%{name} est vide" blankslate_description: "Aucun élément n'a encore été planifié. Faites glisser les éléments ici pour les ajouter." + sprint_component: + blankslate_title: "%{name} est vide" + blankslate_description: "Aucun élément n'a encore été planifié. Faites glisser les éléments ici pour les ajouter." backlog_header_component: label_toggle_backlog: "Réduire/développer %{name}" label_story_count: zero: "Aucune histoire dans le backlog" one: "%{count} histoire dans le backlog" other: "%{count} histoires dans le backlog" + sprint_header_component: + label_toggle_backlog: "Réduire/développer %{name}" + label_story_count: + zero: "Aucune story dans le sprint" + one: "%{count} story dans le sprint" + other: "%{count} stories dans le sprint" backlog_menu_component: label_actions: "Actions dans le backlog" action_menu: @@ -91,6 +119,14 @@ fr: burndown_chart: "Graphique burndown" wiki: "Wiki" properties: "Propriétés" + sprint_menu_component: + label_actions: "Actions du sprint" + action_menu: + edit_sprint: "Modifier le sprint" + new_story: "Nouvelle story" + stories_tasks: "Stories/tâches" + task_board: "Tableau des tâches" + burndown_chart: "Graphique burndown" story_component: label_drag_story: "Déplacer %{name}" story_menu_component: @@ -118,15 +154,41 @@ fr: label_backlogs_unconfigured: "Vous n'avez pas encore configuré Backlogs. Veuillez vous rendre dans %{administration} > %{plugins}, puis cliquer sur le lien %{configure} pour ce plugin. Une fois que vous avez défini les champs, revenez sur cette page pour commencer à utiliser l'outil." label_blocks_ids: "ID des lots de travaux bloqués" label_column_in_backlog: "Colonne dans le backlog" + label_used_as_backlog: "Utilisé comme backlog" label_points_burn_down: "Vers le bas" label_points_burn_up: "Vers le haut" + label_sprint_edit: "Modifier le sprint" label_sprint_impediments: "Obstacles de sprint" + label_sprint_new: "Nouveau sprint" label_task_board: "Tableau des tâches" - permission_view_master_backlog: "Afficher le backlog principal" - permission_view_taskboards: "Voir les tableaux des tâches" + permission_create_sprints: "Créer des sprints" + permission_manage_sprint_items: "Gérer les éléments du sprint" permission_select_done_statuses: "Sélectionner les statuts terminés" - permission_update_sprints: "Éditer les sprints" + permission_share_sprint: "Partager le sprint" + permission_start_complete_sprint: "Commencer/terminer le sprint" + permission_view_sprints: "Voir les sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Ne pas partager" + caption: "Les sprints créés dans ce projet ne seront disponibles et visibles que pour ce projet. Ils ne seront pas non plus visibles par les sous-projets." + receive_shared: + label: "Recevoir des sprints partagés" + caption: "Ce projet ne peut utiliser que des sprints partagés par d'autres projets." + warning: "Ce projet ne peut utiliser que des sprints partagés par d'autres projets. Les sprints inutilisés créés dans ce projet par le passé ne seront plus visibles." + share_all_projects: + label: "Tous les projets" + caption: "Les sprints créés dans ce projet seront disponibles pour tous les projets de cette instance. Si vous sélectionnez cette option, les sprints ne seront plus disponibles pour les autres projets." + disabled_caption: "L'option n'est pas disponible car le projet \"%{name}\" est actuellement partagé avec tous les projets et seul un projet peut le faire." + disabled_caption_anonymous: "Option non disponible car un autre projet est actuellement en train de partager avec tous les projets et seul un projet peut le faire." + share_subprojects: + label: "Sous-projets" + caption: "Les sprints créés dans ce projet seront disponibles pour tous les sous-projets du projet en cours." + info: "Le partage d'un sprint entraîne le partage du nom, du statut et des dates de début et de fin dans tous les projets. Ceux-ci ne peuvent pas être modifiés dans les projets qui reçoivent et utilisent ces sprints." + sprint_sharing: Partager les sprints rb_burndown_charts: show: blankslate_title: "Aucune donnée disponible sur le burndown" diff --git a/modules/backlogs/config/locales/crowdin/he.yml b/modules/backlogs/config/locales/crowdin/he.yml index a3d2729ba67..f06acdb71fa 100644 --- a/modules/backlogs/config/locales/crowdin/he.yml +++ b/modules/backlogs/config/locales/crowdin/he.yml @@ -25,6 +25,14 @@ he: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ he: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ he: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "הכל" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -79,12 +98,21 @@ he: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -95,6 +123,14 @@ he: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -122,15 +158,41 @@ he: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "המזהים של חבילות עבודה חסומים" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/hi.yml b/modules/backlogs/config/locales/crowdin/hi.yml index c1624aaae8f..cfe9fafe806 100644 --- a/modules/backlogs/config/locales/crowdin/hi.yml +++ b/modules/backlogs/config/locales/crowdin/hi.yml @@ -25,6 +25,14 @@ hi: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ hi: backlogs_work_package_type: "बैकलॉग प्रकार" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ hi: task_version_must_be_the_same_as_story_version: "मूल कहानी के संस्करण के समान होना चाहिए।" sprint: cannot_end_before_it_starts: "स्प्रिंट शुरू होने से पहले खत्म नहीं हो सकता।" + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "कोई" column_width: "Column width" definition_of_done: "पूर्ण की परिभाषा" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "बाधा" label_versions_default_fold_state: "मुड़े हुए संस्करण दिखाएं" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ hi: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ hi: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ hi: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/hr.yml b/modules/backlogs/config/locales/crowdin/hr.yml index 4a46ad8f6c5..2d0f59ea3d7 100644 --- a/modules/backlogs/config/locales/crowdin/hr.yml +++ b/modules/backlogs/config/locales/crowdin/hr.yml @@ -25,6 +25,14 @@ hr: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ hr: backlogs_work_package_type: "Backlog tip" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ hr: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Period razvoja ne može završiti prije nego li je započeo." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "bilo koji" column_width: "Column width" definition_of_done: "Definicija učinjenog" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Teškoće" label_versions_default_fold_state: "Prikaži prikupljene verzije" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -77,12 +96,21 @@ hr: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -93,6 +121,14 @@ hr: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -120,15 +156,41 @@ hr: label_backlogs_unconfigured: "Niste još konfigurirali Backlog. Za konfiguraciju odaberite %{administration}>%{plugins}, zatim %{configure} Backlog dodatak. Nakon što ste uredili potrebna polja, vratite se na ovu stranicu da biste započeli s korištenjem ovog alata." label_blocks_ids: "ID blokiranih radnih paketa" label_column_in_backlog: "Stupac u backlogu" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Dolje" label_points_burn_up: "Gore" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Prepreke perioda razvoja" + label_sprint_new: "New sprint" label_task_board: "Upravitelj zadatcima" - permission_view_master_backlog: "Pogledaj glavni backlog" - permission_view_taskboards: "Pogledaj upravitelj zadatcima" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Ažuriraj periode razvoja" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/hu.yml b/modules/backlogs/config/locales/crowdin/hu.yml index 7aab8818879..1162b326881 100644 --- a/modules/backlogs/config/locales/crowdin/hu.yml +++ b/modules/backlogs/config/locales/crowdin/hu.yml @@ -25,6 +25,14 @@ hu: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ hu: backlogs_work_package_type: "Backlog típus" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ hu: task_version_must_be_the_same_as_story_version: "a szülő story verziójával megegyezőnek kell lennie." sprint: cannot_end_before_it_starts: "Sprint nem érhet véget, mielőtt elindul." + models: + sprint: "Sprint" attributes: task_type: "Feladat típusa" backlogs: any: "bármely" column_width: "Column width" definition_of_done: "A \"Kész\" meghatározása" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Akadály" label_versions_default_fold_state: "Összecsukott verziók mutatása" caption_versions_default_fold_state: "A verziók alapértelmezés szerint nem lesznek kibontva a várólista (backlog) megtekintésekor. Mindegyiket manuálisan kell kibontani." @@ -75,12 +94,21 @@ hu: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ hu: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ hu: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Le" label_points_burn_up: "Fel" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint akadályai" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Elkészültek kiválasztása" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/id.yml b/modules/backlogs/config/locales/crowdin/id.yml index 39f4116d3c8..0358278d122 100644 --- a/modules/backlogs/config/locales/crowdin/id.yml +++ b/modules/backlogs/config/locales/crowdin/id.yml @@ -25,6 +25,14 @@ id: description: "Modul ini menambahkan fitur yang memungkinkan tim yang gesit bekerja dengan OpenProject dalam proyek Scrum." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ id: backlogs_work_package_type: "Jenis jaminan tersimpan" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ id: task_version_must_be_the_same_as_story_version: "harus sama seperti versi kisah orang tua." sprint: cannot_end_before_it_starts: "Sprint tidak bisa berakhir sebelum mulai." + models: + sprint: "Sprint" attributes: task_type: "Jenis tugas" backlogs: any: "apapun" column_width: "Column width" definition_of_done: "Definisi Selesai" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Halangan" label_versions_default_fold_state: "Tampilkan versi terlipat" caption_versions_default_fold_state: "Versi tidak akan diperluas secara default saat melihat daftar tugas yang tertunda. Setiap versi harus diperluas secara manual." @@ -73,12 +92,21 @@ id: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -89,6 +117,14 @@ id: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -116,15 +152,41 @@ id: label_backlogs_unconfigured: "Anda belum mengkonfigurasi Backlogs. Silakan masuk ke %{administration}> %{plugins}, lalu klik pada link %{configure} untuk plugin ini. Setelah Anda menyetel bidang, kembali ke halaman ini untuk mulai menggunakan alat ini." label_blocks_ids: "ID dari paket pekerjaan yang diblokir" label_column_in_backlog: "Kolom di backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Menurun" label_points_burn_up: "Naik" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Hanbatan kekuatan" + label_sprint_new: "New sprint" label_task_board: "Papan tugas" - permission_view_master_backlog: "Lihat backlog master" - permission_view_taskboards: "Lihat papan tugas" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Pilih status selesai" - permission_update_sprints: "Perbarui sprint" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/it.yml b/modules/backlogs/config/locales/crowdin/it.yml index 4caea4de2d5..09199d2219e 100644 --- a/modules/backlogs/config/locales/crowdin/it.yml +++ b/modules/backlogs/config/locales/crowdin/it.yml @@ -25,6 +25,14 @@ it: description: "Questo modulo aggiunge funzionalità che consentono ai team agili di lavorare con i progetti OpenProject in Scrum." activerecord: attributes: + agile/sprint: + duration: "Durata" + finish_date: "Data di fine" + goal: "Obiettivo dello sprint" + name: "Nome dello sprint" + sharing: "Condivisione" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Durata dello sprint" work_package: @@ -33,6 +41,11 @@ it: backlogs_work_package_type: "Tipo di backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ it: task_version_must_be_the_same_as_story_version: "deve essere uguale alla versione della storia del genitore." sprint: cannot_end_before_it_starts: "Uno sprint non può terminare prima che venga avviato." + models: + sprint: "Sprint" attributes: task_type: "Tipo di attività" backlogs: any: "qualsiasi" column_width: "Larghezza della colonna" definition_of_done: "Definizione di fatto" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impedimento" label_versions_default_fold_state: "Espandi le versioni" caption_versions_default_fold_state: "Le versioni non verranno espanse per impostazione predefinita durante la visualizzazione dei backlog. Ogni versione deve essere espansa manualmente." @@ -75,12 +94,21 @@ it: backlog_component: blankslate_title: "%{name} è vuoto" blankslate_description: "Nessun elemento pianificato ancora. Trascina gli elementi qui per aggiungerli." + sprint_component: + blankslate_title: "%{name} è vuoto" + blankslate_description: "Nessun elemento pianificato ancora. Trascina gli elementi qui per aggiungerli." backlog_header_component: label_toggle_backlog: "Comprimi/Espandi %{name}" label_story_count: zero: "Nessuna story nel backlog" one: "%{count} story nel backlog" other: "%{count} story nel backlog" + sprint_header_component: + label_toggle_backlog: "Comprimi/Espandi %{name}" + label_story_count: + zero: "Nessuna story nello sprint" + one: "%{count} story nello sprint" + other: "%{count} story nello sprint" backlog_menu_component: label_actions: "Azioni nel backlog" action_menu: @@ -91,6 +119,14 @@ it: burndown_chart: "Grafico Burndown" wiki: "Wiki" properties: "Proprietà" + sprint_menu_component: + label_actions: "Azioni dello sprint" + action_menu: + edit_sprint: "Modifica sprint" + new_story: "Nuova story" + stories_tasks: "Story/Task" + task_board: "Bacheca dei task" + burndown_chart: "Grafico Burndown" story_component: label_drag_story: "Sposta %{name}" story_menu_component: @@ -118,15 +154,41 @@ it: label_backlogs_unconfigured: "Non hai ancora configurato i Backlog. Vai su %{administration} > %{plugins}, quindi fai clic sul link %{configure} per il plugin. Dopo aver impostato i campi, torna su questa pagina per iniziare a utilizzare lo strumento." label_blocks_ids: "ID dei pacchetti di lavoro bloccati" label_column_in_backlog: "Colonna nel backlog" + label_used_as_backlog: "Usato come backlog" label_points_burn_down: "Verso il basso" label_points_burn_up: "Verso l'alto" + label_sprint_edit: "Modifica sprint" label_sprint_impediments: "Impedimenti allo sprint" + label_sprint_new: "Nuovo sprint" label_task_board: "Pannello delle attività" - permission_view_master_backlog: "Visualizza il master backlog" - permission_view_taskboards: "Visualizza i pannelli delle attività" + permission_create_sprints: "Crea gli sprint" + permission_manage_sprint_items: "Gestisci gli elementi dello sprint" permission_select_done_statuses: "Seleziona gli stati terminati" - permission_update_sprints: "Aggiorna gli sprint" + permission_share_sprint: "Condividi lo sprint" + permission_start_complete_sprint: "Iniziare/completa lo sprint" + permission_view_sprints: "Visualizza gli sprint" project_module_backlogs: "Backlog" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Non sono disponibili dati di burndown" diff --git a/modules/backlogs/config/locales/crowdin/ja.yml b/modules/backlogs/config/locales/crowdin/ja.yml index 5af9e30d172..ed559ad21e0 100644 --- a/modules/backlogs/config/locales/crowdin/ja.yml +++ b/modules/backlogs/config/locales/crowdin/ja.yml @@ -25,6 +25,14 @@ ja: description: "このモジュールには、アジャイルチームがスクラムプロジェクトでOpenProjectを使用できるようにする機能が追加されています。" activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ja: backlogs_work_package_type: "バックログの種類" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ja: task_version_must_be_the_same_as_story_version: "親ストーリーのバージョンと同じでなければなりません。" sprint: cannot_end_before_it_starts: "スプリントは開始する前に終了できません。" + models: + sprint: "Sprint" attributes: task_type: "タスクのタイプ" backlogs: any: "全て" column_width: "Column width" definition_of_done: "「終了」の定義" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "障害事項" label_versions_default_fold_state: "バージョンを折り畳んで表示" caption_versions_default_fold_state: "バックログを表示する場合、デフォルトではバージョンは展開されません。各バージョンは手動で展開する必要があります。" @@ -73,12 +92,21 @@ ja: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -89,6 +117,14 @@ ja: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -116,15 +152,41 @@ ja: label_backlogs_unconfigured: "バックログは未設定です。%{administration} > %{plugins}をアクセスして、このプラグインの%{configure}リンクをクリックしてください。フィールドを設定した後、このページに戻ってツールを使用開始してください。" label_blocks_ids: "ブロックされているワークパッケージのID" label_column_in_backlog: "バックログの列" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "ダウン" label_points_burn_up: "アップ" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "スプリント障害事項" + label_sprint_new: "New sprint" label_task_board: "かんばん" - permission_view_master_backlog: "マスター バックログの表示" - permission_view_taskboards: "かんばんの表示" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "完了ステータスを選択" - permission_update_sprints: "スプリントの更新" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "バックログ" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ka.yml b/modules/backlogs/config/locales/crowdin/ka.yml index 9939b839c08..a29561c677e 100644 --- a/modules/backlogs/config/locales/crowdin/ka.yml +++ b/modules/backlogs/config/locales/crowdin/ka.yml @@ -25,6 +25,14 @@ ka: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ka: backlogs_work_package_type: "ჩამორჩენის ტიპი" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ka: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "ნებისმიერი" column_width: "Column width" definition_of_done: "დასრულების აღწერა" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "წინააღმდეგობა" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ ka: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ ka: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ ka: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "ქვემოთ" label_points_burn_up: "ზემოთ" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "ამოცანების დაფა" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "აირჩიეთ დასრულების სტატუსები" - permission_update_sprints: "სპრინტების განახლება" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "შეუსრულებელი ამოცანები" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/kk.yml b/modules/backlogs/config/locales/crowdin/kk.yml index 584b33393b7..cd93e23a720 100644 --- a/modules/backlogs/config/locales/crowdin/kk.yml +++ b/modules/backlogs/config/locales/crowdin/kk.yml @@ -25,6 +25,14 @@ kk: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ kk: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ kk: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ kk: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ kk: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ kk: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ko.yml b/modules/backlogs/config/locales/crowdin/ko.yml index 3127941ab0c..57af32fb81a 100644 --- a/modules/backlogs/config/locales/crowdin/ko.yml +++ b/modules/backlogs/config/locales/crowdin/ko.yml @@ -25,6 +25,14 @@ ko: description: "이 모듈은 애자일 팀이 Scrum 프로젝트에서 OpenProject로 작업할 수 있도록 하는 기능을 추가합니다." activerecord: attributes: + agile/sprint: + duration: "기간" + finish_date: "완료 날짜" + goal: "스프린트 목표" + name: "스프린트 이름" + sharing: "공유" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "스프린트 기간" work_package: @@ -33,6 +41,11 @@ ko: backlogs_work_package_type: "백로그 유형" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ko: task_version_must_be_the_same_as_story_version: "은(는) 부모 스토리의 버전과 동일해야 합니다." sprint: cannot_end_before_it_starts: "스프린트는 시작되기 전에 끝날 수 없습니다." + models: + sprint: "스프린트" attributes: task_type: "작업 유형" backlogs: any: "모두" column_width: "열 너비" definition_of_done: "완료 정의" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "제한" label_versions_default_fold_state: "접힌 버전 표시" caption_versions_default_fold_state: "백로그를 볼 때 버전은 기본적으로 확장되지 않습니다. 각 버전을 수동으로 확장해야 합니다." @@ -73,12 +92,21 @@ ko: backlog_component: blankslate_title: "%{name}이(가) 비어 있습니다" blankslate_description: "아직 계획된 항목이 없습니다. 항목을 추가하려면 여기로 드래그하세요." + sprint_component: + blankslate_title: "%{name}이(가) 비어 있습니다" + blankslate_description: "아직 계획된 항목이 없습니다. 항목을 추가하려면 여기로 드래그하세요." backlog_header_component: label_toggle_backlog: "%{name} 축소/확장" label_story_count: zero: "백로그에 스토리 없음" one: "백로그의 %{count}개 스토리" other: "백로그의 %{count}개 스토리" + sprint_header_component: + label_toggle_backlog: "%{name} 축소/확장" + label_story_count: + zero: "스프린트의 스토리 없음" + one: "스프린트의 %{count}개 스토리" + other: "스프린트의 %{count}개 스토리" backlog_menu_component: label_actions: "백로그 작업" action_menu: @@ -89,6 +117,14 @@ ko: burndown_chart: "번다운 차트" wiki: "위키" properties: "속성" + sprint_menu_component: + label_actions: "스프린트 작업" + action_menu: + edit_sprint: "스프린트 편집" + new_story: "새로운 스토리" + stories_tasks: "스토리/작업" + task_board: "작업 보드" + burndown_chart: "번다운 차트" story_component: label_drag_story: "%{name} 이동" story_menu_component: @@ -116,15 +152,41 @@ ko: label_backlogs_unconfigured: "백로그를 아직 구성하지 않았습니다. %{administration} > %{plugins}(으)로 이동한 다음 이 플러그인의 %{configure} 링크를 클릭하세요. 필드를 설정한 후 이 페이지로 돌아가서 해당 도구 사용을 시작하세요." label_blocks_ids: "차단된 작업 패키지의 ID" label_column_in_backlog: "백로그의 열" + label_used_as_backlog: "백로그로 사용됨" label_points_burn_down: "아래" label_points_burn_up: "위" + label_sprint_edit: "스프린트 편집" label_sprint_impediments: "스프린트 제한" + label_sprint_new: "새로운 스프린트" label_task_board: "작업 보드" - permission_view_master_backlog: "마스터 백로그 보기" - permission_view_taskboards: "작업 보드 보기" + permission_create_sprints: "스프린트 만들기" + permission_manage_sprint_items: "스프린트 항목 관리" permission_select_done_statuses: "완료 상태 선택" - permission_update_sprints: "스프린트 업데이트" + permission_share_sprint: "스프린트 공유" + permission_start_complete_sprint: "스프린트 시작/완료" + permission_view_sprints: "스프린트 보기" project_module_backlogs: "백로그" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "사용 가능한 번다운 데이터 없음" diff --git a/modules/backlogs/config/locales/crowdin/lt.yml b/modules/backlogs/config/locales/crowdin/lt.yml index 47f4923d387..4ccb602b9fa 100644 --- a/modules/backlogs/config/locales/crowdin/lt.yml +++ b/modules/backlogs/config/locales/crowdin/lt.yml @@ -25,6 +25,14 @@ lt: description: "Šis modulis prideda funkcionalumą, leidžianti agile komandoms dirbti su OpenProject Scrum projektuose." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ lt: backlogs_work_package_type: "Darbų sąrašo tipas" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ lt: task_version_must_be_the_same_as_story_version: "turi būti tokia pati kaip ir tėvinės istorijos versija." sprint: cannot_end_before_it_starts: "Sprintas negali baigtis prieš prasidėdamas." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "bet koks" column_width: "Column width" definition_of_done: "Pabaigimo apibrėžimas" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Kliūtis" label_versions_default_fold_state: "Rodyti suskleistas versijas" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -79,12 +98,21 @@ lt: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -95,6 +123,14 @@ lt: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -122,15 +158,41 @@ lt: label_backlogs_unconfigured: "Jūs dar nesukonfigūravote Darbų sąrašų. Prašome eiti į %{administration} > %{plugins}, tada nuspausti ant %{configure} nuorodos šiam įskiepiui. Kai nustatysite laukus, grįžkite čia ir pradėkite naudoti instrumentą." label_blocks_ids: "Blokuotų darbų paketų ID reikšmės" label_column_in_backlog: "Stulpelis darbų sąraše" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Žemyn" label_points_burn_up: "Aukštyn" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprinto trukdžiai" + label_sprint_new: "New sprint" label_task_board: "Užduočių lenta" - permission_view_master_backlog: "Peržiūrėti pagrindinį darbų sąrašą" - permission_view_taskboards: "Peržiūrėti užduočių lentas" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Parinkite atliktas būsenas" - permission_update_sprints: "Atnaujinti sprintus" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Darbų sąrašai" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/lv.yml b/modules/backlogs/config/locales/crowdin/lv.yml index 2adefaeaf7a..6109d4099ee 100644 --- a/modules/backlogs/config/locales/crowdin/lv.yml +++ b/modules/backlogs/config/locales/crowdin/lv.yml @@ -25,6 +25,14 @@ lv: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ lv: backlogs_work_package_type: "Produkta darbu krātuves tips" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ lv: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprints nevar beigties, pirms tas ir sācies." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "visi" column_width: "Column width" definition_of_done: "Pabeigtības definīcija" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Šķēršļi" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -77,12 +96,21 @@ lv: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -93,6 +121,14 @@ lv: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -120,15 +156,41 @@ lv: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Atlikušo darbu backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Lejup" label_points_burn_up: "Augšup" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprinta šķēršļi" + label_sprint_new: "New sprint" label_task_board: "Pieteikumu tāfele" - permission_view_master_backlog: "Skatīt visus nepabeigtos darbus" - permission_view_taskboards: "Apskatīt uzdevumu dēļus" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Izvēlieties pabeigtības statusu" - permission_update_sprints: "Atjaunināt sprintus" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Darbu krātuve" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/mn.yml b/modules/backlogs/config/locales/crowdin/mn.yml index 3bc7baea066..34332e35397 100644 --- a/modules/backlogs/config/locales/crowdin/mn.yml +++ b/modules/backlogs/config/locales/crowdin/mn.yml @@ -25,6 +25,14 @@ mn: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ mn: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ mn: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "ямар ч" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ mn: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ mn: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ mn: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ms.yml b/modules/backlogs/config/locales/crowdin/ms.yml index d3ea60be5c1..d17ec5b7404 100644 --- a/modules/backlogs/config/locales/crowdin/ms.yml +++ b/modules/backlogs/config/locales/crowdin/ms.yml @@ -25,6 +25,14 @@ ms: description: "Modul ini menambahkan fitur-fitur yang membolehkan kumpulan-kumpulan yang tangkas untuk bekerja menggunakan OpenProject di projek Scrum." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ms: backlogs_work_package_type: "Jenis tunggakan kerja" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ms: task_version_must_be_the_same_as_story_version: "perlu sama dengan versi cerita induk." sprint: cannot_end_before_it_starts: "Pecutan tidak boleh berakhir lebih awal sebelum ia bermula." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "sebarang" column_width: "Column width" definition_of_done: "Definisi Selesai" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Halangan" label_versions_default_fold_state: "Paparkan versi dilipat" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -73,12 +92,21 @@ ms: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -89,6 +117,14 @@ ms: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -116,15 +152,41 @@ ms: label_backlogs_unconfigured: "Anda masih belum mengkonfigurasi tunggakan. Sila pergi ke %{administration} > %{plugins}, kemudian klik pautan %{configure} untuk plugin ini. Setelah anda menetapkan ruangan, kembali ke halaman ini untuk mula menggunakan alat ini." label_blocks_ids: "ID pakej kerja yang disekat" label_column_in_backlog: "Kolum dalam tunggakan" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Bawah" label_points_burn_up: "Atas" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Halangan Pecutan" + label_sprint_new: "New sprint" label_task_board: "Papan tugasan" - permission_view_master_backlog: "Paparkan tunggakan utama" - permission_view_taskboards: "Lihat papan tugasan" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Pilih status selesai" - permission_update_sprints: "Kemas kini pecutan" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Tunggakan" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ne.yml b/modules/backlogs/config/locales/crowdin/ne.yml index 5b907ae6b84..da2c8ce48bd 100644 --- a/modules/backlogs/config/locales/crowdin/ne.yml +++ b/modules/backlogs/config/locales/crowdin/ne.yml @@ -25,6 +25,14 @@ ne: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ ne: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ne: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ ne: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ ne: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ ne: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/nl.yml b/modules/backlogs/config/locales/crowdin/nl.yml index 50c125516ba..11907683026 100644 --- a/modules/backlogs/config/locales/crowdin/nl.yml +++ b/modules/backlogs/config/locales/crowdin/nl.yml @@ -25,6 +25,14 @@ nl: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ nl: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ nl: task_version_must_be_the_same_as_story_version: "moet hetzelfde zijn als de bovenliggende verhaalversie." sprint: cannot_end_before_it_starts: "De sprint kan niet eindigen voordat het start." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "elke" column_width: "Column width" definition_of_done: "Definitie van Klaar" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Belemmering" label_versions_default_fold_state: "Laat versies samengevouwen zien" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ nl: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ nl: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ nl: label_backlogs_unconfigured: "Je hebt Backlogs nog niet geconfigureerd. Ga naar %{administration} >%{plugins}, en klik op de %{configure} voor deze plug-in. Kom hier terug nadat u de velden hebt geconfigureerd." label_blocks_ids: "ID's van geblokkeerde werkpakketten" label_column_in_backlog: "Kolom in achterstand" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Omlaag" label_points_burn_up: "Omhoog" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Obstakels" + label_sprint_new: "New sprint" label_task_board: "Taakbord" - permission_view_master_backlog: "Toon Máster Backlog" - permission_view_taskboards: "Bekijk taakborden" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Selecteer voltooide statussen" - permission_update_sprints: "Sprints bijwerken" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/no.yml b/modules/backlogs/config/locales/crowdin/no.yml index d463b97f8e9..03a46a7a196 100644 --- a/modules/backlogs/config/locales/crowdin/no.yml +++ b/modules/backlogs/config/locales/crowdin/no.yml @@ -25,6 +25,14 @@ description: "Denne modulen legger til funksjoner som setter dynamiske team i stand til å arbeide med OpenProject i Scrum prosjekter." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ backlogs_work_package_type: "Forsinkelser type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ task_version_must_be_the_same_as_story_version: "må være det samme som den overordnete historiens versjon." sprint: cannot_end_before_it_starts: "Sprinten kan ikke avsluttes før den starter." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "hvilken som helst" column_width: "Column width" definition_of_done: "Definisjon av ferdig" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Hinder" label_versions_default_fold_state: "Vis versjoner kollapset" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ label_backlogs_unconfigured: "Du har ikke konfigurert Forsinkelser enda. Gå til %{administration} > %{plugins}og klikk deretter på %{configure} lenken for denne utvidelsen. Når du har angitt felter, går du tilbake til denne siden for å begynne å bruke verktøyet." label_blocks_ids: "ID'er for blokkerte arbeidspakker" label_column_in_backlog: "Kolonne i forsinkelse" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Ned" label_points_burn_up: "Opp" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Hindring i etappe" + label_sprint_new: "New sprint" label_task_board: "Oppgavetavle" - permission_view_master_backlog: "Vis master forsinkelse" - permission_view_taskboards: "Vis oppgavetavler" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Velg ferdige statuser" - permission_update_sprints: "Oppdater etapper" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Forsinkelser" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/pl.yml b/modules/backlogs/config/locales/crowdin/pl.yml index 8cab7c56f21..cd0b8696e42 100644 --- a/modules/backlogs/config/locales/crowdin/pl.yml +++ b/modules/backlogs/config/locales/crowdin/pl.yml @@ -25,6 +25,14 @@ pl: description: "Moduł ten dodaje funkcje umożliwiające zwinnym zespołom pracę z OpenProject w projektach Scrum." activerecord: attributes: + agile/sprint: + duration: "Czas trwania" + finish_date: "Data zakończenia" + goal: "Cel sprintu" + name: "Nazwa sprintu" + sharing: "Udostępnianie" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Czas trwania sprintu" work_package: @@ -33,6 +41,11 @@ pl: backlogs_work_package_type: "Typ backlogu" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ pl: task_version_must_be_the_same_as_story_version: "musi być taka sama jak wersja story nadrzędnej." sprint: cannot_end_before_it_starts: "Sprint nie może się skończyć przed swoim rozpoczęciem." + models: + sprint: "Sprint" attributes: task_type: "Typ zadania" backlogs: any: "którekolwiek" column_width: "Szerokość kolumny" definition_of_done: "Definicja Zrobione" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Przeszkoda" label_versions_default_fold_state: "Pokaż zwinięte wersje" caption_versions_default_fold_state: "Wersje nie będą domyślnie rozwijane podczas przeglądania backlogów. Każdą z nich należy rozwinąć ręcznie." @@ -79,12 +98,21 @@ pl: backlog_component: blankslate_title: "Element %{name} jest pusty" blankslate_description: "Nie zaplanowano jeszcze żadnych punktów. Przeciągnij punkty tutaj, aby je dodać." + sprint_component: + blankslate_title: "Element %{name} jest pusty" + blankslate_description: "Nie zaplanowano jeszcze żadnych punktów. Przeciągnij punkty tutaj, aby je dodać." backlog_header_component: label_toggle_backlog: "Zwiń/rozwiń %{name}" label_story_count: zero: "Brak historii w backlogu" one: "%{count} historia w backlogu" other: "Liczba historii w backlogu: %{count}" + sprint_header_component: + label_toggle_backlog: "Zwiń/rozwiń %{name}" + label_story_count: + zero: "Brak historii w sprincie" + one: "%{count} historia w sprincie" + other: "Liczba historii w sprincie: %{count}" backlog_menu_component: label_actions: "Działania backlogu" action_menu: @@ -95,6 +123,14 @@ pl: burndown_chart: "Wykres spalania (burndown)" wiki: "Wiki" properties: "Właściwości" + sprint_menu_component: + label_actions: "Działania w ramach sprintu" + action_menu: + edit_sprint: "Edytuj sprint" + new_story: "Nowa historia" + stories_tasks: "Historie/zadania" + task_board: "Panel zadań" + burndown_chart: "Wykres spalania (burndown)" story_component: label_drag_story: "Przenieś %{name}" story_menu_component: @@ -122,15 +158,41 @@ pl: label_backlogs_unconfigured: "Jeszcze nie skonfigurowałeś backlogów. Przejdź do %{administration} > %{plugins}, następnie kliknij link %{configure} dla otrzymania tego dodatku. Po ustawieniu pól, wróć na tę stronę, aby zacząć korzystanie z narzędzia." label_blocks_ids: "Identyfikatory zablokowanych pakietów prac" label_column_in_backlog: "Kolumna w backlogu" + label_used_as_backlog: "Służy jako backlog" label_points_burn_down: "W dół" label_points_burn_up: "W górę" + label_sprint_edit: "Edytuj sprint" label_sprint_impediments: "Przeszkody sprintu" + label_sprint_new: "Nowy sprint" label_task_board: "Panel zadań" - permission_view_master_backlog: "Wyświetl master backlog" - permission_view_taskboards: "Wyświetl panel zadań" + permission_create_sprints: "Utwórz sprinty" + permission_manage_sprint_items: "Zarządzaj elementami sprintu" permission_select_done_statuses: "Wybierz wykonane statusy" - permission_update_sprints: "Aktualizuj sprint" + permission_share_sprint: "Udostępnij sprint" + permission_start_complete_sprint: "Rozpocznij/ukończ sprint" + permission_view_sprints: "Wyświetl sprinty" project_module_backlogs: "Backlogi" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Brak dostępnych danych spalania" diff --git a/modules/backlogs/config/locales/crowdin/pt-BR.yml b/modules/backlogs/config/locales/crowdin/pt-BR.yml index 941be3e4c9d..6178178d0eb 100644 --- a/modules/backlogs/config/locales/crowdin/pt-BR.yml +++ b/modules/backlogs/config/locales/crowdin/pt-BR.yml @@ -25,6 +25,14 @@ pt-BR: description: "Este módulo acrescenta recursos que permitem que as equipes ágeis trabalhem com o OpenProject em projetos Scrum." activerecord: attributes: + agile/sprint: + duration: "Duração" + finish_date: "Data de conclusão" + goal: "Objetivo da sprint" + name: "Nome da sprint" + sharing: "Compartilhamento" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Duração da sprint" work_package: @@ -33,6 +41,11 @@ pt-BR: backlogs_work_package_type: "Tipo de backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ pt-BR: task_version_must_be_the_same_as_story_version: "deve ser igual à versão da história dos pais." sprint: cannot_end_before_it_starts: "A sprint não pode terminar antes de começar." + models: + sprint: "Sprint" attributes: task_type: "Tipo de tarefa" backlogs: any: "qualquer" column_width: "Largura da coluna" definition_of_done: "Definição de pronto" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impedimento" label_versions_default_fold_state: "Mostrar versões em modo fechado" caption_versions_default_fold_state: "As versões não serão expandidas por padrão ao visualizar backlogs. Cada uma deve ser expandida manualmente." @@ -75,12 +94,21 @@ pt-BR: backlog_component: blankslate_title: "%{name} está vazio" blankslate_description: "Ainda não há itens planejados. Arraste os itens para este espaço para adicioná-los." + sprint_component: + blankslate_title: "%{name} está vazio" + blankslate_description: "Ainda não há itens planejados. Arraste os itens para este espaço para adicioná-los." backlog_header_component: label_toggle_backlog: "Recolher/Expandir %{name}" label_story_count: zero: "Nenhuma história no backlog" one: "%{count} história no backlog" other: "%{count} histórias no backlog" + sprint_header_component: + label_toggle_backlog: "Recolher/Expandir %{name}" + label_story_count: + zero: "Nenhuma história na sprint" + one: "%{count} história na sprint" + other: "%{count} histórias na sprint" backlog_menu_component: label_actions: "Ações do backlog" action_menu: @@ -91,6 +119,14 @@ pt-BR: burndown_chart: "Gráfico de burndown" wiki: "Wiki" properties: "Propriedades" + sprint_menu_component: + label_actions: "Ações da sprint" + action_menu: + edit_sprint: "Editar sprint" + new_story: "Nova história" + stories_tasks: "Histórias/tarefas" + task_board: "Quadro de tarefas" + burndown_chart: "Gráfico de burndown" story_component: label_drag_story: "Mover %{name}" story_menu_component: @@ -118,15 +154,41 @@ pt-BR: label_backlogs_unconfigured: "Você ainda não configurou o Backlog. Por favor, vá para %{administration} > %{plugins} e, em seguida, clique em %{configure} o link para este plugin. Uma vez que você definiu os campos, volte a esta página para começar a usar a ferramenta." label_blocks_ids: "IDs dos pacotes de trabalho bloqueados" label_column_in_backlog: "Coluna no backlog" + label_used_as_backlog: "Usado como backlog" label_points_burn_down: "Abaixo" label_points_burn_up: "Acima" + label_sprint_edit: "Editar sprint" label_sprint_impediments: "Impedimentos da Sprint" + label_sprint_new: "Nova sprint" label_task_board: "Quadro de tarefas" - permission_view_master_backlog: "Visualizar backlog principal" - permission_view_taskboards: "Visualizar quadro de tarefas" + permission_create_sprints: "Criar sprints" + permission_manage_sprint_items: "Gerenciar itens da sprint" permission_select_done_statuses: "Selecione situações concluídas" - permission_update_sprints: "Editar sprints" + permission_share_sprint: "Compartilhar sprint" + permission_start_complete_sprint: "Iniciar/concluir sprint" + permission_view_sprints: "Ver sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Nenhum dado de burndown disponível" diff --git a/modules/backlogs/config/locales/crowdin/pt-PT.yml b/modules/backlogs/config/locales/crowdin/pt-PT.yml index 5f50d045ed9..377207d9045 100644 --- a/modules/backlogs/config/locales/crowdin/pt-PT.yml +++ b/modules/backlogs/config/locales/crowdin/pt-PT.yml @@ -25,6 +25,14 @@ pt-PT: description: "Este módulo acrescenta funcionalidades que permitem às equipas Agile trabalhar com o OpenProject em projetos Scrum." activerecord: attributes: + agile/sprint: + duration: "Duração" + finish_date: "Data de término" + goal: "Objetivo do sprint" + name: "Nome do sprint" + sharing: "Partilhar" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Duração do sprint" work_package: @@ -33,6 +41,11 @@ pt-PT: backlogs_work_package_type: "Tipo de backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ pt-PT: task_version_must_be_the_same_as_story_version: "tem ser o mesmo que a versão da história do pai." sprint: cannot_end_before_it_starts: "Sprint não pode terminar antes de começar." + models: + sprint: "Sprint" attributes: task_type: "Tipo de tarefa" backlogs: any: "qualquer" column_width: "Largura da coluna" definition_of_done: "Definição de feito" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impedimento" label_versions_default_fold_state: "Mostrar versões dobradas" caption_versions_default_fold_state: "Versões não serão expandidas por predefinição quando visualizar os backlogs. Cada uma tem de ser expandida manualmente." @@ -75,12 +94,21 @@ pt-PT: backlog_component: blankslate_title: "%{name} está vazio" blankslate_description: "Ainda não existem elementos planeados. Arraste elementos para aqui para os adicionar." + sprint_component: + blankslate_title: "%{name} está vazio" + blankslate_description: "Ainda não existem elementos planeados. Arraste elementos para aqui para os adicionar." backlog_header_component: label_toggle_backlog: "Recolher/expandir %{name}" label_story_count: zero: "Não há histórias no backlog" one: "%{count} história no backlog" other: "%{count} histórias no backlog" + sprint_header_component: + label_toggle_backlog: "Recolher/expandir %{name}" + label_story_count: + zero: "Não há histórias no sprint" + one: "%{count} história em sprint" + other: "%{count} histórias em sprint" backlog_menu_component: label_actions: "Ações de backlog" action_menu: @@ -91,6 +119,14 @@ pt-PT: burndown_chart: "Gráfico de burndown" wiki: "Wiki" properties: "Propriedades" + sprint_menu_component: + label_actions: "Ações do sprint" + action_menu: + edit_sprint: "Editar sprint" + new_story: "Nova história" + stories_tasks: "Histórias/tarefas" + task_board: "Quadro de tarefas" + burndown_chart: "Gráfico de burndown" story_component: label_drag_story: "Mover %{name}" story_menu_component: @@ -118,15 +154,41 @@ pt-PT: label_backlogs_unconfigured: "Ainda não configurou os backlogs. Vá a %{administration} > %{plugins}, e depois clique no link %{configure} para este plugin. Após definir os campos, volte a esta página para começar a utilizar a ferramenta." label_blocks_ids: "Identificações de pacotes de trabalho bloqueados" label_column_in_backlog: "Coluna no backlog" + label_used_as_backlog: "Utilizado como backlog" label_points_burn_down: "Abaixo" label_points_burn_up: "Acima" + label_sprint_edit: "Editar sprint" label_sprint_impediments: "Impedimentos de Sprint" + label_sprint_new: "Novo sprint" label_task_board: "Quadro de tarefas" - permission_view_master_backlog: "Ver o backlog principal" - permission_view_taskboards: "Ver quadros de tarefas" + permission_create_sprints: "Criar sprints" + permission_manage_sprint_items: "Gerir elementos de sprint" permission_select_done_statuses: "Selecione os estados concluídos" - permission_update_sprints: "Atualizar sprints" + permission_share_sprint: "Partilhar sprint" + permission_start_complete_sprint: "Iniciar/completar sprint" + permission_view_sprints: "Ver sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Não há dados de burndown disponíveis" diff --git a/modules/backlogs/config/locales/crowdin/ro.yml b/modules/backlogs/config/locales/crowdin/ro.yml index 1a32c9370b0..c569e875c4b 100644 --- a/modules/backlogs/config/locales/crowdin/ro.yml +++ b/modules/backlogs/config/locales/crowdin/ro.yml @@ -25,14 +25,27 @@ ro: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: position: "Poziție" story_points: "Puncte" - backlogs_work_package_type: "Tipul de restante" + backlogs_work_package_type: "Tip restanță" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ro: task_version_must_be_the_same_as_story_version: "trebuie să fie aceeași cu versiunea cerinței părinte." sprint: cannot_end_before_it_starts: "Sprint nu se poate termina înainte de a începe." + models: + sprint: "Sprint" attributes: task_type: "Tip de sarcină" backlogs: any: "Oricare" column_width: "Column width" definition_of_done: "Procent realizat" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Afișare versiuni complete" caption_versions_default_fold_state: "Versiunile nu vor fi extinse în mod implicit la vizualizarea restanțelor. Fiecare versiune trebuie să fie extinsă manual." @@ -77,12 +96,21 @@ ro: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -93,6 +121,14 @@ ro: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -120,15 +156,41 @@ ro: label_backlogs_unconfigured: "Nu ai configurat încă Backlogs. Te rog să mergi la %{administration} > %{plugins}, apoi dă clic pe link-ul %{configure} pentru acest plugin. După ce ai configurat câmpurile, revino la această pagină pentru a începe să utilizezi instrumentul." label_blocks_ids: "ID-urile pachetelor de lucru blocate" label_column_in_backlog: "Coloană în backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Jos" label_points_burn_up: "Sus" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Impedimentele Sprint" + label_sprint_new: "New sprint" label_task_board: "Tablă de sarcini" - permission_view_master_backlog: "Vizualizare master backlog" - permission_view_taskboards: "Vizualizați tablourile de sarcini" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Selectează stările realizate" - permission_update_sprints: "Sprinturi" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Restanțe" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/ru.yml b/modules/backlogs/config/locales/crowdin/ru.yml index 87b53de1666..d719dcc6ca2 100644 --- a/modules/backlogs/config/locales/crowdin/ru.yml +++ b/modules/backlogs/config/locales/crowdin/ru.yml @@ -25,6 +25,14 @@ ru: description: "Этот модуль добавляет функции, позволяющие agile-командам работать с OpenProject в Scrum проектах." activerecord: attributes: + agile/sprint: + duration: "Продолжительность" + finish_date: "Дата окончания" + goal: "Цель спринта" + name: "Название спринта" + sharing: "Совместное использование" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Продолжительность спринта" work_package: @@ -33,6 +41,11 @@ ru: backlogs_work_package_type: "Тип невыполненной работы" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ ru: task_version_must_be_the_same_as_story_version: "должен совпадать с версией родительской истории." sprint: cannot_end_before_it_starts: "Спринт не может заканчиваться до того, как начнется." + models: + sprint: "Спринт" attributes: task_type: "Тип задачи" backlogs: any: "любой" column_width: "Ширина столбца" definition_of_done: "Определение термина \"Завершено\"" + definition_of_done_caption: "Пакеты работ с такими статусами рассматриваются как завершенные в представлениях бэклога и отчетах." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Препятствие" label_versions_default_fold_state: "Показать свернутые версии" caption_versions_default_fold_state: "Версии не будут разворачиваться по умолчанию при просмотре бэклогов. Каждая версия должна быть развернута вручную." @@ -79,12 +98,21 @@ ru: backlog_component: blankslate_title: "%{name} пустой" blankslate_description: "Пока ничего не запланировано. Перетащите элементы сюда, чтобы добавить их." + sprint_component: + blankslate_title: "%{name} пустой" + blankslate_description: "Пока ничего не запланировано. Перетащите элементы сюда, чтобы добавить их." backlog_header_component: label_toggle_backlog: "Свернуть/Развернуть %{name}" label_story_count: zero: "Нет историй в бэклоге" one: "%{count} история в бэклоге" other: "%{count} историй в бэклоге" + sprint_header_component: + label_toggle_backlog: "Свернуть/Развернуть %{name}" + label_story_count: + zero: "Нет историй в спринте" + one: "%{count} история в спринте" + other: "%{count} историй в спринте" backlog_menu_component: label_actions: "Действия в рамках бэклога" action_menu: @@ -95,6 +123,14 @@ ru: burndown_chart: "Сводная таблица" wiki: "Wiki" properties: "Свойства" + sprint_menu_component: + label_actions: "Действия в спринте" + action_menu: + edit_sprint: "Редактировать спринт" + new_story: "Новая история" + stories_tasks: "Истории/задачи" + task_board: "Панель задач" + burndown_chart: "Сводная таблица" story_component: label_drag_story: "Переместить %{name}" story_menu_component: @@ -122,15 +158,41 @@ ru: label_backlogs_unconfigured: "Вы еще не настроили Невыполненные работы. Перейдите на страницу %{administration} > %{plugins}, а затем нажмите на ссылку %{configure} для получения этого дополнения. После того как вы настроите поля, возвратитесь на эту страницу, чтобы начать пользоваться инструментом." label_blocks_ids: "Идентификаторы заблокированных рабочих пакетов" label_column_in_backlog: "Колонка в бэклоге" + label_used_as_backlog: "Используется как бэклог" label_points_burn_down: "Вниз" label_points_burn_up: "Вверх" + label_sprint_edit: "Редактировать спринт" label_sprint_impediments: "Препятствия спринта" + label_sprint_new: "Новый спринт" label_task_board: "Панель задач" - permission_view_master_backlog: "Просмотреть главную невыполненную работу" - permission_view_taskboards: "Просмотреть панели задач" + permission_create_sprints: "Создание спринтов" + permission_manage_sprint_items: "Управление пунктами спринта" permission_select_done_statuses: "Выберите завершенные статусы" - permission_update_sprints: "Обновить спринты" + permission_share_sprint: "Поделиться спринтом" + permission_start_complete_sprint: "Начать/завершить спринт" + permission_view_sprints: "Просмотр спринтов" project_module_backlogs: "Бэклоги" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Спринты, созданные в этом проекте, будут доступны и видны только в этом проекте. Они также не будут видны подпроектам." + receive_shared: + label: "Receive shared sprints" + caption: "Этот проект может использовать только спринты, совместно используемые другими проектами." + warning: "Этот проект может использовать только общие спринты других проектов. Неиспользуемые спринты, созданные в этом проекте в прошлом, больше не будут видны." + share_all_projects: + label: "Все проекты" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Подпроекты" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Нет сводных данных" diff --git a/modules/backlogs/config/locales/crowdin/rw.yml b/modules/backlogs/config/locales/crowdin/rw.yml index 83a8442ce8f..377e44fc915 100644 --- a/modules/backlogs/config/locales/crowdin/rw.yml +++ b/modules/backlogs/config/locales/crowdin/rw.yml @@ -25,6 +25,14 @@ rw: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ rw: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ rw: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ rw: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ rw: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ rw: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/si.yml b/modules/backlogs/config/locales/crowdin/si.yml index f2789c8f7b8..cef2feee69e 100644 --- a/modules/backlogs/config/locales/crowdin/si.yml +++ b/modules/backlogs/config/locales/crowdin/si.yml @@ -25,6 +25,14 @@ si: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ si: backlogs_work_package_type: "බැක්ලොග් වර්ගය" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ si: task_version_must_be_the_same_as_story_version: "මව් කතාවේ අනුවාදයට සමාන විය යුතුය." sprint: cannot_end_before_it_starts: "ස්ප්රින්ට් ආරම්භ වීමට පෙර අවසන් කළ නොහැක." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "ඔනෑම" column_width: "Column width" definition_of_done: "සිදු කරන ලද අර්ථ දැක්වීම" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "බාධාවන්" label_versions_default_fold_state: "නවනු අනුවාද පෙන්වන්න" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ si: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ si: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ si: label_backlogs_unconfigured: "ඔබ තවමත් Backlogs වින්යාස කර නැත. කරුණාකර යන්න %{administration} > %{plugins}, ඉන්පසු මෙම ප්ලගිනය සඳහා %{configure} සබැඳිය ක්ලික් කරන්න. ඔබ ක්ෂේත්ර සකස් කළ පසු, මෙවලම භාවිතා කිරීම ආරම්භ කිරීමට මෙම පිටුවට නැවත එන්න." label_blocks_ids: "අවහිර කරන ලද වැඩ පැකේජ වල IDS" label_column_in_backlog: "පසුබිම තුළ තීරුව" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "පහළට" label_points_burn_up: "ඉහළට" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "ස්ප්රින්ට් බාධාවන්" + label_sprint_new: "New sprint" label_task_board: "කාර්ය මණ්ඩලය" - permission_view_master_backlog: "ස්වාමියා බැක්ලොග් දැක්ම" - permission_view_taskboards: "දැක්ම කාර්ය මණ්ඩල" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "ස්ප්රින්ට් යාවත්කාලීන" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "බැක්ලොග්ස්" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/sk.yml b/modules/backlogs/config/locales/crowdin/sk.yml index 2e9db70d37f..867697ba38d 100644 --- a/modules/backlogs/config/locales/crowdin/sk.yml +++ b/modules/backlogs/config/locales/crowdin/sk.yml @@ -25,6 +25,14 @@ sk: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Trvanie šprintu" work_package: @@ -33,6 +41,11 @@ sk: backlogs_work_package_type: "Typ oneskorenia" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ sk: task_version_must_be_the_same_as_story_version: "musí byť rovnaká ako verzia nadradeného článku." sprint: cannot_end_before_it_starts: "Šprint nemôže byť ukončený ešte pred jeho spustením." + models: + sprint: "Sprint" attributes: task_type: "Typ úlohy" backlogs: any: "akékoľvek" column_width: "Šírka stĺpca" definition_of_done: "Definícia pojmu Hotovo" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Prekážka" label_versions_default_fold_state: "Zobrazenie zložených verzií" caption_versions_default_fold_state: "Verzie sa pri prezeraní nevybavených dokumentov nebudú predvolene rozbaľovať. Každú z nich je potrebné rozbaliť manuálne." @@ -79,12 +98,21 @@ sk: backlog_component: blankslate_title: "%{name} je prázdne" blankslate_description: "Zatiaľ nie sú plánované žiadne položky. Ak chcete pridať položky, potiahnite ich sem." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Zbaliť/rozšíriť %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -95,6 +123,14 @@ sk: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -122,15 +158,41 @@ sk: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "Identifikátory blokovaných pracovných balíkov" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Nadol" label_points_burn_up: "Nahor" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "Zobrazenie tabúľ úloh" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Výber stavov hotovo" - permission_update_sprints: "Aktualizácia šprinty" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/sl.yml b/modules/backlogs/config/locales/crowdin/sl.yml index 75de0f0bad3..5fafed62682 100644 --- a/modules/backlogs/config/locales/crowdin/sl.yml +++ b/modules/backlogs/config/locales/crowdin/sl.yml @@ -25,6 +25,14 @@ sl: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ sl: backlogs_work_package_type: "Tip opravila na čakanju" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ sl: task_version_must_be_the_same_as_story_version: "mora biti enako različici nadrejene zgodbe." sprint: cannot_end_before_it_starts: "Tek se ne mora končati preden se začne" + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "katerikoli" column_width: "Column width" definition_of_done: "Opredelitev opravljenega" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Ovira" label_versions_default_fold_state: "Pokaži različice zložene" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -79,12 +98,21 @@ sl: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -95,6 +123,14 @@ sl: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -122,15 +158,41 @@ sl: label_backlogs_unconfigured: "Zaostankov še niste konfigurirali. Pojdite na %{administration} > %{plugins}, nato kliknite povezavo %{configure} za ta vtičnik. Ko nastavite polja, se vrnite na to stran, da začnete uporabljati orodje." label_blocks_ids: "ID blokiranih delovnih paketov" label_column_in_backlog: "Stolpec v zaostanku" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Navzdol" label_points_burn_up: "Navzgor" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Motnje sprinta" + label_sprint_new: "New sprint" label_task_board: "Tabla opravil" - permission_view_master_backlog: "glavni zaostanek prejšnje poizvedbe" - permission_view_taskboards: "Prikaz delovne table" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Označi statuse narejeno" - permission_update_sprints: "Posodobi šprinte" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Zaostanki" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/sr.yml b/modules/backlogs/config/locales/crowdin/sr.yml index 58043dd441a..f849101982e 100644 --- a/modules/backlogs/config/locales/crowdin/sr.yml +++ b/modules/backlogs/config/locales/crowdin/sr.yml @@ -25,6 +25,14 @@ sr: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ sr: backlogs_work_package_type: "Tip backlog-a" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ sr: task_version_must_be_the_same_as_story_version: "mora biti ista kao verzija priče roditelja." sprint: cannot_end_before_it_starts: "Sprint se ne može završiti pre nego što počne." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "bilo koji" column_width: "Column width" definition_of_done: "Definicija završetka" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Smetnja" label_versions_default_fold_state: "Prikaži verzije skupljene" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -77,12 +96,21 @@ sr: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -93,6 +121,14 @@ sr: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -120,15 +156,41 @@ sr: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/sv.yml b/modules/backlogs/config/locales/crowdin/sv.yml index 399d2b9b27f..fb3fc262b39 100644 --- a/modules/backlogs/config/locales/crowdin/sv.yml +++ b/modules/backlogs/config/locales/crowdin/sv.yml @@ -25,6 +25,14 @@ sv: description: "Modulen lägger till funktioner som gör det möjligt för agila team att arbeta med OpenProject i Scrum-projekt." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ sv: backlogs_work_package_type: "Typ av backlogg" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ sv: task_version_must_be_the_same_as_story_version: "måste vara samma som föräldraberättelsens version." sprint: cannot_end_before_it_starts: "En sprint kan inte avslutas innan den startar." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "någon" column_width: "Column width" definition_of_done: "Definition av klart" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Hinder" label_versions_default_fold_state: "Visa ihopfällda versioner" caption_versions_default_fold_state: "Versioner kommer inte att utökas som standard när du visar backloggar. Var och en måste utökas manuellt." @@ -75,12 +94,21 @@ sv: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ sv: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ sv: label_backlogs_unconfigured: "Du har inte konfigurerat backloggar ännu. Gå till %{administration} > %{plugins}, klicka på %{configure} länken för denna plugin. När du har ställt in fälten, kan du komma tillbaka till denna sida för att börja använda verktyget." label_blocks_ids: "ID:n för blockerade arbetspaket" label_column_in_backlog: "Kolumn i backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Ner" label_points_burn_up: "Upp" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint hinder" + label_sprint_new: "New sprint" label_task_board: "Aktivitetstavla" - permission_view_master_backlog: "Visa master backlog" - permission_view_taskboards: "Visa aktivitetstavlor" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Välj klar status" - permission_update_sprints: "Uppdatera sprinter" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backloggar" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/th.yml b/modules/backlogs/config/locales/crowdin/th.yml index f438571c80c..089c7feccb0 100644 --- a/modules/backlogs/config/locales/crowdin/th.yml +++ b/modules/backlogs/config/locales/crowdin/th.yml @@ -25,6 +25,14 @@ th: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "ระยะเวลา" + finish_date: "วันที่เสร็จสิ้น" + goal: "เป้าหมายสปริ๊นต์" + name: "ชื่อสปรินต์" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "ระยะเวลาการสปรินต์" work_package: @@ -33,6 +41,11 @@ th: backlogs_work_package_type: "ประเภทงานค้าง" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ th: task_version_must_be_the_same_as_story_version: "เวอร์ชันต้องตรงกับสตอรี่หลัก" sprint: cannot_end_before_it_starts: "กรุณาระบุวันสิ้นสุดให้หลังจากวันเริ่มต้นสปรินต์" + models: + sprint: "สปรินท์" attributes: task_type: "ประเภทงาน" backlogs: any: "ทั้งหมด" column_width: "ความกว้างของคอลัมน์" definition_of_done: "นิยามของคำว่าเสร็จสิ้น" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "รายการอุปสรรค" label_versions_default_fold_state: "แสดงเวอร์ชันที่พับอยู่" caption_versions_default_fold_state: "เวอร์ชันจะถูกพับไว้เป็นค่าเริ่มต้น กรุณาคลิกเพื่อขยายดูทีละรายการ" @@ -73,12 +92,21 @@ th: backlog_component: blankslate_title: "%{name} ว่างอยู่" blankslate_description: "ยังไม่มีรายการที่วางแผนไว้ ลากรายการมาวางที่นี่เพื่อเพิ่ม" + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "พับ/ขยาย %{name}" label_story_count: zero: "ไม่มีสตอรี่ในรายการงานคงค้าง" one: "%{count} สตอรี่ในรายการงานคงค้าง" other: "%{count} สตอรี่ในรายการงานคงค้าง" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "การจัดการรายการงานคงค้าง" action_menu: @@ -89,6 +117,14 @@ th: burndown_chart: "กราฟติดตามงาน (Burndown)" wiki: "วิกิ" properties: "คุณสมบัติ" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "ย้าย %{name}" story_menu_component: @@ -116,15 +152,41 @@ th: label_backlogs_unconfigured: "คุณยังไม่ได้ตั้งค่ารายการงานคงค้าง กรุณาไปที่ %{administration} > %{plugins} จากนั้นคลิกที่ลิงก์ %{configure} ของปลั๊กอินนี้ เมื่อคุณตั้งค่าฟิลด์ต่าง ๆ เรียบร้อยแล้ว ให้กลับมาที่หน้านี้เพื่อเริ่มใช้งานเครื่องมือ" label_blocks_ids: "Id ของแพคเกจการทำงานที่ถูกบล็อก" label_column_in_backlog: "คอลัมน์ในรายการงานคงค้าง" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "ลง" label_points_burn_up: "ขึ้น" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "อุปสรรคของสปรินท์" + label_sprint_new: "สปรินท์ใหม่" label_task_board: "กระดานงาน" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "สร้างสปรินท์" + permission_manage_sprint_items: "จัดการรายการในสปรินท์" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "แชร์สปรินท์" + permission_start_complete_sprint: "เริ่มต้น/เสร็จสิ้นสปรินท์" + permission_view_sprints: "ดูสปรินท์" project_module_backlogs: "รายการงานคงค้าง" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "ไม่มีข้อมูลกราฟติดตามงาน (Burndown)" diff --git a/modules/backlogs/config/locales/crowdin/tr.yml b/modules/backlogs/config/locales/crowdin/tr.yml index ebd436c906f..6e2ee977b0d 100644 --- a/modules/backlogs/config/locales/crowdin/tr.yml +++ b/modules/backlogs/config/locales/crowdin/tr.yml @@ -25,6 +25,14 @@ tr: description: "Bu modül, çevik ekiplerin Scrum projelerinde OpenProject ile çalışmasını sağlayan özellikler ekler." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Paylaşım" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint süresi" work_package: @@ -33,6 +41,11 @@ tr: backlogs_work_package_type: "Bekleme listesi türü" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ tr: task_version_must_be_the_same_as_story_version: "ana hikaye sürümüyle aynı olmalıdır." sprint: cannot_end_before_it_starts: "Sprint başlamadan önce bitemez." + models: + sprint: "Sprint" attributes: task_type: "Görev türü" backlogs: any: "herhangi bir" column_width: "Sütun genişliği" definition_of_done: "Bitti Tanımı" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Engel" label_versions_default_fold_state: "Katlanmış sürümleri göster" caption_versions_default_fold_state: "Birikmiş işler görüntülenirken sürümler öntanımlı olarak genişletilmeyecektir. Her birinin ayrı ayrı genişletilmesi gerekir." @@ -75,12 +94,21 @@ tr: backlog_component: blankslate_title: "%{name} boş" blankslate_description: "Henüz planlanan öğe yok. Eklemek için öğeleri buraya sürükleyin." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Daralt/Genişlet %{name}" label_story_count: zero: "Kuyrukta hikaye yok" one: "%{count} kuyruktaki hikaye" other: "%{count} kuyruktaki hikayeler" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Kuyruk eylemleri" action_menu: @@ -91,6 +119,14 @@ tr: burndown_chart: "Burndown grafiği" wiki: "Wiki" properties: "Özellikler" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Taşı %{name}" story_menu_component: @@ -118,15 +154,41 @@ tr: label_backlogs_unconfigured: "Backlog'ları henüz yapılandırmadınız. Lütfen% %{administration}> %{plugins} adresine gidin, ardından bu eklenti için %{configure} bağlantısını tıklayın. Alanları belirledikten sonra, aracı kullanmaya başlamak için bu sayfaya geri dönün." label_blocks_ids: "Bloke iş paketleri kimlikleri" label_column_in_backlog: "Sütununda birikim var" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Aşağı" label_points_burn_up: "Yukarı" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Engelleri" + label_sprint_new: "New sprint" label_task_board: "Görev panosu" - permission_view_master_backlog: "Ana bekleme günlüğünü görüntüleme" - permission_view_taskboards: "Görev tahtalarını görüntüleme" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Tamamlanan durumları seçiniz" - permission_update_sprints: "Süratleri güncelle" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "İş listesi" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Burndown verisi yok" diff --git a/modules/backlogs/config/locales/crowdin/uk.yml b/modules/backlogs/config/locales/crowdin/uk.yml index 175b9af4e09..5cf85e3fc10 100644 --- a/modules/backlogs/config/locales/crowdin/uk.yml +++ b/modules/backlogs/config/locales/crowdin/uk.yml @@ -25,6 +25,14 @@ uk: description: "Цей модуль додає функції, завдяки яким agile-команди можуть працювати над проєктами Scrum в OpenProject." activerecord: attributes: + agile/sprint: + duration: "Тривалість" + finish_date: "Дата закінчення" + goal: "Ціль спринту" + name: "Ім’я спринту" + sharing: "Надання доступу" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Тривалість спринту" work_package: @@ -33,6 +41,11 @@ uk: backlogs_work_package_type: "Тип Backlog-у" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ uk: task_version_must_be_the_same_as_story_version: "має збігатись із версією батьківської історії." sprint: cannot_end_before_it_starts: "Спринт не може закінчитися, перш ніж він почне працювати." + models: + sprint: "Спринт" attributes: task_type: "Тип завдання" backlogs: any: "будь-який" column_width: "Ширина стовпця" definition_of_done: "Визначення завершено" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Перешкода" label_versions_default_fold_state: "Показати складені версії" caption_versions_default_fold_state: "Версії не розгортатимуться за замовчуванням при перегляді невиконаних завдань. Кожну версію потрібно розгортати вручну." @@ -79,12 +98,21 @@ uk: backlog_component: blankslate_title: "%{name} нічого не містить" blankslate_description: "Ще нічого не заплановано. Перетягніть елементи сюди, щоб додати їх." + sprint_component: + blankslate_title: "%{name} нічого не містить" + blankslate_description: "Ще нічого не заплановано. Перетягніть елементи сюди, щоб додати їх." backlog_header_component: label_toggle_backlog: "Згорнути/розгорнути %{name}" label_story_count: zero: "У беклозі немає історій" one: "У беклозі %{count} історія" other: "У беклозі стільки історій: %{count}" + sprint_header_component: + label_toggle_backlog: "Згорнути/розгорнути %{name}" + label_story_count: + zero: "У спринті немає історій" + one: "У спринті %{count} історія" + other: "У спринті стільки історій: %{count}" backlog_menu_component: label_actions: "Дії беклогу" action_menu: @@ -95,6 +123,14 @@ uk: burndown_chart: "Діаграма згорання завдань" wiki: "Wiki" properties: "Властивості" + sprint_menu_component: + label_actions: "Дії спринту" + action_menu: + edit_sprint: "Редагувати спринт" + new_story: "Нова історія" + stories_tasks: "Історії/завдання" + task_board: "Дошка завдань" + burndown_chart: "Діаграма згорання завдань" story_component: label_drag_story: "Перемістити історію «%{name}»" story_menu_component: @@ -122,15 +158,41 @@ uk: label_backlogs_unconfigured: "Ви ще не настроїли Backlogs. Перейдіть на %{administration}>%{plugins}, потім натисніть посилання %{configure} для цього плагіна. Після того, як ви встановите поля, поверніться на цю сторінку, щоб розпочати використання інструмента." label_blocks_ids: "Ідентифікатори заблокованих робочих пакетів" label_column_in_backlog: "Стовпець у backlog-у" + label_used_as_backlog: "Використовується як беклог" label_points_burn_down: "Вниз" label_points_burn_up: "Вгору" + label_sprint_edit: "Редагувати спринт" label_sprint_impediments: "Перешкоди спринту" + label_sprint_new: "Новий спринт" label_task_board: "Дошка завдань" - permission_view_master_backlog: "Перегляд головного backlog-у" - permission_view_taskboards: "Перегляд панелі завдань" + permission_create_sprints: "Створення спринтів" + permission_manage_sprint_items: "Керування елементами спринтів" permission_select_done_statuses: "Виберіть завершені статуси" - permission_update_sprints: "Оновлення спринту" + permission_share_sprint: "Надання доступу до спринтів" + permission_start_complete_sprint: "Початок/завершення спринтів" + permission_view_sprints: "Перегляд спринтів" project_module_backlogs: "Невиконані завдання" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "Немає даних про згорання завдань" diff --git a/modules/backlogs/config/locales/crowdin/uz.yml b/modules/backlogs/config/locales/crowdin/uz.yml index 1d784af9e97..4b5d038b3f7 100644 --- a/modules/backlogs/config/locales/crowdin/uz.yml +++ b/modules/backlogs/config/locales/crowdin/uz.yml @@ -25,6 +25,14 @@ uz: description: "This module adds features enabling agile teams to work with OpenProject in Scrum projects." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ uz: backlogs_work_package_type: "Backlog type" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ uz: task_version_must_be_the_same_as_story_version: "must be the same as the parent story's version." sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" attributes: task_type: "Task type" backlogs: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -75,12 +94,21 @@ uz: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -91,6 +119,14 @@ uz: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -118,15 +154,41 @@ uz: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/vi.yml b/modules/backlogs/config/locales/crowdin/vi.yml index 4c8ca2e4f8f..681c24b2418 100644 --- a/modules/backlogs/config/locales/crowdin/vi.yml +++ b/modules/backlogs/config/locales/crowdin/vi.yml @@ -25,6 +25,14 @@ vi: description: "Mô-đun này bổ sung các tính năng cho phép các nhóm linh hoạt làm việc với OpenProject trong các dự án Scrum." activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -33,6 +41,11 @@ vi: backlogs_work_package_type: "Loại Backlog" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ vi: task_version_must_be_the_same_as_story_version: "phải giống với phiên bản của bảng cha." sprint: cannot_end_before_it_starts: "Sprint không thể kết thúc trước khi nó bắt đầu." + models: + sprint: "Sprint" attributes: task_type: "Loại nhiệm vụ" backlogs: any: "bất kỳ" column_width: "Column width" definition_of_done: "Định nghĩa về Hoàn thành" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Trở ngại" label_versions_default_fold_state: "Hiển thị các phiên bản \n" caption_versions_default_fold_state: "Các phiên bản sẽ không được mở rộng theo mặc định khi xem hồ sơ tồn đọng. Mỗi cái phải được mở rộng bằng tay." @@ -73,12 +92,21 @@ vi: backlog_component: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: zero: "No stories in backlog" one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -89,6 +117,14 @@ vi: burndown_chart: "Burndown chart" wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "Move %{name}" story_menu_component: @@ -116,15 +152,41 @@ vi: label_backlogs_unconfigured: "Bạn chưa cấu hình Bảng nhiệm vụ tồn đọng. Vui lòng vào %{administration} > %{plugins}, sau đó nhấp vào liên kết %{configure} cho gắn thêm này. Khi bạn đã thiết lập các trường, quay lại trang này để bắt đầu sử dụng công cụ." label_blocks_ids: "ID của các work package bị chặn" label_column_in_backlog: "Cột tồn đọng" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Xuống" label_points_burn_up: "lên" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Trở ngại nước rút" + label_sprint_new: "New sprint" label_task_board: "Bảng nhiệm vụ" - permission_view_master_backlog: "Xem tồn đọng chính" - permission_view_taskboards: "Xem bảng tác vụ" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Chọn trạng thái hoàn thành" - permission_update_sprints: "Cập nhật các lần chạy nước rút" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "tồn đọng" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/locales/crowdin/zh-CN.yml b/modules/backlogs/config/locales/crowdin/zh-CN.yml index 148af750cd9..ae92da0c00c 100644 --- a/modules/backlogs/config/locales/crowdin/zh-CN.yml +++ b/modules/backlogs/config/locales/crowdin/zh-CN.yml @@ -25,6 +25,14 @@ zh-CN: description: "该模块为敏捷团队在敏捷项目中使用 OpenProject 添加了功能。" activerecord: attributes: + agile/sprint: + duration: "持续时间" + finish_date: "完成日期" + goal: "冲刺目标" + name: "冲刺名称" + sharing: "共享" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "冲刺持续时间" work_package: @@ -33,6 +41,11 @@ zh-CN: backlogs_work_package_type: "待办清单类型" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ zh-CN: task_version_must_be_the_same_as_story_version: "必须与父级故事的版本相同。" sprint: cannot_end_before_it_starts: "比赛不可以在开始前结束。" + models: + sprint: "冲刺" attributes: task_type: "任务类型" backlogs: any: "任一" column_width: "列宽" definition_of_done: "完成的定义" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "障碍" label_versions_default_fold_state: "显示已折叠的版本" caption_versions_default_fold_state: "查看积压工作时,默认情况下不会展开版本。每个版本都必须手动展开。" @@ -73,12 +92,21 @@ zh-CN: backlog_component: blankslate_title: "%{name} 为空" blankslate_description: "尚未计划条目。请将条目拖到此处以添加。" + sprint_component: + blankslate_title: "%{name} 为空" + blankslate_description: "尚未计划条目。请将条目拖到此处以添加。" backlog_header_component: label_toggle_backlog: "折叠/展开 %{name}" label_story_count: zero: "积压工作中没有故事" one: "积压工作中有 %{count} 个故事" other: "积压工作中有 %{count} 个故事" + sprint_header_component: + label_toggle_backlog: "折叠/展开 %{name}" + label_story_count: + zero: "冲刺中没有故事" + one: "冲刺中有 %{count} 个故事" + other: "冲刺中有 %{count} 个故事" backlog_menu_component: label_actions: "积压工作操作" action_menu: @@ -89,6 +117,14 @@ zh-CN: burndown_chart: "燃尽图" wiki: "维基" properties: "属性" + sprint_menu_component: + label_actions: "冲刺操作" + action_menu: + edit_sprint: "编辑冲刺" + new_story: "新建故事" + stories_tasks: "故事/任务" + task_board: "任务面板" + burndown_chart: "燃尽图" story_component: label_drag_story: "移动 %{name}" story_menu_component: @@ -116,15 +152,41 @@ zh-CN: label_backlogs_unconfigured: "您尚未配置待办清单。请转到“%{administration} > %{plugins}”,然后单击此插件的 %{configure} 链接。设置字段后,返回到此页面开始使用工具。" label_blocks_ids: "被阻止的工作包 ID" label_column_in_backlog: "待办清单中的列" + label_used_as_backlog: "用作积压工作" label_points_burn_down: "减少" label_points_burn_up: "增加" + label_sprint_edit: "编辑冲刺" label_sprint_impediments: "冲刺 (sprint) 障碍" + label_sprint_new: "新冲刺" label_task_board: "任务板" - permission_view_master_backlog: "查看主待办清单" - permission_view_taskboards: "查看任务板" + permission_create_sprints: "创建冲刺" + permission_manage_sprint_items: "管理冲刺条目" permission_select_done_statuses: "选择完成状态" - permission_update_sprints: "更新迭代" + permission_share_sprint: "共享冲刺" + permission_start_complete_sprint: "开始/完成冲刺" + permission_view_sprints: "查看冲刺" project_module_backlogs: "待办清单" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "没有可用的燃尽数据" diff --git a/modules/backlogs/config/locales/crowdin/zh-TW.yml b/modules/backlogs/config/locales/crowdin/zh-TW.yml index 6058a1249a7..483f6689f6b 100644 --- a/modules/backlogs/config/locales/crowdin/zh-TW.yml +++ b/modules/backlogs/config/locales/crowdin/zh-TW.yml @@ -21,10 +21,18 @@ #++ zh-TW: plugin_openproject_backlogs: - name: "OpenProject待辦事項" + name: "OpenProject代辦事項" description: "此模組新增了讓敏捷團隊能夠在 Scrum 專案中使用 OpenProject 的功能。" activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "分享" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "衝刺時間" work_package: @@ -33,6 +41,11 @@ zh-TW: backlogs_work_package_type: "待辦事項類型" errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -42,12 +55,18 @@ zh-TW: task_version_must_be_the_same_as_story_version: "必須與上層「使用者需求」(User Story)的版本相同。" sprint: cannot_end_before_it_starts: "進度不可以在開始前結束" + models: + sprint: "Sprint" attributes: task_type: "任務類型" backlogs: any: "任何" column_width: "欄寬" definition_of_done: "定義" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "阻礙" label_versions_default_fold_state: "顯示精簡版本" caption_versions_default_fold_state: "檢視待辦清單時,版本預設不會展開,需手動逐一展開。" @@ -73,12 +92,21 @@ zh-TW: backlog_component: blankslate_title: "%{name} 為空" blankslate_description: "尚未規劃任何項目。將項目拖曳至此即可新增。" + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." backlog_header_component: label_toggle_backlog: "折疊/展開 %{name}" label_story_count: zero: "目前沒有待辦故事" one: "待辦清單中有 %{count} 個故事" other: "待辦清單中有 %{count} 個故事" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" backlog_menu_component: label_actions: "待辦清單操作" action_menu: @@ -89,6 +117,14 @@ zh-TW: burndown_chart: "燃盡圖" wiki: "維基" properties: "屬性" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" story_component: label_drag_story: "移動 %{name}" story_menu_component: @@ -116,15 +152,41 @@ zh-TW: label_backlogs_unconfigured: "您尚未設定待辦事項。請前往 '%{administration} -> %{plugins}' 然後在 %{configure} 連結上按一下。當你設定欄位後,再返回這個頁面使用這個工具。" label_blocks_ids: "被禁止的工作套件 IDs" label_column_in_backlog: "待辦事項的欄位" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "減少" label_points_burn_up: "增加" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "進度阻礙" + label_sprint_new: "New sprint" label_task_board: "任務看板" - permission_view_master_backlog: "檢視主待辦事項" - permission_view_taskboards: "檢視任務看板" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "選擇完成狀態" - permission_update_sprints: "更新進度" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "待辦事項" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "沒有可用的燃盡圖資料" diff --git a/modules/backlogs/config/locales/en.yml b/modules/backlogs/config/locales/en.yml index c36d4b44210..ef130788c11 100644 --- a/modules/backlogs/config/locales/en.yml +++ b/modules/backlogs/config/locales/en.yml @@ -34,6 +34,14 @@ en: activerecord: attributes: + agile/sprint: + duration: "Duration" + finish_date: "Finish date" + goal: "Sprint goal" + name: "Sprint name" + sharing: "Sharing" + project: + sprint_sharing: "Sprint sharing" sprint: duration: "Sprint duration" work_package: @@ -43,6 +51,11 @@ en: errors: models: + project: + attributes: + sprint_sharing: + share_all_projects_already_taken: "cannot be set because project \"%{name}\" is already sharing with all projects." + share_all_projects_already_taken_anonymous: "cannot be set because another project is already sharing with all projects." work_package: attributes: blocks_ids: @@ -53,6 +66,9 @@ en: sprint: cannot_end_before_it_starts: "Sprint cannot end before it starts." + models: + sprint: "Sprint" + attributes: task_type: "Task type" @@ -60,6 +76,10 @@ en: any: "any" column_width: "Column width" definition_of_done: "Definition of Done" + definition_of_done_caption: "Work packages with these statuses are treated as completed in backlog views and reporting." + done_status: "Done status" + sharing_description: "This project can either share its own sprints, receive shared sprints or handle sprints independently (no sharing)." + sharing: "Sharing" impediment: "Impediment" label_versions_default_fold_state: "Show versions folded" caption_versions_default_fold_state: "Versions will not be expanded by default when viewing backlogs. Each one has to be manually expanded." @@ -89,6 +109,10 @@ en: blankslate_title: "%{name} is empty" blankslate_description: "No items planned yet. Drag items here to add them." + sprint_component: + blankslate_title: "%{name} is empty" + blankslate_description: "No items planned yet. Drag items here to add them." + backlog_header_component: label_toggle_backlog: "Collapse/Expand %{name}" label_story_count: @@ -96,6 +120,13 @@ en: one: "%{count} story in backlog" other: "%{count} stories in backlog" + sprint_header_component: + label_toggle_backlog: "Collapse/Expand %{name}" + label_story_count: + zero: "No stories in sprint" + one: "%{count} story in sprint" + other: "%{count} stories in sprint" + backlog_menu_component: label_actions: "Backlog actions" action_menu: @@ -107,6 +138,15 @@ en: wiki: "Wiki" properties: "Properties" + sprint_menu_component: + label_actions: "Sprint actions" + action_menu: + edit_sprint: "Edit sprint" + new_story: "New story" + stories_tasks: "Stories/Tasks" + task_board: "Task board" + burndown_chart: "Burndown chart" + story_component: label_drag_story: "Move %{name}" @@ -140,18 +180,44 @@ en: label_backlogs_unconfigured: "You have not configured Backlogs yet. Please go to %{administration} > %{plugins}, then click on the %{configure} link for this plugin. Once you have set the fields, come back to this page to start using the tool." label_blocks_ids: "IDs of blocked work packages" label_column_in_backlog: "Column in backlog" + label_used_as_backlog: "Used as backlog" label_points_burn_down: "Down" label_points_burn_up: "Up" + label_sprint_edit: "Edit sprint" label_sprint_impediments: "Sprint Impediments" + label_sprint_new: "New sprint" label_task_board: "Task board" - permission_view_master_backlog: "View master backlog" - permission_view_taskboards: "View taskboards" + permission_create_sprints: "Create sprints" + permission_manage_sprint_items: "Manage sprint items" permission_select_done_statuses: "Select done statuses" - permission_update_sprints: "Update sprints" + permission_share_sprint: "Share sprint" + permission_start_complete_sprint: "Start/complete sprint" + permission_view_sprints: "View sprints" project_module_backlogs: "Backlogs" + projects: + settings: + backlog_sharing: + options: + no_sharing: + label: "Don't share" + caption: "Sprints created in this project will only be available and visible to this project. They will also not be visible to subprojects." + receive_shared: + label: "Receive shared sprints" + caption: "This project can only use sprints shared by other projects." + warning: "This project can only use sprints shared by other projects. Unused sprints created in this project in the past, will no longer be visible." + share_all_projects: + label: "All projects" + caption: "Sprints created in this project will be available to all projects in this instance. If you select this option, it will no longer be available to other projects." + disabled_caption: "Option not available since project \"%{name}\" is currently sharing with all projects and only one project can do this." + disabled_caption_anonymous: "Option not available since another project is currently sharing with all projects and only one project can do this." + share_subprojects: + label: "Subprojects" + caption: "Sprints created in this project will be available to all subprojects of the current project." + info: "Sharing a sprint will share the name, status and the start and finish dates in all projects. These cannot be modified in projects that receive and use these sprints." + sprint_sharing: Share sprints rb_burndown_charts: show: blankslate_title: "No burndown data available" diff --git a/modules/backlogs/config/routes.rb b/modules/backlogs/config/routes.rb index ee9d1aba906..f4a3e0436d5 100644 --- a/modules/backlogs/config/routes.rb +++ b/modules/backlogs/config/routes.rb @@ -27,6 +27,29 @@ #++ Rails.application.routes.draw do + # Routes for the new Agile::Sprint + # Scoped under projects for permissions: + resources :projects, only: [] do + resources :sprints, controller: :rb_sprints, only: %i[create] do + collection do + get :new_dialog + get :refresh_form + end + + member do + get :edit_dialog + put :update_agile_sprint + end + + resources :stories, controller: :rb_stories, only: [] do + member do + put :move + end + end + end + end + + # Legacy routes scope "", as: "backlogs" do scope "projects/:project_id", as: "project" do resources :backlogs, controller: :rb_master_backlogs, only: :index do @@ -54,7 +77,7 @@ Rails.application.routes.draw do resources :stories, controller: :rb_stories, only: [] do member do - put :move + put :move_legacy post :reorder end end @@ -71,6 +94,8 @@ Rails.application.routes.draw do scope "projects/:project_id", as: "project", module: "projects" do namespace "settings" do + resource :backlog_sharing, only: %i[show update] + resource :backlogs, only: %i[show update] do member do post "rebuild_positions" => "backlogs#rebuild_positions" diff --git a/modules/backlogs/db/migrate/20260227143618_remove_sharing_from_sprints.rb b/modules/backlogs/db/migrate/20260227143618_remove_sharing_from_sprints.rb new file mode 100644 index 00000000000..2f70e7b6ed4 --- /dev/null +++ b/modules/backlogs/db/migrate/20260227143618_remove_sharing_from_sprints.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 RemoveSharingFromSprints < ActiveRecord::Migration[8.1] + def change + remove_column :sprints, :sharing, :string, null: false, default: "none" + end +end diff --git a/modules/backlogs/db/migrate/20260305175656_add_index_to_projects_sprint_sharing_settings.rb b/modules/backlogs/db/migrate/20260305175656_add_index_to_projects_sprint_sharing_settings.rb new file mode 100644 index 00000000000..108c65cd1c1 --- /dev/null +++ b/modules/backlogs/db/migrate/20260305175656_add_index_to_projects_sprint_sharing_settings.rb @@ -0,0 +1,38 @@ +# 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 AddIndexToProjectsSprintSharingSettings < ActiveRecord::Migration[8.1] + def change + add_index :projects, + "(settings->'sprint_sharing')", + using: :gin, + name: "index_projects_settings_sprint_sharing" + end +end diff --git a/modules/backlogs/lib/open_project/backlogs/engine.rb b/modules/backlogs/lib/open_project/backlogs/engine.rb index 18210c6e1e0..e9b0daef535 100644 --- a/modules/backlogs/lib/open_project/backlogs/engine.rb +++ b/modules/backlogs/lib/open_project/backlogs/engine.rb @@ -59,33 +59,24 @@ module OpenProject::Backlogs end OpenProject::AccessControl.permission(:edit_work_packages).tap do |edit| - edit.controller_actions << "rb_stories/move" - edit.controller_actions << "rb_stories/reorder" edit.controller_actions << "rb_tasks/update" edit.controller_actions << "rb_impediments/update" end end project_module :backlogs, dependencies: :work_package_tracking do - # Master backlog permissions - permission :view_master_backlog, + permission :view_sprints, { rb_master_backlogs: %i[index details], rb_sprints: %i[index show show_name], rb_wikis: :show, rb_stories: %i[index show], rb_queries: :show, - rb_burndown_charts: :show }, - permissible_on: :project - - permission :view_taskboards, - { rb_taskboards: :show, - rb_sprints: :show, - rb_stories: :show, + rb_burndown_charts: :show, + rb_taskboards: :show, rb_tasks: %i[index show], - rb_impediments: %i[index show], - rb_wikis: :show, - rb_burndown_charts: :show }, - permissible_on: :project + rb_impediments: %i[index show] }, + permissible_on: :project, + dependencies: :view_work_packages permission :select_done_statuses, { @@ -94,15 +85,32 @@ module OpenProject::Backlogs permissible_on: :project, require: :member - # Sprint permissions - # :show_sprints and :list_sprints are implicit in :view_master_backlog permission - permission :update_sprints, - { - rb_sprints: %i[edit_name update], - rb_wikis: %i[edit update] - }, + permission :create_sprints, + { rb_sprints: %i[new_dialog refresh_form create edit_name update edit_dialog update_agile_sprint], + rb_wikis: %i[edit update] }, permissible_on: :project, - require: :member + require: :member, + dependencies: :view_sprints + + permission :start_complete_sprint, + {}, + permissible_on: :project, + require: :member, + dependencies: :view_sprints, + visible: -> { OpenProject::FeatureDecisions.scrum_projects_active? } + + permission :manage_sprint_items, + { rb_stories: %i[move move_legacy reorder] }, + permissible_on: :project, + require: :member, + dependencies: :view_sprints + + permission :share_sprint, + { "projects/settings/backlog_sharings": %i[show update] }, + permissible_on: :project, + require: :member, + dependencies: :create_sprints, + visible: -> { OpenProject::FeatureDecisions.scrum_projects_active? } end menu :project_menu, @@ -177,6 +185,21 @@ module OpenProject::Backlogs OpenProject::Backlogs::Hooks::UserSettingsHook end + initializer "openproject_backlogs.event_subscriptions" do + Rails.application.config.after_initialize do + OpenProject::Notifications.subscribe(OpenProject::Events::MODULE_DISABLED) do |payload| + disabled_module = payload[:disabled_module] + next unless disabled_module.name == "backlogs" + + disabled_module.project.not_sharing_sprints! + end + + OpenProject::Notifications.subscribe(OpenProject::Events::PROJECT_ARCHIVED) do |payload| + payload[:project].not_sharing_sprints! + end + end + end + config.to_prepare do ::Type.add_constraint :position, ->(type, project: nil) do if project.present? diff --git a/modules/backlogs/lib/open_project/backlogs/patches/project_patch.rb b/modules/backlogs/lib/open_project/backlogs/patches/project_patch.rb index f63de9c4b45..9ddee562a10 100644 --- a/modules/backlogs/lib/open_project/backlogs/patches/project_patch.rb +++ b/modules/backlogs/lib/open_project/backlogs/patches/project_patch.rb @@ -29,26 +29,23 @@ #++ module OpenProject::Backlogs::Patches::ProjectPatch - def self.included(base) - base.class_eval do - has_and_belongs_to_many :done_statuses, join_table: :done_statuses_for_project, class_name: "::Status" - has_many :sprints, class_name: "Agile::Sprint", dependent: :destroy + extend ActiveSupport::Concern + include Projects::SprintSharing - include InstanceMethods - end + included do + has_and_belongs_to_many :done_statuses, join_table: :done_statuses_for_project, class_name: "::Status" + has_many :sprints, class_name: "Agile::Sprint", dependent: :destroy end - module InstanceMethods - def rebuild_positions - return unless backlogs_enabled? + def rebuild_positions + return unless backlogs_enabled? - shared_versions.each { |v| v.rebuild_story_positions(self) } - nil - end + shared_versions.each { |v| v.rebuild_story_positions(self) } + nil + end - def backlogs_enabled? - module_enabled? "backlogs" - end + def backlogs_enabled? + module_enabled? "backlogs" end end diff --git a/modules/backlogs/lib/open_project/backlogs/patches/version_patch.rb b/modules/backlogs/lib/open_project/backlogs/patches/version_patch.rb index 2730388c184..0adb1ee5ad3 100644 --- a/modules/backlogs/lib/open_project/backlogs/patches/version_patch.rb +++ b/modules/backlogs/lib/open_project/backlogs/patches/version_patch.rb @@ -37,6 +37,14 @@ module OpenProject::Backlogs::Patches::VersionPatch end module InstanceMethods + def used_as_backlog?(project = self.project) + return false unless project.backlogs_enabled? + + settings = version_settings&.where(project:)&.first + + !!settings&.display_right? + end + def rebuild_story_positions(project = self.project) return unless project.backlogs_enabled? diff --git a/modules/backlogs/spec/components/backlogs/backlog_component_spec.rb b/modules/backlogs/spec/components/backlogs/backlog_component_spec.rb index 1eebc30e9f0..a963c3dcf30 100644 --- a/modules/backlogs/spec/components/backlogs/backlog_component_spec.rb +++ b/modules/backlogs/spec/components/backlogs/backlog_component_spec.rb @@ -31,6 +31,8 @@ require "rails_helper" RSpec.describe Backlogs::BacklogComponent, type: :component do + include Rails.application.routes.url_helpers + shared_let(:type_feature) { create(:type_feature) } shared_let(:type_task) { create(:type_task) } shared_let(:default_status) { create(:default_status) } @@ -115,7 +117,9 @@ RSpec.describe Backlogs::BacklogComponent, type: :component do render_component box = page.find(".Box") - expect(box["data-target-id"]).to eq(sprint.id.to_s) + expect(box["data-generic-drag-and-drop-target"]).to eq("container") + expect(box["data-target-container-accessor"]).to eq(":scope > ul") + expect(box["data-target-id"]).to eq("version:#{sprint.id}") expect(box["data-target-allowed-drag-type"]).to eq("story") end @@ -125,7 +129,7 @@ RSpec.describe Backlogs::BacklogComponent, type: :component do story_row = page.find(".Box-row[id='story_#{story1.id}']") expect(story_row["data-draggable-id"]).to eq(story1.id.to_s) expect(story_row["data-draggable-type"]).to eq("story") - expect(story_row["data-drop-url"]).to include("move") + expect(story_row["data-drop-url"]).to end_with(move_legacy_backlogs_project_sprint_story_path(project, sprint, story1)) end it "renders story rows with proper classes" do diff --git a/modules/backlogs/spec/components/backlogs/backlog_menu_component_spec.rb b/modules/backlogs/spec/components/backlogs/backlog_menu_component_spec.rb index 310642d19d2..d9dfac1d79a 100644 --- a/modules/backlogs/spec/components/backlogs/backlog_menu_component_spec.rb +++ b/modules/backlogs/spec/components/backlogs/backlog_menu_component_spec.rb @@ -59,8 +59,36 @@ RSpec.describe Backlogs::BacklogMenuComponent, type: :component do end describe "permission-based items" do - context "with :update_sprints permission" do - let(:permissions) { %i[view_master_backlog update_sprints] } + context "with :manage_sprint_items permission" do + let(:permissions) { %i[view_sprints manage_sprint_items] } + + it "shows Add new story item with compose icon" do + render_component + + expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.new_story")) + expect(page).to have_octicon(:compose) + end + end + + context "without :manage_sprint_items permission" do + let(:permissions) { [:view_sprints] } + + it "does not show Add new story item" do + render_component + + expect(page).to have_no_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.new_story")) + end + end + + context "with :create_sprints permission" do + let(:permissions) { %i[view_sprints create_sprints] } + + it "shows Properties item with gear icon" do + render_component + + expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.properties")) + expect(page).to have_octicon(:gear) + end it "shows Edit item with pencil icon" do render_component @@ -71,8 +99,14 @@ RSpec.describe Backlogs::BacklogMenuComponent, type: :component do end end - context "without :update_sprints permission" do - let(:permissions) { [:view_master_backlog] } + context "without :create_sprints permission" do + let(:permissions) { [:view_sprints] } + + it "does not show Properties item" do + render_component + + expect(page).to have_no_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.properties")) + end it "does not show Edit item" do render_component @@ -81,50 +115,8 @@ RSpec.describe Backlogs::BacklogMenuComponent, type: :component do end end - context "with :add_work_packages permission" do - let(:permissions) { %i[view_master_backlog add_work_packages] } - - it "shows Add new story item with compose icon" do - render_component - - expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.new_story")) - expect(page).to have_octicon(:compose) - end - end - - context "without :add_work_packages permission" do - let(:permissions) { [:view_master_backlog] } - - it "does not show Add new story item" do - render_component - - expect(page).to have_no_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.new_story")) - end - end - - context "with :manage_versions permission" do - let(:permissions) { %i[view_master_backlog manage_versions] } - - it "shows Properties item with gear icon" do - render_component - - expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.properties")) - expect(page).to have_octicon(:gear) - end - end - - context "without :manage_versions permission" do - let(:permissions) { [:view_master_backlog] } - - it "does not show Properties item" do - render_component - - expect(page).to have_no_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.properties")) - end - end - - context "with :view_taskboards permission" do - let(:permissions) { %i[view_master_backlog view_taskboards] } + context "with :view_sprints permission" do + let(:permissions) { %i[view_sprints] } it "shows Task board item" do render_component @@ -132,20 +124,10 @@ RSpec.describe Backlogs::BacklogMenuComponent, type: :component do expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.task_board")) end end - - context "without :view_taskboards permission" do - let(:permissions) { [:view_master_backlog] } - - it "does not show Task board item" do - render_component - - expect(page).to have_no_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.task_board")) - end - end end describe "always-visible items" do - let(:permissions) { [:view_master_backlog] } + let(:permissions) { [:view_sprints] } it "renders a stable id on the action menu and stories/tasks item" do render_component @@ -190,7 +172,7 @@ RSpec.describe Backlogs::BacklogMenuComponent, type: :component do describe "module-based items" do context "when wiki module is enabled" do - let(:permissions) { [:view_master_backlog] } + let(:permissions) { [:view_sprints] } let(:project) { create(:project, types: [type_feature, type_task], enabled_module_names: %w[backlogs wiki]) } it "shows Wiki item" do @@ -202,7 +184,7 @@ RSpec.describe Backlogs::BacklogMenuComponent, type: :component do end context "when wiki module is disabled" do - let(:permissions) { [:view_master_backlog] } + let(:permissions) { [:view_sprints] } let(:project) { create(:project, types: [type_feature, type_task], enabled_module_names: %w[backlogs]) } it "does not show Wiki item" do diff --git a/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb b/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb new file mode 100644 index 00000000000..bc56e0db62e --- /dev/null +++ b/modules/backlogs/spec/components/backlogs/sprint_component_spec.rb @@ -0,0 +1,148 @@ +# 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 "rails_helper" + +RSpec.describe Backlogs::SprintComponent, type: :component do + include Rails.application.routes.url_helpers + + shared_let(:type_feature) { create(:type_feature) } + shared_let(:type_task) { create(:type_task) } + shared_let(:default_status) { create(:default_status) } + shared_let(:default_priority) { create(:default_priority) } + shared_let(:user) { create(:admin) } + current_user { user } + + let(:project) { create(:project, types: [type_feature, type_task]) } + let(:sprint) { create(:agile_sprint, project:, name: "Sprint 1", start_date: Date.yesterday, finish_date: Date.tomorrow) } + + before do + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return("story_types" => [type_feature.id.to_s], "task_type" => type_task.id.to_s) + + allow(user).to receive(:backlogs_preference).with(:versions_default_fold_state).and_return("open") + end + + def render_component + render_inline(described_class.new(sprint:, current_user: user)) + end + + describe "rendering" do + context "with stories" do + let!(:story1) do + create(:work_package, + project:, + type: type_feature, + status: default_status, + priority: default_priority, + story_points: 5, + position: 1, + sprint: sprint) + end + let!(:story2) do + create(:work_package, + project:, + type: type_feature, + status: default_status, + priority: default_priority, + story_points: 3, + position: 2, + sprint: sprint) + end + + it "renders a Primer::Beta::BorderBox" do + render_component + + expect(page).to have_css(".Box") + end + + it "has the sprint ID in the DOM id" do + render_component + + expect(page).to have_css(".Box#agile_sprint_#{sprint.id}") + end + + it "renders BacklogHeaderComponent in header" do + render_component + + expect(page).to have_css(".Box-header h3", text: "Sprint 1") + end + + it "renders a stable id on the sprint header" do + render_component + + expect(page).to have_element(:div, class: "Box-header", id: /\Aagile_sprint_#{sprint.id}_header\z/) + end + + it "renders StoryComponent for each story" do + render_component + + expect(page).to have_css(".Box-row", count: 2) # 2 stories + expect(page).to have_text(story1.subject) + expect(page).to have_text(story2.subject) + end + + it "has drop target data attributes" do + render_component + + box = page.find(".Box") + expect(box["data-generic-drag-and-drop-target"]).to eq("container") + expect(box["data-target-container-accessor"]).to eq(":scope > ul") + expect(box["data-target-id"]).to eq("sprint:#{sprint.id}") + expect(box["data-target-allowed-drag-type"]).to eq("story") + end + + it "has draggable data attributes on story rows" do + render_component + + story_row = page.find(".Box-row[id='work_package_#{story1.id}']") + expect(story_row["data-draggable-id"]).to eq(story1.id.to_s) + expect(story_row["data-draggable-type"]).to eq("story") + expect(story_row["data-drop-url"]).to end_with(move_project_sprint_story_path(project, sprint, story1)) + end + + it "renders story rows with proper classes" do + render_component + + story_row = page.find(".Box-row[id='work_package_#{story1.id}']") + expect(story_row[:class]).to include("Box-row--hover-blue") + expect(story_row[:class]).to include("Box-row--focus-gray") + expect(story_row[:class]).to include("Box-row--clickable") + end + end + + context "without stories" do + let(:rendered_component) { render_component } + + it_behaves_like "rendering Blank Slate", heading: "Sprint 1 is empty" + end + end +end diff --git a/modules/backlogs/spec/components/backlogs/sprint_header_component_spec.rb b/modules/backlogs/spec/components/backlogs/sprint_header_component_spec.rb new file mode 100644 index 00000000000..2318fd41172 --- /dev/null +++ b/modules/backlogs/spec/components/backlogs/sprint_header_component_spec.rb @@ -0,0 +1,184 @@ +# 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 "rails_helper" + +RSpec.describe Backlogs::SprintHeaderComponent, type: :component do + shared_let(:type_feature) { create(:type_feature) } + shared_let(:type_task) { create(:type_task) } + shared_let(:default_status) { create(:default_status) } + shared_let(:default_priority) { create(:default_priority) } + shared_let(:user) { create(:admin) } + current_user { user } + + let(:project) { create(:project, types: [type_feature, type_task]) } + let(:start_date) { Date.new(2024, 1, 15) } + let(:finish_date) { Date.new(2024, 1, 29) } + let(:sprint) { create(:agile_sprint, project:, name: "Sprint 1", start_date:, finish_date:) } + let(:state) { :show } + let(:folded) { false } + + before do + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return("story_types" => [type_feature.id.to_s], "task_type" => type_task.id.to_s) + end + + def render_component(folded: false) + render_inline(described_class.new(sprint:, folded:, current_user: user)) + end + + describe "show state (default)" do + context "with stories" do + let!(:story1) do + create(:story, + project:, + type: type_feature, + status: default_status, + priority: default_priority, + story_points: 5, + sprint:) + end + let!(:story2) do + create(:story, + project:, + type: type_feature, + status: default_status, + priority: default_priority, + story_points: 3, + sprint:) + end + let!(:story_with_nil_points) do + create(:story, + project:, + type: type_feature, + status: default_status, + priority: default_priority, + story_points: nil, + sprint:) + end + + it "displays sprint name in h4" do + render_component + + expect(page).to have_css("h3", text: "Sprint 1") + end + + it "shows story count via Primer::Beta::Counter" do + render_component + + expect(page).to have_css(".Counter", text: "3") + end + + it "shows formatted date range with time tags" do + render_component + + expect(page).to have_css("time[datetime='2024-01-15']") + expect(page).to have_css("time[datetime='2024-01-29']") + end + + it "shows story points total (nil treated as 0)" do + render_component + + # 5 + 3 + 0 = 8 points + expect(page).to have_text("8 points", normalize_ws: true) + end + + it "renders collapse/expand chevrons" do + render_component + + expect(page).to have_octicon(:"chevron-up", visible: :all) + expect(page).to have_octicon(:"chevron-down", visible: :all) + end + + it "renders BacklogMenuComponent" do + render_component + + expect(page).to have_css("action-menu") + end + + it "renders a stable id on the sprint menu trigger" do + render_component + + expect(page).to have_element(:button, id: /\Aagile_sprint_#{sprint.id}_menu-button\z/) + end + end + + context "with no stories" do + let(:stories) { [] } + + it "shows 0 story count" do + render_component + + expect(page).to have_css(".Counter", text: "0") + end + + it "shows 0 points" do + render_component + + expect(page).to have_text("0 points", normalize_ws: true) + end + end + + context "when sprint has no dates" do + let(:sprint) { build_stubbed(:agile_sprint, project:, name: "Sprint 1", start_date: nil, finish_date: nil) } + + it "renders without date range" do + render_component + + expect(page).to have_no_css("time") + end + end + end + + describe "folded state" do + context "when folded is true" do + it "renders chevron-up hidden and chevron-down visible" do + render_component(folded: true) + + # When folded, chevron-up is hidden (has hidden attribute on svg) + # and chevron-down is visible (for expanding) + expect(page).to have_css("svg[hidden][data-target='collapsible-header.arrowUp']", visible: :hidden) + expect(page).to have_css("svg[data-target='collapsible-header.arrowDown']:not([hidden])", visible: :all) + end + end + + context "when folded is false" do + it "renders chevron-down hidden and chevron-up visible" do + render_component(folded: false) + + # When expanded, chevron-down is hidden (has hidden attribute) + # and chevron-up is visible (for collapsing) + expect(page).to have_css("svg[hidden][data-target='collapsible-header.arrowDown']", visible: :hidden) + expect(page).to have_css("svg[data-target='collapsible-header.arrowUp']:not([hidden])", visible: :all) + end + end + end +end diff --git a/modules/backlogs/spec/components/backlogs/sprint_menu_component_spec.rb b/modules/backlogs/spec/components/backlogs/sprint_menu_component_spec.rb new file mode 100644 index 00000000000..c2c96a07263 --- /dev/null +++ b/modules/backlogs/spec/components/backlogs/sprint_menu_component_spec.rb @@ -0,0 +1,122 @@ +# 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 "rails_helper" + +RSpec.describe Backlogs::SprintMenuComponent, type: :component do + shared_let(:type_feature) { create(:type_feature) } + shared_let(:type_task) { create(:type_task) } + + let(:project) { create(:project, types: [type_feature, type_task]) } + let(:sprint) { create(:agile_sprint, project:, name: "Sprint 1", start_date: Date.yesterday, finish_date: Date.tomorrow) } + let(:stories) { [] } + let(:user) { create(:user) } + let(:permissions) { [] } + + before do + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return("story_types" => [type_feature.id.to_s], "task_type" => type_task.id.to_s) + + # Set up user with specific permissions + create(:member, + project:, + principal: user, + roles: [create(:project_role, permissions:)]) + login_as(user) + end + + def render_component + render_inline(described_class.new(sprint:, project:, current_user: user)) + end + + describe "permission-based items" do + context "with :manage_sprint_items permission" do + let(:permissions) { %i[view_sprints manage_sprint_items] } + + it "shows Add new story item with compose icon" do + render_component + + expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.new_story")) + expect(page).to have_octicon(:compose) + end + end + + context "without :manage_sprint_items permission" do + let(:permissions) { [:view_sprints] } + + it "does not show Add new story item" do + render_component + + expect(page).to have_no_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.new_story")) + end + end + + context "with :create_sprints permission" do + let(:permissions) { %i[view_sprints create_sprints] } + + it "shows Edit item with pencil icon" do + render_component + + expect(page).to have_css("action-menu") + expect(page).to have_text(I18n.t("backlogs.backlog_menu_component.action_menu.edit_sprint")) + expect(page).to have_octicon(:pencil) + end + end + + context "without :create_sprints permission" do + let(:permissions) { [:view_sprints] } + + it "does not show Edit item" do + render_component + + expect(page).to have_no_text(I18n.t("backlogs.backlog_menu_component.action_menu.edit_sprint")) + end + end + end + + describe "always-visible items" do + let(:permissions) { [:view_sprints] } + + it "renders stable ids on the action menu and stories/tasks item" do + render_component + + expect(page).to have_element(:button, id: /\Aagile_sprint_#{sprint.id}_menu-button\z/) + expect(page).to have_element(:ul, id: /\Aagile_sprint_#{sprint.id}_menu-list\z/) + expect(page).to have_element(:a, id: /\Aagile_sprint_#{sprint.id}_menu_stories_tasks\z/) + end + + it "shows Stories/Tasks link" do + render_component + + expect(page).to have_text(I18n.t(:"backlogs.backlog_menu_component.action_menu.stories_tasks")) + end + end +end diff --git a/modules/backlogs/spec/components/backlogs/story_component_spec.rb b/modules/backlogs/spec/components/backlogs/story_component_spec.rb index 5481e6bfcbf..8cd5e3207eb 100644 --- a/modules/backlogs/spec/components/backlogs/story_component_spec.rb +++ b/modules/backlogs/spec/components/backlogs/story_component_spec.rb @@ -97,6 +97,13 @@ RSpec.describe Backlogs::StoryComponent, type: :component do expect(page).to have_css("action-menu") end + + it "renders a drag handle compatible with GenericDragAndDropController" do + render_component + + expect(page).to have_css(".DragHandle[role='button'][tabindex='0']") + expect(page).to have_css(".DragHandle[aria-label='Move Test Story Subject']") + end end describe "story points handling" do diff --git a/modules/backlogs/spec/contracts/projects/backlog_settings_contract_spec.rb b/modules/backlogs/spec/contracts/projects/backlog_settings_contract_spec.rb new file mode 100644 index 00000000000..510289290d1 --- /dev/null +++ b/modules/backlogs/spec/contracts/projects/backlog_settings_contract_spec.rb @@ -0,0 +1,132 @@ +# frozen_string_literal: true + +require "spec_helper" +require "contracts/shared/model_contract_shared_context" + +RSpec.describe Projects::BacklogSettingsContract, type: :model do + include_context "ModelContract shared context" + + let(:current_user) { build_stubbed(:user) } + let(:project) { create(:project) } + let(:permissions) { %i(share_sprint) } + + subject(:contract) { described_class.new(project, current_user) } + + before do + mock_permissions_for(current_user) do |mock| + mock.allow_in_project(*permissions, project:) + mock.allow_in_project(*other_permissions, project: other_project) if defined?(other_project) + end + end + + it "is expected to be a subclass of ModelContract" do + expect(described_class).to be < ModelContract + end + + describe "validations" do + it_behaves_like "contract is valid" + + it { expect(subject).to validate_presence_of(:sprint_sharing) } + + it do + expect(subject) + .to validate_inclusion_of(:sprint_sharing).in_array(Project::SPRINT_SHARING_MODES) + end + + # This spec of explicitly setting sprint_sharing to empty is required because the + # simple presence validation spec is not sufficient to catch certain corner cases. + # For example, when the sprint_sharing getter is overriden to provide a default value, + # and the user submits an empty value, the contract should be invalid. + context "when sprint_sharing is empty" do + before { project.sprint_sharing = "" } + + it_behaves_like "contract is invalid", sprint_sharing: :blank + end + + describe "permissions" do + context "when user can share sprint" do + let(:permissions) { %i(share_sprint) } + + it_behaves_like "contract is valid" + end + + context "when user cannot share sprint" do + let(:permissions) { [] } + + it_behaves_like "contract user is unauthorized" + end + end + + describe "#validate_global_sprint_sharer_uniqueness" do + before do + project.sprint_sharing = "share_all_projects" + end + + context "when no other project shares with all projects" do + it_behaves_like "contract is valid" + end + + context "when the project already has share_all_projects" do + let(:project) { create(:project, sprint_sharing: "share_all_projects") } + + it_behaves_like "contract is valid" + end + + context "when another project already shares with all projects" do + let!(:other_project) { create(:project, sprint_sharing: "share_all_projects") } + let(:other_permissions) { %i(view_project) } + + it_behaves_like "contract is invalid", sprint_sharing: :share_all_projects_already_taken + + context "when sprint_sharing is set to Share subprojects" do + before { project.sprint_sharing = "share_subprojects" } + + it_behaves_like "contract is valid" + end + + context "when the other project is archived" do + let!(:other_project) { create(:project, :archived, sprint_sharing: "share_all_projects") } + + it_behaves_like "contract is valid" + end + + context "when the current user cannot see the other project" do + let(:other_permissions) { [] } + + it_behaves_like "contract is invalid", sprint_sharing: :share_all_projects_already_taken_anonymous + end + end + end + end + + describe "#writable_attributes" do + it "only allows sprint_sharing to be written" do + expect(contract.writable_attributes).to include("sprint_sharing") + expect(contract.writable_attributes).not_to include("settings") + expect(contract.writable_attributes).not_to include("deactivate_work_package_attachments") + end + + context "when sprint_sharing is the only changed setting" do + before { project.sprint_sharing = "share_subprojects" } + + it "includes the settings column too" do + expect(contract.writable_attributes).to include("settings") + end + + it_behaves_like "contract is valid" + end + + context "when other settings keys are also changed" do + before do + project.sprint_sharing = "share_subprojects" + project.deactivate_work_package_attachments = true + end + + it "excludes the settings column" do + expect(contract.writable_attributes).not_to include("settings") + end + + it_behaves_like "contract is invalid", settings: :error_readonly + end + end +end diff --git a/modules/backlogs/spec/contracts/sprints/create_contract_spec.rb b/modules/backlogs/spec/contracts/sprints/create_contract_spec.rb new file mode 100644 index 00000000000..f542fac55b7 --- /dev/null +++ b/modules/backlogs/spec/contracts/sprints/create_contract_spec.rb @@ -0,0 +1,115 @@ +# 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 "spec_helper" +require "contracts/shared/model_contract_shared_context" + +RSpec.describe Sprints::CreateContract do + include_context "ModelContract shared context" + + let(:project) { build_stubbed(:project) } + let(:user) { build_stubbed(:user) } + let(:sprint) do + Agile::Sprint.new(name: sprint_name, + project:, + start_date: sprint_start_date, + finish_date: sprint_finish_date, + status: sprint_status) + end + let(:sprint_name) { "Sprint 1" } + let(:sprint_start_date) { Time.zone.today } + let(:sprint_finish_date) { Time.zone.today + 14.days } + let(:sprint_status) { "in_planning" } + let(:permissions) { [:create_sprints] } + + subject(:contract) { described_class.new(sprint, user) } + + before do + mock_permissions_for(user) do |mock| + mock.allow_in_project(*permissions, project:) if project + end + end + + describe "validation" do + context "with valid attributes and permissions" do + it_behaves_like "contract is valid" + end + + context "when project is nil" do + let(:project) { nil } + + it_behaves_like "contract is invalid", project: :blank + end + + context "when user does not have create_sprints permission" do + let(:permissions) { [:view_work_packages] } + + it_behaves_like "contract is invalid", base: :error_unauthorized + end + + context "when user has no permissions in project" do + let(:permissions) { [] } + + it_behaves_like "contract is invalid", base: :error_unauthorized + end + + context "when name is blank" do + let(:sprint_name) { "" } + + it_behaves_like "contract is invalid", name: :blank + end + + context "when start_date is blank" do + let(:sprint_start_date) { nil } + + it_behaves_like "contract is invalid", start_date: :blank + end + + context "when finish_date is blank" do + let(:sprint_finish_date) { nil } + + it_behaves_like "contract is invalid", finish_date: %i[blank blank] + end + + context "when finish_date is before start_date" do + let(:sprint_start_date) { Time.zone.today } + let(:sprint_finish_date) { Time.zone.today - 1.day } + + it_behaves_like "contract is invalid", finish_date: %i[greater_than_or_equal_to] + end + + context "when user is admin without project permission" do + let(:user) { build_stubbed(:admin) } + let(:permissions) { [] } + + it_behaves_like "contract is valid" + end + end +end diff --git a/modules/backlogs/spec/controllers/projects/settings/backlog_sharings_controller_spec.rb b/modules/backlogs/spec/controllers/projects/settings/backlog_sharings_controller_spec.rb new file mode 100644 index 00000000000..100e317df11 --- /dev/null +++ b/modules/backlogs/spec/controllers/projects/settings/backlog_sharings_controller_spec.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Projects::Settings::BacklogSharingsController, with_flag: { scrum_projects: true } do + shared_let(:user) { create(:admin) } + + current_user { user } + + before do + visible_relation = instance_double(ActiveRecord::Relation) + allow(Project).to receive(:visible).and_return(visible_relation) + allow(visible_relation).to receive(:find).with(project.identifier).and_return(project) + end + + describe "PATCH #update" do + let(:project) { build_stubbed(:project, sprint_sharing: "no_sharing") } + let(:service_result) { ServiceResult.success(result: project) } + let(:update_service) { instance_double(Projects::UpdateService, call: service_result) } + + before do + allow(Projects::UpdateService) + .to receive(:new) + .with(model: project, user:, contract_class: Projects::BacklogSettingsContract) + .and_return(update_service) + + patch :update, params: { project_id: project.identifier, project: project_params } + end + + context "when service call succeeds" do + let(:project_params) { { sprint_sharing: "share_subprojects", name: "must_be_ignored" } } + + it "updates sprint sharing and redirects to show", :aggregate_failures do + expect(update_service).to have_received(:call).with( + ActionController::Parameters.new("sprint_sharing" => "share_subprojects").permit! + ) + expect(response).to redirect_to(project_settings_backlog_sharing_path(project)) + expect(flash[:notice]).to include I18n.t(:notice_successful_update) + end + end + + context "when service call fails" do + let(:service_result) { ServiceResult.failure(result: project, message: "invalid setting") } + let(:project_params) { { sprint_sharing: "invalid_option" } } + + it "renders show with an error", :aggregate_failures do + expect(response).to have_http_status(:unprocessable_entity) + expect(response).to render_template("projects/settings/backlog_sharings/show") + expect(flash[:error]).to eq I18n.t(:notice_unsuccessful_update_with_reason, reason: "invalid setting") + end + end + end + + context "when scrum_projects feature flag is inactive", with_flag: { scrum_projects: false } do + let(:project) { build_stubbed(:project) } + + it "returns 404 for show" do + get :show, params: { project_id: project.identifier } + + expect(response).to have_http_status(:not_found) + end + + it "returns 404 for update" do + patch :update, params: { project_id: project.identifier, project: { sprint_sharing: "no_sharing" } } + + expect(response).to have_http_status(:not_found) + end + end +end diff --git a/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.rb b/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.rb index e0a372b0f3c..d415deb0221 100644 --- a/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.rb +++ b/modules/backlogs/spec/controllers/rb_sprints_controller_permissions_spec.rb @@ -39,7 +39,7 @@ RSpec.describe RbSprintsController, "permissions" do create(:project, enabled_module_names: %w[work_package_tracking backlogs]).tap do |p| create(:member, user: current_user, - roles: [create(:project_role, permissions: [:update_sprints])], + roles: [create(:project_role, permissions: [:create_sprints])], project: p) end end @@ -80,7 +80,7 @@ RSpec.describe RbSprintsController, "permissions" do before do create(:member, user: current_user, - roles: [create(:project_role, permissions: %i[view_work_packages view_versions update_sprints])], + roles: [create(:project_role, permissions: %i[view_work_packages view_versions create_sprints])], project: sprint_project) end diff --git a/modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb b/modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb index 4219debbf88..396fb2b9e1b 100644 --- a/modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb +++ b/modules/backlogs/spec/controllers/rb_sprints_controller_spec.rb @@ -31,123 +31,362 @@ require "rails_helper" RSpec.describe RbSprintsController do - shared_let(:type_feature) { create(:type_feature) } - shared_let(:type_task) { create(:type_task) } - shared_let(:user) { create(:admin) } - current_user { user } + describe "inline name actions" do + shared_let(:type_feature) { create(:type_feature) } + shared_let(:type_task) { create(:type_task) } + shared_let(:user) { create(:admin) } + current_user { user } - let(:visible_projects_scope) { instance_double(ActiveRecord::Relation) } - let(:visible_sprints_scope) { instance_double(ActiveRecord::Relation) } - - before do - allow(Setting) - .to receive(:plugin_openproject_backlogs) - .and_return({ "story_types" => [type_feature.id], "task_type" => type_task.id }) - - allow(Project) - .to receive(:visible) - .and_return(visible_projects_scope) - - allow(visible_projects_scope) - .to receive(:find) - .with(project.identifier) - .and_return(project) - - allow(Sprint) - .to receive(:visible) - .and_return(visible_sprints_scope) - - allow(visible_sprints_scope) - .to receive(:find) - .with(sprint.id.to_s) - .and_return(sprint) - end - - describe "GET #edit_name" do - let(:project) { build_stubbed(:project) } - let(:sprint) { build_stubbed(:sprint) } - - it "responds with success", :aggregate_failures do - get :edit_name, params: { project_id: project.identifier, id: sprint.id }, format: :turbo_stream - - expect(response).to be_successful - expect(response).to have_http_status :ok - expect(response).to have_turbo_stream action: "update", target: "backlogs-backlog-header-component-#{sprint.id}" - assert_select %(turbo-stream[action="update"][target="backlogs-backlog-header-component-#{sprint.id}"][method="morph"]) - expect(assigns(:project)).to eq(project) - expect(assigns(:sprint)).to eq(sprint) - expect(assigns(:backlog)).to be_a(Backlog) - end - end - - describe "GET #show_name" do - let(:project) { build_stubbed(:project) } - let(:sprint) { build_stubbed(:sprint) } - - it "responds with success", :aggregate_failures do - get :show_name, params: { project_id: project.identifier, id: sprint.id }, format: :turbo_stream - - expect(response).to be_successful - expect(response).to have_http_status :ok - expect(response).to have_turbo_stream action: "update", target: "backlogs-backlog-header-component-#{sprint.id}" - assert_select %(turbo-stream[action="update"][target="backlogs-backlog-header-component-#{sprint.id}"][method="morph"]) - expect(assigns(:project)).to eq(project) - expect(assigns(:sprint)).to eq(sprint) - expect(assigns(:backlog)).to be_a(Backlog) - end - end - - describe "PATCH #update" do - let(:project) { build_stubbed(:project) } - let(:sprint) { build_stubbed(:sprint) } + let(:visible_projects_scope) { instance_double(ActiveRecord::Relation) } + let(:visible_sprints_scope) { instance_double(ActiveRecord::Relation) } before do - update_service = instance_double(Versions::UpdateService, call: service_result) + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return({ "story_types" => [type_feature.id], "task_type" => type_task.id }) - allow(Versions::UpdateService) - .to receive(:new) - .with(user:, model: sprint) - .and_return(update_service) + allow(Project) + .to receive(:visible) + .and_return(visible_projects_scope) + + allow(visible_projects_scope) + .to receive(:find) + .with(project.identifier) + .and_return(project) + + allow(Sprint) + .to receive(:visible) + .and_return(visible_sprints_scope) + + allow(visible_sprints_scope) + .to receive(:find) + .with(sprint.id.to_s) + .and_return(sprint) end - context "when service call succeeds" do - let(:service_result) { ServiceResult.success(result: sprint) } + describe "GET #edit_name" do + let(:project) { build_stubbed(:project) } + let(:sprint) { build_stubbed(:sprint) } it "responds with success", :aggregate_failures do - patch :update, params: { project_id: project.identifier, id: sprint.id, sprint: { name: "Updated Sprint" } }, - format: :turbo_stream + get :edit_name, params: { project_id: project.identifier, id: sprint.id }, format: :turbo_stream expect(response).to be_successful expect(response).to have_http_status :ok expect(response).to have_turbo_stream action: "update", target: "backlogs-backlog-header-component-#{sprint.id}" assert_select %(turbo-stream[action="update"][target="backlogs-backlog-header-component-#{sprint.id}"][method="morph"]) - expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" expect(assigns(:project)).to eq(project) expect(assigns(:sprint)).to eq(sprint) expect(assigns(:backlog)).to be_a(Backlog) end end - context "when service call fails" do - let(:service_result) { ServiceResult.failure(result: sprint) } + describe "GET #show_name" do + let(:project) { build_stubbed(:project) } + let(:sprint) { build_stubbed(:sprint) } - before do - project.name = "" - end + it "responds with success", :aggregate_failures do + get :show_name, params: { project_id: project.identifier, id: sprint.id }, format: :turbo_stream - it "responds with 422", :aggregate_failures do - patch :update, params: { project_id: project.identifier, id: sprint.id, sprint: { name: "" } }, - format: :turbo_stream - - expect(response).not_to be_successful - expect(response).to have_http_status :unprocessable_entity + expect(response).to be_successful + expect(response).to have_http_status :ok expect(response).to have_turbo_stream action: "update", target: "backlogs-backlog-header-component-#{sprint.id}" assert_select %(turbo-stream[action="update"][target="backlogs-backlog-header-component-#{sprint.id}"][method="morph"]) - expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" expect(assigns(:project)).to eq(project) expect(assigns(:sprint)).to eq(sprint) expect(assigns(:backlog)).to be_a(Backlog) end end + + describe "PATCH #update" do + let(:project) { build_stubbed(:project) } + let(:sprint) { build_stubbed(:sprint) } + + before do + update_service = instance_double(Versions::UpdateService, call: service_result) + + allow(Versions::UpdateService) + .to receive(:new) + .with(user:, model: sprint) + .and_return(update_service) + end + + context "when service call succeeds" do + let(:service_result) { ServiceResult.success(result: sprint) } + + it "responds with success", :aggregate_failures do + patch :update, params: { project_id: project.identifier, id: sprint.id, sprint: { name: "Updated Sprint" } }, + format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "update", target: "backlogs-backlog-header-component-#{sprint.id}" + assert_select %(turbo-stream[action="update"][target="backlogs-backlog-header-component-#{sprint.id}"][method="morph"]) + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(sprint) + expect(assigns(:backlog)).to be_a(Backlog) + end + end + + context "when service call fails" do + let(:service_result) { ServiceResult.failure(result: sprint) } + + before do + project.name = "" + end + + it "responds with 422", :aggregate_failures do + patch :update, params: { project_id: project.identifier, id: sprint.id, sprint: { name: "" } }, + format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :unprocessable_entity + expect(response).to have_turbo_stream action: "update", target: "backlogs-backlog-header-component-#{sprint.id}" + assert_select %(turbo-stream[action="update"][target="backlogs-backlog-header-component-#{sprint.id}"][method="morph"]) + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(sprint) + expect(assigns(:backlog)).to be_a(Backlog) + end + end + end + end + + describe "new actions" do + shared_let(:type_feature) { create(:type_feature) } + shared_let(:type_task) { create(:type_task) } + + let(:all_permissions) { %i[view_sprints view_work_packages create_sprints] } + let(:permissions) { all_permissions } + let(:user) do + create(:user, member_with_permissions: { project => permissions }) + end + let(:project) { create(:project) } + + current_user { user } + + before do + # Necessary to get the controller running due to check_if_plugin_is_configured + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return({ "story_types" => [type_feature.id], "task_type" => type_task.id }) + end + + describe "GET #new_dialog" do + context "with the feature flag inactive" do + it "responds with forbidden" do + get :new_dialog, params: { project_id: project.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + it "responds with success", :aggregate_failures do + get :new_dialog, params: { project_id: project.id }, format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "dialog", target: "backlogs-new-sprint-dialog-component" + expect(assigns(:project)).to eq(project) + end + + context "without the 'create_sprints' permission" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "responds with forbidden", :aggregate_failures do + get :new_dialog, params: { project_id: project.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + end + end + + describe "GET #edit_dialog" do + let!(:sprint) { create(:agile_sprint, project:) } + + context "with the feature flag inactive" do + it "responds with forbidden" do + get :edit_dialog, params: { project_id: project.id, id: sprint.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + it "responds with success", :aggregate_failures do + get :edit_dialog, params: { project_id: project.id, id: sprint.id }, format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "dialog", target: "backlogs-new-sprint-dialog-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(sprint) + end + + context "without the 'create_sprints' permission" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "responds with forbidden", :aggregate_failures do + get :edit_dialog, params: { project_id: project.id, id: sprint.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + end + end + + describe "POST #create" do + context "with the feature flag inactive" do + it "responds with forbidden" do + post :create, params: { project_id: project.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + let(:params) do + { + project_id: project.id, + sprint: { name: "My Sprint", start_date: "2025-10-05", finish_date: "2025-10-15" } + } + end + + it "responds with success, creates a sprint, and redirects to backlogs", :aggregate_failures do + post :create, format: :turbo_stream, params: params + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response.body).to include("turbo-stream") + expect(response.body).to include("action=\"redirect_to\"") + expect(response.body).to include(backlogs_project_backlogs_path(project)) + expect(project.reload.sprints.last.name).to eq("My Sprint") + expect(flash[:notice]).to eq(I18n.t(:notice_successful_create)) + end + + context "without the 'create_sprints' permission" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "responds with forbidden", :aggregate_failures do + post :create, format: :turbo_stream, params: params + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + end + end + + describe "PUT #update_agile_sprint" do + let!(:sprint) { create(:agile_sprint, name: "Original sprint name", project:) } + + context "with the feature flag inactive" do + it "responds with forbidden" do + put :update_agile_sprint, params: { id: sprint.id, project_id: project.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + let(:params) do + { + id: sprint.id, + project_id: project.id, + sprint: { name: "Changed sprint name" } + } + end + + it "responds with success", :aggregate_failures do + put :update_agile_sprint, format: :turbo_stream, params: params + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response.body).to have_turbo_stream action: "flash" + expect(response.body).to have_turbo_stream action: "update", target: "backlogs-sprint-header-component-#{sprint.id}" + assert_select %(turbo-stream[action="update"][target="backlogs-sprint-header-component-#{sprint.id}"][method="morph"]) + expect(response.body).to include("Successful update.") + expect(sprint.reload.name).to eq("Changed sprint name") + end + + context "without the 'create_sprints' permission" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "responds with forbidden", :aggregate_failures do + put :update_agile_sprint, format: :turbo_stream, params: params + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + end + end + + describe "GET #refresh_form" do + context "with the feature flag inactive" do + it "responds with forbidden" do + get :refresh_form, params: { project_id: project.id }, format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + let(:params) do + { + project_id: project.id, + sprint: { name: "My Sprint", start_date: "2025-10-05", finish_date: "2025-10-15" } + } + end + + it "responds with success", :aggregate_failures do + get :refresh_form, format: :turbo_stream, params: params + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "update", target: "backlogs-new-sprint-form-component" + expect(assigns(:sprint)).to be_nil + end + + context "without the 'create_sprints' permission" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "responds with forbidden", :aggregate_failures do + get :refresh_form, format: :turbo_stream, params: params + + expect(response).not_to be_successful + expect(response).to have_http_status :forbidden + end + end + + context "when refreshing the form in edit mode by passing a sprint id" do + let!(:sprint) { create(:agile_sprint, project:) } + let(:params) do + { + project_id: project.id, + sprint: { id: sprint.id, name: "My Sprint", start_date: "2025-10-05", finish_date: "2025-10-15" } + } + end + + it "responds with success", :aggregate_failures do + get :refresh_form, format: :turbo_stream, params: params + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "update", target: "backlogs-new-sprint-form-component" + end + end + end + end end end diff --git a/modules/backlogs/spec/controllers/rb_stories_controller_spec.rb b/modules/backlogs/spec/controllers/rb_stories_controller_spec.rb index dd59a5b2903..6fd8581ca2c 100644 --- a/modules/backlogs/spec/controllers/rb_stories_controller_spec.rb +++ b/modules/backlogs/spec/controllers/rb_stories_controller_spec.rb @@ -33,43 +33,65 @@ require "rails_helper" RSpec.describe RbStoriesController do shared_let(:type_feature) { create(:type_feature) } shared_let(:type_task) { create(:type_task) } - shared_let(:user) { create(:admin) } + current_user { user } + let(:user) { create(:admin) } let(:project) { create(:project) } - let(:status) { create(:status, name: "status 1", is_default: true) } - let(:sprint) { create(:sprint, project:) } - let(:story) { create(:story, status:, version: sprint, project:) } + let(:status) { create(:status, name: "status 1", is_default: true) } + let(:version_sprint) { create(:sprint, project:) } + let(:story) { create(:story, status:, version: version_sprint, project:) } + + # Via this setting, version_sprint is used as backlog: + let!(:version_setting) { create(:version_setting, version: version_sprint, project:, display: VersionSetting::DISPLAY_RIGHT) } before do allow(Setting) .to receive(:plugin_openproject_backlogs) - .and_return({ "story_types" => [type_feature.id], "task_type" => type_task.id }) + .and_return({ "story_types" => [type_feature.id], "task_type" => type_task.id }) end - describe "PUT #move" do + describe "PUT #move_legacy" do + context "with a user lacking project permission" do + let(:user) { create(:user) } + + it "responds with 403" do + put :move_legacy, params: { + project_id: project.id, + sprint_id: version_sprint.id, + id: story.id, + target_id: "foo", + position: 1 + }, + format: :turbo_stream + + expect(response).not_to be_successful + expect(response).to have_http_status :not_found + end + end + context "with a version from the same project" do - let(:other_sprint) { create(:sprint, name: "Sprint 2", project:) } + let(:other_version_sprint) { create(:sprint, name: "Sprint 2", project:) } it "responds with success", :aggregate_failures do - put :move, params: { - project_id: project.id, - sprint_id: sprint.id, - id: story.id, - target_id: other_sprint.id, - position: 1 - }, - format: :turbo_stream + put :move_legacy, params: { + project_id: project.id, + sprint_id: version_sprint.id, + id: story.id, + target_id: "version:#{other_version_sprint.id}", + position: 1 + }, + format: :turbo_stream expect(response).to be_successful expect(response).to have_http_status :ok - expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{sprint.id}" - expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{other_sprint.id}" - assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{sprint.id}"][method="morph"]) - assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{other_sprint.id}"][method="morph"]) + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{other_version_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{version_sprint.id}"][method="morph"]) + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{other_version_sprint.id}"][method="morph"]) # rubocop:disable Layout/LineLength expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" expect(assigns(:project)).to eq(project) - expect(assigns(:sprint)).to eq(sprint) + expect(assigns(:sprint)).to eq(version_sprint) expect(assigns(:story)).to eq(story) expect(assigns(:backlog)).to be_a(Backlog) end @@ -77,35 +99,64 @@ RSpec.describe RbStoriesController do context "with a version from another project" do let(:other_project) { create(:project) } - let(:other_sprint) { create(:sprint, name: "Sprint 2", project: other_project, sharing: "system") } - let(:story) { create(:story, status:, version: other_sprint, project:) } + let(:other_version_sprint) { create(:sprint, name: "Sprint 2", project: other_project, sharing: "system") } + let(:story) { create(:story, status:, version: other_version_sprint, project:) } it "responds with success", :aggregate_failures do - put :move, params: { - project_id: project.id, - sprint_id: other_sprint.id, - id: story.id, - target_id: sprint.id, - position: 1 - }, - format: :turbo_stream + put :move_legacy, params: { + project_id: project.id, + sprint_id: other_version_sprint.id, + id: story.id, + target_id: "version:#{version_sprint.id}", + position: 1 + }, + format: :turbo_stream expect(response).to be_successful expect(response).to have_http_status :ok - expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{other_sprint.id}" - expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{sprint.id}" - assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{other_sprint.id}"][method="morph"]) - assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{sprint.id}"][method="morph"]) + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{other_version_sprint.id}" + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{other_version_sprint.id}"][method="morph"]) # rubocop:disable Layout/LineLength + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{version_sprint.id}"][method="morph"]) expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" expect(assigns(:project)).to eq(project) - expect(assigns(:sprint)).to eq(other_sprint) + expect(assigns(:sprint)).to eq(other_version_sprint) expect(assigns(:story)).to eq(story) expect(assigns(:backlog)).to be_a(Backlog) end end + context "with an Agile::Sprint as target" do + let(:agile_sprint) { create(:agile_sprint, name: "Agile Sprint 1", project:) } + + it "responds with success and moves story to Agile::Sprint, removing the association to the version", :aggregate_failures do + put :move_legacy, params: { + project_id: project.id, + sprint_id: version_sprint.id, + id: story.id, + target_id: "sprint:#{agile_sprint.id}", + position: 1 + }, + format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" + expect(response).to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{agile_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{version_sprint.id}"][method="morph"]) + assert_select %(turbo-stream[action="replace"][target="backlogs-sprint-component-#{agile_sprint.id}"]) + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(version_sprint) + expect(assigns(:story)).to eq(story) + + # It will remove the association to the version since the version is used as backlog: + expect(story.reload.version).to be_nil + end + end + context "when service call fails" do - let(:other_sprint) { create(:sprint, name: "Sprint 2", project:) } + let(:other_version_sprint) { create(:sprint, name: "Sprint 2", project:) } let(:service_result) { ServiceResult.failure(message: "Something went wrong") } before do @@ -117,33 +168,33 @@ RSpec.describe RbStoriesController do end it "renders an error flash with 422", :aggregate_failures do - put :move, params: { - project_id: project.id, - sprint_id: sprint.id, - id: story.id, - target_id: other_sprint.id, - position: 1 - }, - format: :turbo_stream + put :move_legacy, params: { + project_id: project.id, + sprint_id: version_sprint.id, + id: story.id, + target_id: "version:#{other_version_sprint.id}", + position: 1 + }, + format: :turbo_stream expect(response).to have_http_status :unprocessable_entity expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" - expect(response).not_to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{sprint.id}" + expect(response).not_to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" end end end describe "POST #reorder" do it "responds with success", :aggregate_failures do - post :reorder, params: { project_id: project.id, sprint_id: sprint.id, id: story.id, direction: "highest" }, + post :reorder, params: { project_id: project.id, sprint_id: version_sprint.id, id: story.id, direction: "highest" }, format: :turbo_stream expect(response).to be_successful expect(response).to have_http_status :ok - expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{sprint.id}" - assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{sprint.id}"][method="morph"]) + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{version_sprint.id}"][method="morph"]) expect(assigns(:project)).to eq(project) - expect(assigns(:sprint)).to eq(sprint) + expect(assigns(:sprint)).to eq(version_sprint) expect(assigns(:story)).to eq(story) expect(assigns(:backlog)).to be_a(Backlog) end @@ -160,12 +211,127 @@ RSpec.describe RbStoriesController do end it "renders an error flash with 422", :aggregate_failures do - post :reorder, params: { project_id: project.id, sprint_id: sprint.id, id: story.id, direction: "highest" }, + post :reorder, params: { project_id: project.id, sprint_id: version_sprint.id, id: story.id, direction: "highest" }, format: :turbo_stream expect(response).to have_http_status :unprocessable_entity expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" - expect(response).not_to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{sprint.id}" + expect(response).not_to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" + end + end + end + + describe "PUT #move" do + let(:agile_sprint) { create(:agile_sprint, name: "Agile Sprint 1", project:) } + let(:story_in_agile_sprint) { create(:work_package, status:, sprint: agile_sprint, project:) } + + context "with another Agile::Sprint as target" do + let(:other_agile_sprint) { create(:agile_sprint, name: "Agile Sprint 2", project:) } + + it "responds with success and moves story to another Agile::Sprint", :aggregate_failures do + put :move, params: { + project_id: project.id, + sprint_id: agile_sprint.id, + id: story_in_agile_sprint.id, + target_id: "sprint:#{other_agile_sprint.id}", + position: 1 + }, + format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{agile_sprint.id}" + expect(response).to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{other_agile_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-sprint-component-#{agile_sprint.id}"]) + assert_select %(turbo-stream[action="replace"][target="backlogs-sprint-component-#{other_agile_sprint.id}"]) + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(agile_sprint) + expect(assigns(:story)).to eq(story_in_agile_sprint) + end + + context "when the story has a version that is not used as backlog" do + let(:story_in_agile_sprint) { create(:work_package, status:, sprint: agile_sprint, version: version_sprint, project:) } + # Via this setting, version_sprint is NOT used as backlog: + let!(:version_setting) { create(:version_setting, version: version_sprint, project:, display: VersionSetting::DISPLAY_NONE) } + + it "responds with success and moves story to Agile::Sprint, keeping the version", :aggregate_failures do + put :move, params: { + project_id: project.id, + sprint_id: agile_sprint.id, + id: story_in_agile_sprint.id, + target_id: "sprint:#{other_agile_sprint.id}", + position: 1 + }, + format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{agile_sprint.id}" + expect(response).to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{other_agile_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-sprint-component-#{agile_sprint.id}"]) + assert_select %(turbo-stream[action="replace"][target="backlogs-sprint-component-#{other_agile_sprint.id}"]) + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(agile_sprint) + expect(assigns(:story)).to eq(story_in_agile_sprint) + + # It will preserve the version since it is not used as backlog/sprint. + expect(story_in_agile_sprint.reload.version).to eq(version_sprint) + end + end + end + + context "with a Sprint (Version) as target" do + it "responds with success and moves story to Sprint", :aggregate_failures do + put :move, params: { + project_id: project.id, + sprint_id: agile_sprint.id, + id: story_in_agile_sprint.id, + target_id: "version:#{version_sprint.id}", + position: 1 + }, + format: :turbo_stream + + expect(response).to be_successful + expect(response).to have_http_status :ok + expect(response).to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{agile_sprint.id}" + expect(response).to have_turbo_stream action: "replace", target: "backlogs-backlog-component-#{version_sprint.id}" + assert_select %(turbo-stream[action="replace"][target="backlogs-sprint-component-#{agile_sprint.id}"]) + assert_select %(turbo-stream[action="replace"][target="backlogs-backlog-component-#{version_sprint.id}"][method="morph"]) + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(assigns(:project)).to eq(project) + expect(assigns(:sprint)).to eq(agile_sprint) + expect(assigns(:story)).to eq(story_in_agile_sprint) + expect(assigns(:backlog)).to be_a(Backlog) + end + end + + context "when service call fails" do + let(:other_agile_sprint) { create(:agile_sprint, name: "Agile Sprint 2", project:) } + let(:service_result) { ServiceResult.failure(message: "Something went wrong") } + + before do + update_service = instance_double(Stories::UpdateService, call: service_result) + + allow(Stories::UpdateService) + .to receive(:new) + .and_return(update_service) + end + + it "renders an error flash with 422", :aggregate_failures do + put :move, params: { + project_id: project.id, + sprint_id: agile_sprint.id, + id: story_in_agile_sprint.id, + target_id: "sprint:#{other_agile_sprint.id}", + position: 1 + }, + format: :turbo_stream + + expect(response).to have_http_status :unprocessable_entity + expect(response).to have_turbo_stream action: "flash", target: "op-primer-flash-component" + expect(response).not_to have_turbo_stream action: "replace", target: "backlogs-sprint-component-#{agile_sprint.id}" end end end diff --git a/modules/backlogs/spec/controllers/rb_taskboards_controller_spec.rb b/modules/backlogs/spec/controllers/rb_taskboards_controller_spec.rb new file mode 100644 index 00000000000..b474ea826a8 --- /dev/null +++ b/modules/backlogs/spec/controllers/rb_taskboards_controller_spec.rb @@ -0,0 +1,88 @@ +# 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 "spec_helper" + +RSpec.describe RbTaskboardsController do + shared_let(:type_feature) { create(:type_feature) } + shared_let(:type_task) { create(:type_task) } + shared_let(:user) { create(:admin) } + current_user { user } + + let(:permissions) { [] } + let(:project) { create(:project, member_with_permissions: { user => permissions }) } + let(:status) { create(:status, name: "status 1", is_default: true) } + let(:sprint) { create(:sprint, project:) } + let(:story) { create(:story, status:, version: sprint, project:) } + + before do + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return({ "story_types" => [type_feature.id], "task_type" => type_task.id }) + end + + describe "GET show" do + before do + get :show, params: { project_id: project.identifier, sprint_id: sprint.id } + end + + it "performs that request" do + expect(response).to be_successful + expect(response).to render_template :show + end + + context "as a member with view_sprints permission" do + let(:user) { create(:user) } + let(:permissions) { %i[view_sprints view_work_packages] } + + it "grants access" do + expect(response).to be_successful + expect(response).to render_template :show + end + end + + context "as a member without view_sprints permission" do + let(:user) { create(:user) } + let(:permissions) { [:view_project] } + + it "denies access" do + expect(response).to have_http_status(:not_found) + end + end + + context "as a non-member" do + current_user { create(:user) } + + it "denies access" do + expect(response).to have_http_status(:not_found) + end + end + end +end diff --git a/modules/backlogs/spec/features/backlogs/context_menu_spec.rb b/modules/backlogs/spec/features/backlogs/context_menu_spec.rb index 540699b5aea..5800e48d2ca 100644 --- a/modules/backlogs/spec/features/backlogs/context_menu_spec.rb +++ b/modules/backlogs/spec/features/backlogs/context_menu_spec.rb @@ -38,9 +38,9 @@ RSpec.describe "Backlogs context menu", :js do shared_let(:user) do create(:user, member_with_permissions: { project => %i[add_work_packages - view_master_backlog - view_taskboards - view_work_packages] }) + view_sprints + view_work_packages + manage_sprint_items] }) end shared_let(:sprint) do create(:version, @@ -134,9 +134,9 @@ RSpec.describe "Backlogs context menu", :js do end end - context "when the user does not have add_work_packages permission" do + context "when the user does not have manage_sprint_items permission" do before do - RolePermission.where(permission: "add_work_packages").delete_all + RolePermission.where(permission: "manage_sprint_items").delete_all end it 'does not display the "New story" menu entry' do @@ -146,18 +146,6 @@ RSpec.describe "Backlogs context menu", :js do end end - context "when the user does not have view_taskboards permission" do - before do - RolePermission.where(permission: "view_taskboards").delete_all - end - - it 'does not display the "Task board" menu entry' do - within_backlog_context_menu do |menu| - expect(menu).to have_no_selector :menuitem, "Task board" - end - end - end - context "when the wiki module is not enabled" do before do project.enabled_module_names -= ["wiki"] diff --git a/modules/backlogs/spec/features/backlogs/create_story_spec.rb b/modules/backlogs/spec/features/backlogs/create_story_spec.rb index 8563a3dd03b..37e3d888493 100644 --- a/modules/backlogs/spec/features/backlogs/create_story_spec.rb +++ b/modules/backlogs/spec/features/backlogs/create_story_spec.rb @@ -54,9 +54,9 @@ RSpec.describe "Backlogs", :js do let(:user) do create(:user, member_with_permissions: { project => %i(add_work_packages - view_master_backlog + view_sprints view_work_packages - assign_versions) }) + manage_sprint_items) }) end let(:project) { create(:project) } diff --git a/modules/backlogs/spec/features/backlogs_in_backlog_view_spec.rb b/modules/backlogs/spec/features/backlogs_in_backlog_view_spec.rb index 0cd6e4093eb..d79c85f67f6 100644 --- a/modules/backlogs/spec/features/backlogs_in_backlog_view_spec.rb +++ b/modules/backlogs/spec/features/backlogs_in_backlog_view_spec.rb @@ -52,14 +52,14 @@ RSpec.describe "Backlogs in backlog view", :js do create(:project_role, permissions: %i( view_project - view_master_backlog + view_sprints + create_sprints + manage_sprint_items add_work_packages view_work_packages edit_work_packages manage_subtasks manage_versions - update_sprints - assign_versions )) end let!(:current_user) do @@ -123,13 +123,13 @@ RSpec.describe "Backlogs in backlog view", :js do # Versions can be folded backlogs_page - .expect_story_in_sprint(sprint_story1, sprint) + .expect_story_in_backlog(sprint_story1, sprint) backlogs_page .fold_backlog(sprint) backlogs_page - .expect_story_not_in_sprint(sprint_story1, sprint) + .expect_story_not_in_backlog(sprint_story1, sprint) # The backlogs can be folded by default visit my_interface_path @@ -142,13 +142,13 @@ RSpec.describe "Backlogs in backlog view", :js do backlogs_page.visit! backlogs_page - .expect_story_not_in_sprint(sprint_story1, sprint) + .expect_story_not_in_backlog(sprint_story1, sprint) backlogs_page .fold_backlog(sprint) backlogs_page - .expect_story_in_sprint(sprint_story1, sprint) + .expect_story_in_backlog(sprint_story1, sprint) # Alter the attributes of the sprint sleep(0.5) diff --git a/modules/backlogs/spec/features/empty_backlogs_spec.rb b/modules/backlogs/spec/features/empty_backlogs_spec.rb index b558b1914ac..9e4ba698e67 100644 --- a/modules/backlogs/spec/features/empty_backlogs_spec.rb +++ b/modules/backlogs/spec/features/empty_backlogs_spec.rb @@ -57,7 +57,7 @@ RSpec.describe "Empty backlogs project", end context "as regular member" do - let(:role) { create(:project_role, permissions: %i(view_master_backlog)) } + let(:role) { create(:project_role, permissions: %i(view_sprints)) } let(:current_user) { create(:user, member_with_roles: { project => role }) } it "shows a blankslate without description" do diff --git a/modules/backlogs/spec/features/impediments_spec.rb b/modules/backlogs/spec/features/impediments_spec.rb index 814071a1a3a..725bacfd8bb 100644 --- a/modules/backlogs/spec/features/impediments_spec.rb +++ b/modules/backlogs/spec/features/impediments_spec.rb @@ -54,12 +54,12 @@ RSpec.describe "Impediments on taskboard", :js, end let(:role) do create(:project_role, - permissions: %i(view_taskboards + permissions: %i(view_sprints add_work_packages view_work_packages edit_work_packages manage_subtasks - assign_versions + manage_sprint_items work_package_assigned)) end let!(:current_user) do diff --git a/modules/backlogs/spec/features/projects/backlogs_settings_spec.rb b/modules/backlogs/spec/features/projects/backlogs_settings_spec.rb new file mode 100644 index 00000000000..32e090f3137 --- /dev/null +++ b/modules/backlogs/spec/features/projects/backlogs_settings_spec.rb @@ -0,0 +1,96 @@ +# 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 "spec_helper" +require_relative "../../support/pages/projects/settings/backlogs" + +RSpec.describe "Backlogs Project Settings", :js do + let!(:project) do + create(:project, + enabled_module_names: %w(backlogs)) + end + let!(:closed_status) { create(:status, name: "Closed", is_closed: true) } + let!(:closed_like_status) { create(:status, name: "Sorta kinda Finished", is_default: true) } + let(:role) do + create(:project_role, + permissions: %i[select_done_statuses]) + end + let!(:current_user) do + create(:user, + member_with_roles: { project => role }) + end + let(:settings_page) { Pages::Projects::Settings::Backlogs.new(project) } + let(:done_status_ids_autocompleter) { FormFields::Primerized::AutocompleteField.new("story_types", selector: "[data-test-selector='done_status_ids_autocomplete']") } + + before do + login_as current_user + end + + it "allows setting a status as done although it is not closed" do + settings_page.visit! + + expect(page).to have_heading "Backlogs" + + wait_for_network_idle + wait_for_autocompleter_options_to_be_loaded + + done_status_ids_autocompleter.expect_blank + done_status_ids_autocompleter.select_option "Closed" + done_status_ids_autocompleter.select_option "Sorta kinda Finished" + + done_status_ids_autocompleter.expect_selected "Closed" + done_status_ids_autocompleter.expect_selected "Sorta kinda Finished" + done_status_ids_autocompleter.expect_not_disabled "Definition of Done" + + done_status_ids_autocompleter.close_autocompleter + + click_button "Save" + + expect_flash(type: :success, message: "Successful update") + + wait_for_network_idle + wait_for_autocompleter_options_to_be_loaded + + done_status_ids_autocompleter.expect_selected "Closed" + done_status_ids_autocompleter.expect_selected "Sorta kinda Finished" + + done_status_ids_autocompleter.deselect_option "Sorta kinda Finished" + + click_button "Save" + + wait_for_network_idle + wait_for_autocompleter_options_to_be_loaded + + expect_flash(type: :success, message: "Successful update") + + done_status_ids_autocompleter.expect_selected "Closed" + done_status_ids_autocompleter.expect_not_selected "Sorta kinda Finished" + end +end diff --git a/modules/backlogs/spec/features/projects/settings/backlog_sharing_settings_spec.rb b/modules/backlogs/spec/features/projects/settings/backlog_sharing_settings_spec.rb new file mode 100644 index 00000000000..33ad5f37a6b --- /dev/null +++ b/modules/backlogs/spec/features/projects/settings/backlog_sharing_settings_spec.rb @@ -0,0 +1,157 @@ +# 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 "rails_helper" + +RSpec.describe "Backlogs project settings sprint sharing", :js, with_flag: { scrum_projects: true } do + let(:project) { create(:project) } + let(:permissions) { %i[create_sprints share_sprint select_done_statuses] } + + let(:current_user) do + create(:user, member_with_permissions: { project => permissions }) + end + + before do + login_as current_user + end + + context "with share_sprint permission" do + it "displays and stores sprint sharing settings" do + visit project_settings_backlog_sharing_path(project) + + expect(page).to have_link( + "Sharing", + href: project_settings_backlog_sharing_path(project) + ) + + # all radio buttons are present with no_sharing checked by default + expect(page).to have_checked_field("Don't share") + expect(page).to have_unchecked_field("All projects") + expect(page).to have_unchecked_field("Subprojects") + expect(page).to have_unchecked_field("Receive shared sprints") + + # no banners visible by default + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.share_subprojects.info")) + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.receive_shared.warning")) + + # selecting share_subprojects shows its info banner + choose("Subprojects") + expect(page).to have_text(I18n.t("projects.settings.backlog_sharing.options.share_subprojects.info")) + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.receive_shared.warning")) + + # selecting receive_shared shows its warning banner + choose("Receive shared sprints") + expect(page).to have_text(I18n.t("projects.settings.backlog_sharing.options.receive_shared.warning")) + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.share_subprojects.info")) + + # persists receive_shared + click_button I18n.t("button_save") + + expect_and_dismiss_flash(type: :success, message: I18n.t(:notice_successful_update)) + expect(page).to have_checked_field("Receive shared sprints") + expect(project.reload.sprint_sharing).to eq("receive_shared") + + # keeps the banner visible after persisting + expect(page).to have_text(I18n.t("projects.settings.backlog_sharing.options.receive_shared.warning")) + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.share_subprojects.info")) + + # selecting no_sharing hides all banners + choose("Don't share") + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.share_subprojects.info")) + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.receive_shared.warning")) + + # persists no_sharing + click_button I18n.t("button_save") + + expect_and_dismiss_flash(type: :success, message: I18n.t(:notice_successful_update)) + expect(page).to have_checked_field("Don't share") + expect(project.reload.sprint_sharing).to eq("no_sharing") + + # keeps the banner hidden after persisting + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.share_subprojects.info")) + expect(page).to have_no_text(I18n.t("projects.settings.backlog_sharing.options.receive_shared.warning")) + end + + context "when another project already shares with all projects" do + let!(:other_project) { create(:project, name: "Sharer Project", sprint_sharing: "share_all_projects") } + + it "disables the all projects option with an explanation" do + visit project_settings_backlog_sharing_path(project) + + expect(page).to have_field("All projects", disabled: true) + expect(page).to have_text( + I18n.t("projects.settings.backlog_sharing.options.share_all_projects.disabled_caption_anonymous") + ) + end + + context "when the current user cannot see the other project" do + let!(:other_project) { create(:project, public: false, name: "Sharer Project", sprint_sharing: "share_all_projects") } + + it "disables the all projects option without revealing the project name" do + visit project_settings_backlog_sharing_path(project) + + expect(page).to have_field("All projects", disabled: true) + expect(page).to have_text( + I18n.t("projects.settings.backlog_sharing.options.share_all_projects.disabled_caption_anonymous") + ) + expect(page).to have_no_text("Sharer Project") + end + end + end + end + + context "without share_sprint permission" do + let(:permissions) { %i[create_sprints select_done_statuses] } + + it "does not show the sharing tab and forbids direct route access" do + visit project_settings_backlogs_path(project) + + expect(page).to have_heading(I18n.t(:label_backlogs)) + expect(page).to have_no_link(I18n.t("backlogs.sharing")) + + visit project_settings_backlog_sharing_path(project) + + expect(page).to have_text(I18n.t(:notice_not_authorized)) + end + end + + context "when scrum_projects feature flag is inactive", with_flag: { scrum_projects: false } do + it "does not show the sharing tab and returns 404 on direct access" do + visit project_settings_backlogs_path(project) + + expect(page).to have_heading(I18n.t(:label_backlogs)) + expect(page).to have_no_link(I18n.t("backlogs.sharing")) + + visit project_settings_backlog_sharing_path(project) + + expect(page).to have_text(I18n.t(:notice_file_not_found)) + end + end +end diff --git a/modules/backlogs/spec/features/sprints/create_spec.rb b/modules/backlogs/spec/features/sprints/create_spec.rb new file mode 100644 index 00000000000..2a3b3a4de17 --- /dev/null +++ b/modules/backlogs/spec/features/sprints/create_spec.rb @@ -0,0 +1,208 @@ +# 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 "spec_helper" +require_relative "../../support/pages/backlogs" + +RSpec.describe "Create", :js do + let(:project) { create(:project) } + let(:all_permissions) { %i[view_sprints view_work_packages create_sprints] } + let(:permissions) { all_permissions } + let(:user) do + create(:user, member_with_permissions: { project => permissions }) + end + let(:backlogs_page) { Pages::Backlogs.new(project) } + + let!(:initial_sprint) do + create(:agile_sprint, + project:, + name: "Initial sprint", + start_date: Date.new(2025, 9, 5), + finish_date: Date.new(2025, 9, 15)) + end + + let(:story_type) do + create(:type_feature) + end + let(:story_type2) do + type = create(:type) + + project.types << type + + type + end + let(:inactive_story_type) do + create(:type) + end + + let(:task_type) do + type = create(:type_task) + project.types << type + + type + end + + before do + login_as(user) + + # Legacy backlogs module requires type configuration + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return("story_types" => [story_type.id.to_s, + story_type2.id.to_s, + inactive_story_type.id.to_s], + "task_type" => task_type.id.to_s) + + backlogs_page.visit! + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + context "with the 'create_sprints' permissions" do + let(:start_date) { Date.new(2025, 10, 5) } + let(:start_date_fmt) { start_date.strftime("%Y-%m-%d") } + let(:finish_date) { Date.new(2025, 10, 20) } + let(:finish_date_fmt) { finish_date.strftime("%Y-%m-%d") } + + it "allows creating a new sprint" do + backlogs_page.expect_sprint_names_in_order(initial_sprint.name) + + backlogs_page.open_create_sprint_dialog + + within_dialog "New sprint" do + page.fill_in "Sprint name", with: "Created sprint" + page.fill_in "Start date", with: start_date_fmt + page.fill_in "Finish date", with: finish_date_fmt + + click_on "Create" + end + + expect_and_dismiss_flash(message: "Successful creation.") + backlogs_page.expect_sprint_names_in_order(initial_sprint.name, "Created sprint") + + sprint = project.reload.sprints.last + expect(sprint).to be_present + expect(sprint.name).to eq "Created sprint" + expect(sprint.start_date).to eq start_date + expect(sprint.finish_date).to eq finish_date + end + + it "previews the sprint duration when changing the dates" do + backlogs_page.open_create_sprint_dialog + + within_dialog "New sprint" do + expect(page).to have_field "Duration", with: "", readonly: true + + page.fill_in "Start date", with: start_date_fmt + page.fill_in "Finish date", with: finish_date_fmt + + expect(page).to have_field "Duration", with: "16 days", readonly: true + end + end + + describe "validations" do + let(:too_early_finish_date) { start_date - 1.day } + + it "validates required fields are present" do + backlogs_page.open_create_sprint_dialog + + within_dialog "New sprint" do + page.fill_in "Sprint name", with: "" + + click_on "Create" + + expect(page).to have_field "Sprint name", validation_error: "can't be blank" + expect(page).to have_field "Start date", validation_error: "can't be blank" + expect(page).to have_field "Finish date", validation_error: "can't be blank" + end + end + + it "validates finish date is not before start date" do + backlogs_page.open_create_sprint_dialog + + within_dialog "New sprint" do + page.fill_in "Start date", with: start_date_fmt + page.fill_in "Finish date", with: too_early_finish_date.strftime("%Y-%m-%d") + + # Shows duration as zero if finish date is before start date: + expect(page).to have_field "Duration", with: "0 days", readonly: true + + click_on "Create" + + expect(page).to have_field("Finish date", + validation_error: "must be greater than or equal to #{start_date_fmt}") + end + end + end + + describe "proposed sprint names" do + let!(:initial_sprint) { nil } # override so that initial sprint is not present + + it "prefilled with 'Sprint 1' if there are no previous sprints" do + backlogs_page.open_create_sprint_dialog + + within_dialog "New sprint" do + expect(page).to have_field "Sprint name *", with: "Sprint 1", required: true, focused: true + end + end + + context "with a previous sprint" do + before do + create(:agile_sprint, name: "Be ambitious 42", project:) + + backlogs_page.visit! + backlogs_page.open_create_sprint_dialog + end + + it "offers the next sprint name with a number increment" do + within_dialog "New sprint" do + expect(page).to have_field "Sprint name *", with: "Be ambitious 43" + end + end + end + end + end + + context "without the necessary permissions" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "is missing the 'new sprint' button" do + expect(page).to have_no_button "Create" + expect(page).not_to have_test_selector("op-sprints--new-sprint-button") + end + end + end + + context "with the feature flag inactive" do + it "is missing the 'new sprint' button" do + expect(page).to have_no_button "Create" + expect(page).not_to have_test_selector("op-sprints--new-sprint-button") + end + end +end diff --git a/modules/backlogs/spec/features/sprints/edit_spec.rb b/modules/backlogs/spec/features/sprints/edit_spec.rb new file mode 100644 index 00000000000..a41ba4f2ff3 --- /dev/null +++ b/modules/backlogs/spec/features/sprints/edit_spec.rb @@ -0,0 +1,192 @@ +# 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 "spec_helper" +require_relative "../../support/pages/backlogs" + +RSpec.describe "Edit", :js do + let(:project) { create(:project) } + let(:all_permissions) { %i[view_sprints add_work_packages view_work_packages create_sprints manage_sprint_items] } + let(:permissions) { all_permissions } + let(:user) do + create(:user, member_with_permissions: { project => permissions }) + end + let(:backlogs_page) { Pages::Backlogs.new(project) } + + let(:story_type) do + create(:type_feature) + end + let(:story_type2) do + type = create(:type) + + project.types << type + + type + end + let(:inactive_story_type) do + create(:type) + end + + let(:task_type) do + type = create(:type_task) + project.types << type + + type + end + + let!(:closed_sprint) do + create(:agile_sprint, + project:, + status: "completed", + start_date: Date.new(2025, 8, 25), + finish_date: Date.new(2025, 9, 4)) + end + + let!(:first_sprint) do + create(:agile_sprint, + project:, + start_date: Date.new(2025, 9, 5), + finish_date: Date.new(2025, 9, 15)) + end + + let!(:second_sprint) do + create(:agile_sprint, + project:, + start_date: Date.new(2025, 9, 16), + finish_date: Date.new(2025, 9, 26)) + end + + let!(:work_package) do + create(:work_package, subject: "First work package", project:, sprint: first_sprint, type: story_type) + end + + # Necessary so that work packages can be created via dialog + shared_let(:default_status) { create(:default_status) } + shared_let(:default_priority) { create(:default_priority) } + + before do + login_as(user) + + # Legacy backlogs module requires type configuration + allow(Setting) + .to receive(:plugin_openproject_backlogs) + .and_return("story_types" => [story_type.id.to_s, + story_type2.id.to_s, + inactive_story_type.id.to_s], + "task_type" => task_type.id.to_s) + + backlogs_page.visit! + end + + context "with the feature flag active", with_flag: { scrum_projects: true } do + it "lists all open sprints" do + backlogs_page.expect_sprint_names_in_order(first_sprint.name, second_sprint.name) + + backlogs_page.expect_story_in_sprint(work_package, first_sprint) + backlogs_page.expect_story_not_in_sprint(work_package, second_sprint) + end + + it "adds a work package to a sprint" do + backlogs_page.click_in_sprint_menu(first_sprint, "New story") + backlogs_page.expect_create_work_package_dialog + + page.within("#create-work-package-dialog") do + page.fill_in "Subject", with: "Story created in sprint" + + click_on "Create" + end + + wait_for_reload + + expect_and_dismiss_flash type: :success, message: "New work package created and added as a child" + created_wp = first_sprint.reload.work_packages.last + expect(created_wp.subject).to eq("Story created in sprint") + backlogs_page.expect_story_in_sprint(created_wp, first_sprint) + end + + context "with the 'create_sprints' permissions" do + context "when editing a sprint" do + it "displays all menu entries" do + backlogs_page.within_sprint_menu(first_sprint) do |menu| + expect(menu).to have_selector :menuitem, count: 3 + expect(menu).to have_selector :menuitem, "Edit sprint" + expect(menu).to have_selector :menuitem, "New story" + expect(menu).to have_selector :menuitem, "Stories/Tasks" + end + end + + it "edits the sprint name" do + backlogs_page.expect_sprint_names_in_order(first_sprint.name, second_sprint.name) + + backlogs_page.click_in_sprint_menu(first_sprint, "Edit sprint") + backlogs_page.expect_sprint_dialog + + within_dialog "Edit sprint" do + page.fill_in "Sprint name", with: "Changed name" + page.click_button "Save" + end + + wait_for_reload + backlogs_page.expect_sprint_names_in_order("Changed name", second_sprint.name) + end + + context "when lacking the 'manage_sprint_items' permission" do + let(:permissions) { all_permissions - %i[manage_sprint_items] } + + it "has no menu entry for creating a new story" do + backlogs_page.within_sprint_menu(first_sprint) do |menu| + expect(menu).to have_selector :menuitem, count: 2 + expect(menu).to have_selector :menuitem, "Edit sprint" + expect(menu).to have_selector :menuitem, "Stories/Tasks" + + expect(menu).to have_no_selector :menuitem, "New story" + end + end + end + end + end + + context "without the necessary permissions" do + let(:permissions) { all_permissions - [:create_sprints] } + + it "is missing the 'new sprint' button" do + expect(page).to have_no_button "Create" + expect(page).not_to have_test_selector("op-sprints--new-sprint-button") + end + + it "has no menu entry for editing a sprint" do + backlogs_page.within_sprint_menu(first_sprint) do |menu| + expect(menu).to have_selector :menuitem, "Stories/Tasks" + expect(menu).to have_no_selector :menuitem, "Edit sprint" + end + end + end + end +end diff --git a/modules/backlogs/spec/features/stories_in_backlog_spec.rb b/modules/backlogs/spec/features/stories_in_backlog_spec.rb index 9c554cb6e21..37dc5efc407 100644 --- a/modules/backlogs/spec/features/stories_in_backlog_spec.rb +++ b/modules/backlogs/spec/features/stories_in_backlog_spec.rb @@ -52,12 +52,12 @@ RSpec.describe "Stories in backlog", :js, :settings_reset do end let(:role) do create(:project_role, - permissions: %i(view_master_backlog + permissions: %i(view_sprints + manage_sprint_items add_work_packages view_work_packages edit_work_packages - manage_subtasks - assign_versions)) + manage_subtasks)) end let!(:current_user) do create(:user, @@ -144,22 +144,22 @@ RSpec.describe "Stories in backlog", :js, :settings_reset do it "displays stories in correct order, calculates velocity, and allows editing story points" do backlogs_page - .expect_story_in_sprint(sprint_story1, sprint) + .expect_story_in_backlog(sprint_story1, sprint) backlogs_page - .expect_story_in_sprint(sprint_story2, sprint) + .expect_story_in_backlog(sprint_story2, sprint) backlogs_page - .expect_story_in_sprint(backlog_story1, backlog) + .expect_story_in_backlog(backlog_story1, backlog) backlogs_page - .expect_story_not_in_sprint(sprint_story2_parent, sprint) + .expect_story_not_in_backlog(sprint_story2_parent, sprint) backlogs_page - .expect_story_not_in_sprint(sprint_story1_task, sprint) + .expect_story_not_in_backlog(sprint_story1_task, sprint) backlogs_page - .expect_story_not_in_sprint(sprint_story_in_other_project, sprint) + .expect_story_not_in_backlog(sprint_story_in_other_project, sprint) backlogs_page .expect_stories_in_order(sprint, sprint_story1, sprint_story2) @@ -182,8 +182,8 @@ RSpec.describe "Stories in backlog", :js, :settings_reset do backlogs_page .edit_story_in_details_view(sprint_story1, version: backlog) - backlogs_page.expect_story_not_in_sprint(sprint_story1, sprint) - backlogs_page.expect_story_in_sprint(sprint_story1, backlog) + backlogs_page.expect_story_not_in_backlog(sprint_story1, sprint) + backlogs_page.expect_story_in_backlog(sprint_story1, backlog) end it "switches the details view from one story to another" do @@ -191,19 +191,19 @@ RSpec.describe "Stories in backlog", :js, :settings_reset do .click_in_story_menu(sprint_story1, "Open details view") backlogs_page.expect_details_view(sprint_story1) - backlogs_page.expect_story_in_sprint(sprint_story2, sprint) + backlogs_page.expect_story_in_backlog(sprint_story2, sprint) backlogs_page .click_in_story_menu(sprint_story2, "Open details view") backlogs_page.expect_details_view(sprint_story2) - backlogs_page.expect_story_in_sprint(sprint_story1, sprint) + backlogs_page.expect_story_in_backlog(sprint_story1, sprint) end it "removes story from sprint when type is changed to non-story type via details view" do backlogs_page .edit_story_in_details_view(sprint_story2, type: task.name) - backlogs_page.expect_story_not_in_sprint(sprint_story2, sprint) + backlogs_page.expect_story_not_in_backlog(sprint_story2, sprint) end end diff --git a/modules/backlogs/spec/features/tasks_on_taskboard_spec.rb b/modules/backlogs/spec/features/tasks_on_taskboard_spec.rb index 42af6abe007..f9539baf4c2 100644 --- a/modules/backlogs/spec/features/tasks_on_taskboard_spec.rb +++ b/modules/backlogs/spec/features/tasks_on_taskboard_spec.rb @@ -51,12 +51,12 @@ RSpec.describe "Tasks on taskboard", :js, end let(:role) do create(:project_role, - permissions: %i(view_taskboards + permissions: %i(view_sprints add_work_packages view_work_packages edit_work_packages manage_subtasks - assign_versions + manage_sprint_items work_package_assigned)) end let!(:current_user) do diff --git a/modules/backlogs/spec/lib/open_project/backlogs/event_subscriptions_spec.rb b/modules/backlogs/spec/lib/open_project/backlogs/event_subscriptions_spec.rb new file mode 100644 index 00000000000..54a863d0a40 --- /dev/null +++ b/modules/backlogs/spec/lib/open_project/backlogs/event_subscriptions_spec.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. +#++ + +require "spec_helper" + +RSpec.describe OpenProject::Notifications, "backlogs event subscriptions" do # rubocop:disable RSpec/SpecFilePathFormat + describe "MODULE_DISABLED" do + subject do + described_class.send( + OpenProject::Events::MODULE_DISABLED, + disabled_module: + ) + end + + Projects::SprintSharing::SPRINT_SHARING_MODES.each do |sharing_mode| + context "when the backlogs module is disabled on a project with #{sharing_mode}" do + let(:project) { create(:project, sprint_sharing: sharing_mode) } + let(:disabled_module) { instance_double(EnabledModule, name: "backlogs", project:) } + + it "sets sprint sharing to no_sharing" do + subject + + expect(project.reload.sprint_sharing).to eq(Projects::SprintSharing::NO_SHARING) + end + end + end + + context "when a different module is disabled" do + let(:project) do + create(:project, sprint_sharing: Projects::SprintSharing::SHARE_ALL_PROJECTS) + end + let(:disabled_module) { instance_double(EnabledModule, name: "wiki", project:) } + + it "does not reset sprint sharing" do + subject + + expect(project.reload.sprint_sharing).to eq(Projects::SprintSharing::SHARE_ALL_PROJECTS) + end + end + end + + describe "PROJECT_ARCHIVED" do + subject do + described_class.send( + OpenProject::Events::PROJECT_ARCHIVED, + project: + ) + end + + Projects::SprintSharing::SPRINT_SHARING_MODES.each do |sharing_mode| + context "when a project with #{sharing_mode} is archived" do + let(:project) { create(:project, sprint_sharing: sharing_mode) } + + it "sets sprint sharing to no_sharing" do + subject + + expect(project.reload.sprint_sharing).to eq(Projects::SprintSharing::NO_SHARING) + end + end + end + end +end diff --git a/modules/backlogs/spec/lib/open_project/backlogs/permissions_spec.rb b/modules/backlogs/spec/lib/open_project/backlogs/permissions_spec.rb new file mode 100644 index 00000000000..8d32adaaf88 --- /dev/null +++ b/modules/backlogs/spec/lib/open_project/backlogs/permissions_spec.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. +#++ + +require "spec_helper" + +RSpec.describe OpenProject::AccessControl, "Backlogs module permissions" do # rubocop:disable RSpec/SpecFilePathFormat + describe "view_sprints" do + subject { described_class.permission(:view_sprints) } + + it "depends on view_work_packages" do + expect(subject.dependencies).to contain_exactly(:view_work_packages) + end + end + + describe "create_sprints" do + subject { described_class.permission(:create_sprints) } + + it "depends on view_sprints" do + expect(subject.dependencies).to contain_exactly(:view_sprints) + end + end + + describe "manage_sprint_items" do + subject { described_class.permission(:manage_sprint_items) } + + it "depends on view_sprints, add_work_packages, and edit_work_packages" do + expect(subject.dependencies).to contain_exactly(:view_sprints) + end + end + + describe "start_complete_sprint" do + subject { described_class.permission(:start_complete_sprint) } + + it "depends on view_sprints" do + expect(subject.dependencies).to contain_exactly(:view_sprints) + end + + context "when scrum_projects feature flag is active", with_flag: { scrum_projects: true } do + it { is_expected.to be_visible } + end + + context "when scrum_projects feature flag is inactive", with_flag: { scrum_projects: false } do + it { is_expected.to be_hidden } + end + end + + describe "share_sprint" do + subject { described_class.permission(:share_sprint) } + + it "depends on create_sprints" do + expect(subject.dependencies).to contain_exactly(:create_sprints) + end + + context "when scrum_projects feature flag is active", with_flag: { scrum_projects: true } do + it { is_expected.to be_visible } + end + + context "when scrum_projects feature flag is inactive", with_flag: { scrum_projects: false } do + it { is_expected.to be_hidden } + end + end +end diff --git a/modules/backlogs/spec/models/agile/sprint_spec.rb b/modules/backlogs/spec/models/agile/sprint_spec.rb index ed1fc3b5628..5ce79972dea 100644 --- a/modules/backlogs/spec/models/agile/sprint_spec.rb +++ b/modules/backlogs/spec/models/agile/sprint_spec.rb @@ -46,7 +46,6 @@ RSpec.describe Agile::Sprint do it { is_expected.to validate_presence_of(:finish_date) } it { is_expected.to validate_presence_of(:project) } it { is_expected.to validate_inclusion_of(:status).in_array(described_class.statuses.keys) } - it { is_expected.to validate_inclusion_of(:sharing).in_array(described_class.sharings.keys) } it "validates finish_date is after or equal to start_date" do sprint.finish_date = sprint.start_date - 1.day @@ -54,6 +53,21 @@ RSpec.describe Agile::Sprint do expect(sprint.errors[:finish_date]).to include(/must be greater than or equal to/) end + it "does not validate finish_date comparison when start_date is nil" do + sprint.start_date = nil + sprint.finish_date = Time.zone.today + expect(sprint).not_to be_valid + expect(sprint.errors[:start_date]).to be_present + expect(sprint.errors[:finish_date]).not_to include(/must be greater than or equal to/) + end + + it "still validates finish_date presence even when start_date is nil" do + sprint.start_date = nil + sprint.finish_date = nil + expect(sprint).not_to be_valid + expect(sprint.errors[:finish_date]).to be_present + end + context "with active sprint validation" do it "allows one active sprint per project" do sprint.status = "active" @@ -98,14 +112,6 @@ RSpec.describe Agile::Sprint do it "status defaults to in_planning" do expect(sprint).to be_in_planning end - - it "has sharing enum with correct values" do - expect(described_class.sharings.keys).to contain_exactly("none", "descendants", "system") - end - - it "sharing defaults to none" do - expect(sprint).to be_sharing_with_none - end end describe "associations" do diff --git a/modules/backlogs/spec/models/agile/sprints/scopes/for_project_spec.rb b/modules/backlogs/spec/models/agile/sprints/scopes/for_project_spec.rb new file mode 100644 index 00000000000..bd45f4578ae --- /dev/null +++ b/modules/backlogs/spec/models/agile/sprints/scopes/for_project_spec.rb @@ -0,0 +1,191 @@ +# 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 "spec_helper" + +RSpec.describe Agile::Sprints::Scopes::ForProject do + let(:sprint_sharing) { "no_sharing" } + let(:project) { create(:project, sprint_sharing:) } + let(:global_sharer) { create(:project, sprint_sharing: "share_all_projects") } + let(:other_project) { create(:project) } + let!(:sprint_in_project) { create(:agile_sprint, project:) } + let!(:global_sprint) { create(:agile_sprint, project: global_sharer) } + let!(:sprint_in_other_project) { create(:agile_sprint, project: other_project) } + + shared_examples "executes a single SQL query" do + it "resolves for_project in a single query" do + expect { Agile::Sprint.for_project(project).load }.to have_a_query_limit(1) + end + end + + describe ".for_project" do + context "when project does not receive sprints (no_sharing)" do + let(:sprint_sharing) { "no_sharing" } + + it_behaves_like "executes a single SQL query" + + context "and there are no work package assignments" do + it "returns only the project's own sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(sprint_in_project) + end + end + + context "and the project has a work package assigned to a sprint from another project" do + let!(:cross_project_sprint) { create(:agile_sprint, project: other_project) } + let!(:work_package) { create(:work_package, project:, sprint: cross_project_sprint) } + + it "returns both the own sprint and the sprint assigned via work package" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(sprint_in_project, cross_project_sprint) + end + + context "when the cross-project sprint is completed" do + let!(:completed_sprint) { create(:agile_sprint, project: other_project, status: "completed") } + let!(:work_package) { create(:work_package, project:, sprint: completed_sprint) } + + it "returns the completed sprint among the sprints" do + expect(Agile::Sprint.for_project(project)).to include(completed_sprint) + end + end + end + end + + context "when project is a sender (share_subprojects)" do + let(:sprint_sharing) { "share_subprojects" } + + it_behaves_like "executes a single SQL query" + + context "and there are no work package assignments" do + it "returns only the project's own sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(sprint_in_project) + end + end + + context "and a work package in the project is assigned to a sprint from another project" do + let!(:cross_project_sprint) { create(:agile_sprint, project: other_project) } + let!(:work_package) { create(:work_package, project:, sprint: cross_project_sprint) } + + it "returns both the own sprint and the cross-project sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(sprint_in_project, cross_project_sprint) + end + end + end + + context "when project is a sender (share_all_projects)" do + let(:sprint_sharing) { "share_all_projects" } + + it_behaves_like "executes a single SQL query" + + context "and there are no work package assignments" do + it "returns only the project's own sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(sprint_in_project) + end + end + + context "and a work package in the project is assigned to a sprint from another project" do + let!(:cross_project_sprint) { create(:agile_sprint, project: other_project) } + let!(:work_package) { create(:work_package, project:, sprint: cross_project_sprint) } + + it "returns both the own sprint and the cross-project sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(sprint_in_project, cross_project_sprint) + end + end + end + + context "when project receives shared sprints" do + let(:sprint_sharing) { "receive_shared" } + + it_behaves_like "executes a single SQL query" + + context "and there is only a global sharer" do + it "returns only the sprints shared from the global sharer project" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(global_sprint) + end + + context "and a work package is assigned to the project's own sprint" do + let!(:work_package) { create(:work_package, project:, sprint: sprint_in_project) } + + it "returns both the global shared sprint and the project's own sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(global_sprint, sprint_in_project) + end + end + + context "and a work package is assigned to the shared sprint from the global sharer" do + let!(:work_package) { create(:work_package, project:, sprint: global_sprint) } + + it "returns the shared sprint only once" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(global_sprint) + end + end + + context "and a work package is assigned to a sprint from an unrelated project" do + let!(:work_package) { create(:work_package, project:, sprint: sprint_in_other_project) } + + it "returns the global shared sprint and the unrelated project's sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(global_sprint, sprint_in_other_project) + end + end + end + + context "and there is a subproject-sharing ancestor" do + let(:subproject_sharer) { create(:project, sprint_sharing: "share_subprojects") } + let(:project) { create(:project, parent: subproject_sharer, sprint_sharing:) } + let!(:subproject_sprint) { create(:agile_sprint, project: subproject_sharer) } + + it "returns only the sprints shared from the closest subproject-sharing ancestor" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(subproject_sprint) + end + + context "and a work package is assigned to the project's own sprint" do + let!(:work_package) { create(:work_package, project:, sprint: sprint_in_project) } + + it "returns both the ancestor's shared sprint and the project's own sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(subproject_sprint, sprint_in_project) + end + end + + context "and a work package is assigned to the ancestor's shared sprint" do + let!(:work_package) { create(:work_package, project:, sprint: subproject_sprint) } + + it "returns the ancestor's shared sprint only once" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(subproject_sprint) + end + end + + context "and a work package is assigned to a sprint from the global sharer" do + let!(:work_package) { create(:work_package, project:, sprint: global_sprint) } + + it "returns both the ancestor's shared sprint and the global sharer's sprint" do + expect(Agile::Sprint.for_project(project)).to contain_exactly(subproject_sprint, global_sprint) + end + end + end + end + end +end diff --git a/modules/backlogs/spec/models/projects/scopes_spec.rb b/modules/backlogs/spec/models/projects/scopes_spec.rb new file mode 100644 index 00000000000..79a57256972 --- /dev/null +++ b/modules/backlogs/spec/models/projects/scopes_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Projects::Scopes, "scopes" do + shared_let(:project_without_settings) { create(:project, sprint_sharing: nil) } + shared_let(:project_with_empty_settings) { create(:project, sprint_sharing: "") } + shared_let(:no_sharing_project) { create(:project, sprint_sharing: "no_sharing") } + shared_let(:all_projects_sharer) { create(:project, sprint_sharing: "share_all_projects") } + shared_let(:subprojects_sharer) { create(:project, sprint_sharing: "share_subprojects") } + shared_let(:receiver) { create(:project, sprint_sharing: "receive_shared") } + + describe ".share_sprints_with_all_projects" do + it "returns projects that share with all projects" do + expect(Project.share_sprints_with_all_projects).to contain_exactly(all_projects_sharer) + end + end + + describe ".share_sprints_with_subprojects" do + it "returns projects that share with subprojects" do + expect(Project.share_sprints_with_subprojects).to contain_exactly(subprojects_sharer) + end + end + + describe ".receive_shared_sprints" do + it "returns projects that receive shared sprints" do + expect(Project.receive_shared_sprints).to contain_exactly(receiver) + end + end + + describe ".not_sharing_sprints" do + it "returns projects with no sharing" do + expect(Project.not_sharing_sprints).to contain_exactly( + project_without_settings, + project_with_empty_settings, + no_sharing_project + ) + end + end +end diff --git a/modules/backlogs/spec/models/projects/sprint_sharing_spec.rb b/modules/backlogs/spec/models/projects/sprint_sharing_spec.rb new file mode 100644 index 00000000000..bc4a85cb3ae --- /dev/null +++ b/modules/backlogs/spec/models/projects/sprint_sharing_spec.rb @@ -0,0 +1,207 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe Projects::SprintSharing do + let(:sprint_sharing) { "no_sharing" } + let(:active) { true } + let!(:project) { create(:project, sprint_sharing:, active:) } + + describe "SPRINT_SHARING_MODES" do + it "defines all supported sprint sharing options" do + expect(described_class::SPRINT_SHARING_MODES).to match_array( + %w[share_all_projects share_subprojects no_sharing receive_shared] + ) + end + + it "is exposed on Project" do + expect(Project::SPRINT_SHARING_MODES).to eq(described_class::SPRINT_SHARING_MODES) + end + end + + describe "#sprint_sharing" do + let(:sprint_sharing) { nil } + + it "defaults to no_sharing" do + expect(project.sprint_sharing).to eq("no_sharing") + end + + it "persists configured values" do + project.update!(sprint_sharing: "share_subprojects") + + expect(project.reload.sprint_sharing).to eq("share_subprojects") + end + end + + describe "predicate methods" do + it "#share_sprints_with_all_projects? returns true when sharing with all projects" do + project.sprint_sharing = "share_all_projects" + expect(project).to be_share_sprints_with_all_projects + end + + it "#share_sprints_with_subprojects? returns true when sharing with subprojects" do + project.sprint_sharing = "share_subprojects" + expect(project).to be_share_sprints_with_subprojects + end + + it "#receive_shared_sprints? returns true when receiving shared sprints" do + project.sprint_sharing = "receive_shared" + expect(project).to be_receive_shared_sprints + end + + it "#not_sharing_sprints? returns true when not sharing (default)" do + expect(project).to be_not_sharing_sprints + end + + it "predicates return false for non-matching values" do + project.sprint_sharing = "share_subprojects" + + expect(project).not_to be_share_sprints_with_all_projects + expect(project).not_to be_receive_shared_sprints + expect(project).not_to be_not_sharing_sprints + end + end + + describe "#not_sharing_sprints!" do + context "when the project is already set to no_sharing" do + let(:sprint_sharing) { "no_sharing" } + + it "does not update the database" do + allow(project).to receive(:update_column) + + subject + + expect(project).not_to have_received(:update_column) + end + end + + context "when the project has an active sharing mode" do + let(:sprint_sharing) { "share_all_projects" } + + it "resets sprint_sharing to no_sharing" do + project.not_sharing_sprints! + + expect(project.reload.sprint_sharing).to eq("no_sharing") + end + end + end + + describe ".global_sprint_sharer" do + context "when no project shares with all projects" do + let(:sprint_sharing) { "no_sharing" } + + it "returns nil" do + expect(Project.global_sprint_sharer).to be_nil + end + end + + context "when a project shares with all projects" do + let(:sprint_sharing) { "share_all_projects" } + + it "returns that project" do + expect(Project.global_sprint_sharer).to eq(project) + end + end + + context "when the sharing project is archived" do + let(:sprint_sharing) { "share_all_projects" } + let(:active) { false } + + it "returns nil" do + expect(Project.global_sprint_sharer).to be_nil + end + end + end + + describe "#sprint_source" do + let(:global_sprint_sharing) { "share_all_projects" } + let(:root_sprint_sharing) { "share_subprojects" } + let(:parent_sprint_sharing) { "share_subprojects" } + let(:project_sprint_sharing) { "receive_shared" } + + let!(:global_sharer) { create(:project, sprint_sharing: global_sprint_sharing) } + let!(:root_project) { create(:project, sprint_sharing: root_sprint_sharing) } + let!(:parent_project) { create(:project, parent: root_project, sprint_sharing: parent_sprint_sharing) } + let!(:project) { create(:project, parent: parent_project, sprint_sharing: project_sprint_sharing) } + + # Projects that should not be returned + shared_let(:other_project) { create(:project, sprint_sharing: "share_subprojects") } + shared_let(:archived_global_sharer) { create(:project, :archived, sprint_sharing: "share_all_projects") } + + shared_examples "returns the project itself" do + it "returns only itself" do + expect(project.sprint_source).to contain_exactly(project) + end + end + + shared_examples "executes a single SQL query" do + it "resolves sprint_source in a single query" do + expect { project.sprint_source.load }.to have_a_query_limit(1) + end + end + + context "when sprint_sharing is no_sharing (default)" do + let(:project_sprint_sharing) { "no_sharing" } + + it_behaves_like "returns the project itself" + it_behaves_like "executes a single SQL query" + end + + context "when sprint_sharing is share_subprojects" do + let(:project_sprint_sharing) { "share_subprojects" } + + it_behaves_like "returns the project itself" + it_behaves_like "executes a single SQL query" + end + + context "when sprint_sharing is share_all_projects" do + let(:global_sprint_sharing) { "no_sharing" } + let(:root_sprint_sharing) { "share_subprojects" } + let(:parent_sprint_sharing) { "share_subprojects" } + let(:project_sprint_sharing) { "share_all_projects" } + + it_behaves_like "returns the project itself" + it_behaves_like "executes a single SQL query" + end + + context "when sprint_sharing is receive_shared" do + let(:project_sprint_sharing) { "receive_shared" } + + context "with only a global sharer" do + let(:global_sprint_sharing) { "share_all_projects" } + let(:root_sprint_sharing) { "no_sharing" } + let(:parent_sprint_sharing) { "no_sharing" } + + it "returns only the global sharer" do + expect(project.sprint_source).to contain_exactly(global_sharer) + end + + it_behaves_like "executes a single SQL query" + end + + context "with a global sharer and both ancestors sharing subprojects" do + let(:global_sprint_sharing) { "share_all_projects" } + let(:root_sprint_sharing) { "share_subprojects" } + let(:parent_sprint_sharing) { "share_subprojects" } + + it "returns only the closest sharing ancestor" do + expect(project.sprint_source).to contain_exactly(parent_project) + end + + it_behaves_like "executes a single SQL query" + end + + context "with no sharing sources" do + let(:global_sprint_sharing) { "no_sharing" } + let(:root_sprint_sharing) { "no_sharing" } + let(:parent_sprint_sharing) { "no_sharing" } + + it "returns an empty scope" do + expect(project.sprint_source).to be_empty + end + + it_behaves_like "executes a single SQL query" + end + end + end +end diff --git a/modules/backlogs/spec/models/version_spec.rb b/modules/backlogs/spec/models/version_spec.rb index bbaa95de5ce..46435be8a20 100644 --- a/modules/backlogs/spec/models/version_spec.rb +++ b/modules/backlogs/spec/models/version_spec.rb @@ -31,6 +31,95 @@ require "spec_helper" RSpec.describe Version do it { is_expected.to have_many :version_settings } + describe "#used_as_backlog?" do + let(:project) { create(:project) } + let(:version) { create(:version, project:) } + + context "when backlogs is not enabled" do + before do + project.enabled_module_names = project.enabled_module_names - ["backlogs"] + end + + it "returns false" do + expect(version.used_as_backlog?(project)).to be false + end + end + + context "when backlogs is enabled" do + before do + project.enabled_module_names = project.enabled_module_names + ["backlogs"] + end + + context "when no version_settings exist" do + it "returns false" do + expect(version.used_as_backlog?(project)).to be false + end + end + + context "when version_settings exist with display_right" do + before do + create(:version_setting, version:, project:, display: VersionSetting::DISPLAY_RIGHT) + end + + it "returns true" do + expect(version.used_as_backlog?(project)).to be true + end + end + + context "when version_settings exist with display_left" do + before do + create(:version_setting, version:, project:, display: VersionSetting::DISPLAY_LEFT) + end + + it "returns false" do + expect(version.used_as_backlog?(project)).to be false + end + end + + context "when version_settings exist with display_none" do + before do + create(:version_setting, version:, project:, display: VersionSetting::DISPLAY_NONE) + end + + it "returns false" do + expect(version.used_as_backlog?(project)).to be false + end + end + + context "when multiple version_settings exist for different projects" do + let(:other_project) { create(:project) } + + before do + project.enabled_module_names = project.enabled_module_names + ["backlogs"] + other_project.enabled_module_names = other_project.enabled_module_names + ["backlogs"] + create(:version_setting, version:, project:, display: VersionSetting::DISPLAY_RIGHT) + create(:version_setting, version:, project: other_project, display: VersionSetting::DISPLAY_LEFT) + end + + it "returns true for the project with display_right" do + expect(version.used_as_backlog?(project)).to be true + end + + it "returns false for the project with display_left" do + expect(version.used_as_backlog?(other_project)).to be false + end + end + + context "when project parameter is not provided" do + context "and version has a project" do + before do + project.enabled_module_names = project.enabled_module_names + ["backlogs"] + create(:version_setting, version:, project:, display: VersionSetting::DISPLAY_RIGHT) + end + + it "uses the version's project" do + expect(version.used_as_backlog?).to be true + end + end + end + end + end + describe "rebuild positions" do def build_work_package(options = {}) build(:work_package, options.reverse_merge(version_id: version.id, diff --git a/modules/backlogs/spec/routing/rb_stories_routing_spec.rb b/modules/backlogs/spec/routing/rb_stories_routing_spec.rb index d0af15bece9..e484c850e36 100644 --- a/modules/backlogs/spec/routing/rb_stories_routing_spec.rb +++ b/modules/backlogs/spec/routing/rb_stories_routing_spec.rb @@ -30,6 +30,16 @@ require "spec_helper" RSpec.describe RbStoriesController do describe "routing" do + it { + expect(put("/projects/project_42/sprints/21/stories/85/move_legacy")).to route_to( + controller: "rb_stories", + action: "move_legacy", + project_id: "project_42", + sprint_id: "21", + id: "85" + ) + } + it { expect(put("/projects/project_42/sprints/21/stories/85/move")).to route_to( controller: "rb_stories", diff --git a/modules/backlogs/spec/services/impediments/create_services_spec.rb b/modules/backlogs/spec/services/impediments/create_services_spec.rb index 21012fa323a..da7317501a4 100644 --- a/modules/backlogs/spec/services/impediments/create_services_spec.rb +++ b/modules/backlogs/spec/services/impediments/create_services_spec.rb @@ -33,7 +33,7 @@ RSpec.describe Impediments::CreateService do let(:impediment_subject) { "Impediment A" } let(:user) { create(:user) } - let(:role) { create(:project_role, permissions: %i(add_work_packages assign_versions work_package_assigned)) } + let(:role) { create(:project_role, permissions: %i(add_work_packages manage_sprint_items work_package_assigned)) } let(:type_feature) { create(:type_feature) } let(:type_task) { create(:type_task) } let(:priority) { create(:priority, is_default: true) } diff --git a/modules/backlogs/spec/features/resolved_status_spec.rb b/modules/backlogs/spec/services/sprints/create_service_spec.rb similarity index 59% rename from modules/backlogs/spec/features/resolved_status_spec.rb rename to modules/backlogs/spec/services/sprints/create_service_spec.rb index 3e094570a96..0971ecf565d 100644 --- a/modules/backlogs/spec/features/resolved_status_spec.rb +++ b/modules/backlogs/spec/services/sprints/create_service_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH @@ -27,37 +29,11 @@ #++ require "spec_helper" -require_relative "../support/pages/projects/settings/backlogs" +require "services/base_services/behaves_like_create_service" -RSpec.describe "Resolved status" do - let!(:project) do - create(:project, - enabled_module_names: %w(backlogs)) - end - let!(:status) { create(:status, is_default: true) } - let(:role) do - create(:project_role, - permissions: %i[select_done_statuses]) - end - let!(:current_user) do - create(:user, - member_with_roles: { project => role }) - end - let(:settings_page) { Pages::Projects::Settings::Backlogs.new(project) } - - before do - login_as current_user - end - - it "allows setting a status as done although it is not closed" do - settings_page.visit! - - check status.name - click_button "Save" - - expect_flash(type: :success, message: "Successful update") - - expect(page) - .to have_checked_field(status.name) +RSpec.describe Sprints::CreateService, type: :model do + it_behaves_like "BaseServices create service" do + let(:model_class) { Agile::Sprint } + let(:factory) { :agile_sprint } end end diff --git a/modules/backlogs/spec/services/sprints/set_attributes_service_spec.rb b/modules/backlogs/spec/services/sprints/set_attributes_service_spec.rb new file mode 100644 index 00000000000..8ef4e349dfa --- /dev/null +++ b/modules/backlogs/spec/services/sprints/set_attributes_service_spec.rb @@ -0,0 +1,281 @@ +# 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 "spec_helper" + +RSpec.describe Sprints::SetAttributesService, type: :model do + let(:user) { build_stubbed(:user) } + let(:contract_class) do + contract = class_double(Sprints::CreateContract) + + allow(contract) + .to receive(:new) + .with(sprint, user, options: {}) + .and_return(contract_instance) + + contract + end + let(:contract_instance) do + instance_double(ModelContract, validate: contract_valid, errors: contract_errors) + end + let(:contract_valid) { true } + let(:contract_errors) do + instance_double(ActiveModel::Errors) + end + let(:sprint_valid) { true } + let(:instance) do + described_class.new(user:, + model: sprint, + contract_class:, + contract_options: {}) + end + let(:project) { create(:project) } + let(:sprint) { Agile::Sprint.new } + let(:params) { { project: } } + + subject(:service_call) { instance.call(params) } + + describe "call" do + before do + allow(sprint) + .to receive(:valid?) + .and_return(sprint_valid) + + allow(sprint).to receive(:save) + end + + context "when contract validates and sprint is valid" do + it "is successful" do + expect(service_call).to be_success + end + + it "sets the attributes on the sprint" do + service_call + + # Default attributes set include: + # * the project, since passed as argument + # * a generated sprint sequence name if applicable, see `sprint_name_from_predecessor` specs. + expect(sprint.changed_attributes).to include("project_id", "name") + + expect(sprint.project_id).to eq(project.id) + expect(sprint.name).to eq("Sprint 1") + end + + it "does not persist the sprint" do + expect(sprint).not_to have_received(:save) + + service_call + end + end + + context "when contract does not validate" do + let(:contract_valid) { false } + + it "is not successful" do + expect(service_call).not_to be_success + end + end + + context "with params" do + let(:params) do + { + name: "New Sprint Name", + start_date: Time.zone.today, + finish_date: Time.zone.today + 21.days, + status: "active" + } + end + + before do + allow(contract_instance) + .to receive(:validate) + .and_return(true) + end + + it "passes the params to the sprint" do + service_call + + expect(sprint.name).to eq("New Sprint Name") + expect(sprint.start_date).to eq(Time.zone.today) + expect(sprint.finish_date).to eq(Time.zone.today + 21.days) + expect(sprint.status).to eq("active") + end + end + + describe "default attributes" do + let(:sprint) { Agile::Sprint.new } + + it "sets default status to in_planning" do + service_call + + expect(sprint.status).to eq("in_planning") + end + + context "when status is already set" do + let(:sprint) { Agile::Sprint.new(status: "active") } + + it "does not override the existing status" do + service_call + + expect(sprint.status).to eq("active") + end + end + end + end + + describe "assigning a name" do + context "when sprint is not a new record" do + let(:sprint) { create(:agile_sprint, project:, name: "Existing Sprint") } + + it "assigns the current name" do + service_call + + expect(sprint.name).to eq("Existing Sprint") + end + end + + context "when sprint is a new record" do + let(:sprint) { Agile::Sprint.new(project:) } + + context "when there is no predecessor sprint" do + it "assigns a default name for the first sprint" do + service_call + + expected_name = "#{I18n.t('activerecord.models.sprint')} 1" + expect(sprint.name).to eq(expected_name) + end + end + + context "when there is a predecessor sprint with a name ending in a number" do + it "increments the number for single-digit numbers" do + create(:agile_sprint, project:, name: "Sprint 1") + + service_call + expect(sprint.name).to eq("Sprint 2") + end + + it "increments the number for multi-digit numbers" do + create(:agile_sprint, project:, name: "Sprint 42") + + service_call + expect(sprint.name).to eq("Sprint 43") + end + + it "increments the number for custom names ending in numbers" do + create(:agile_sprint, project:, name: "Be ambitious 42") + + service_call + expect(sprint.name).to eq("Be ambitious 43") + end + + it "handles names with multiple spaces before the number" do + create(:agile_sprint, project:, name: "Release 99") + + service_call + expect(sprint.name).to eq("Release 100") + end + + it "increments from 9 to 10" do + create(:agile_sprint, project:, name: "Sprint 9") + + service_call + expect(sprint.name).to eq("Sprint 10") + end + + it "increments from 99 to 100" do + create(:agile_sprint, project:, name: "Sprint 99") + + service_call + expect(sprint.name).to eq("Sprint 100") + end + end + + context "when there is a predecessor sprint with a custom name not ending in a number" do + it "assigns an empty string" do + create(:agile_sprint, project:, name: "Custom Sprint Name") + + service_call + expect(sprint.name).to eq("") + end + + it "assigns an empty string for names with numbers in the middle" do + create(:agile_sprint, project:, name: "Sprint 2023 Planning") + + service_call + expect(sprint.name).to eq("") + end + + it "assigns an empty string for names ending with non-numeric characters" do + create(:agile_sprint, project:, name: "Sprint Alpha") + + service_call + expect(sprint.name).to eq("") + end + end + + context "when there are multiple predecessor sprints" do + it "uses the most recent sprint" do + create(:agile_sprint, project:, name: "Sprint 1", created_at: 2.days.ago) + create(:agile_sprint, project:, name: "Sprint 2", created_at: 1.day.ago) + + service_call + expect(sprint.name).to eq("Sprint 3") + end + + it "handles mixed naming patterns by using the most recent" do + create(:agile_sprint, project:, name: "Sprint 1", created_at: 2.days.ago) + create(:agile_sprint, project:, name: "Custom Name", created_at: 1.day.ago) + + service_call + expect(sprint.name).to eq("") + end + end + + context "when there are sprints in other projects" do + let(:other_project) { create(:project) } + + it "ignores sprints from other projects" do + create(:agile_sprint, project: other_project, name: "Other Sprint 5") + + service_call + expect(sprint.name).to eq("#{I18n.t('activerecord.models.sprint')} 1") + end + + it "only considers sprints from the same project" do + create(:agile_sprint, project: other_project, name: "Other Sprint 5") + create(:agile_sprint, project:, name: "Sprint 3") + + service_call + expect(sprint.name).to eq("Sprint 4") + end + end + end + end +end diff --git a/modules/backlogs/spec/services/stories/create_service_spec.rb b/modules/backlogs/spec/services/stories/create_service_spec.rb index 0ee06ac9d8f..87483c4a055 100644 --- a/modules/backlogs/spec/services/stories/create_service_spec.rb +++ b/modules/backlogs/spec/services/stories/create_service_spec.rb @@ -35,7 +35,7 @@ RSpec.describe Stories::CreateService, type: :model do let(:type_feature) { create(:type_feature) } let(:user) do - create(:user, member_with_permissions: { project => %i(add_work_packages manage_subtasks assign_versions) }) + create(:user, member_with_permissions: { project => %i(add_work_packages manage_subtasks manage_sprint_items) }) end let(:instance) do diff --git a/modules/backlogs/spec/support/pages/backlogs.rb b/modules/backlogs/spec/support/pages/backlogs.rb index d3c88f85406..2006e49fb4b 100644 --- a/modules/backlogs/spec/support/pages/backlogs.rb +++ b/modules/backlogs/spec/support/pages/backlogs.rb @@ -100,6 +100,12 @@ module Pages end end + def click_in_sprint_menu(sprint, item_name) + within_sprint_menu(sprint) do |menu| + menu.find(:menuitem, text: item_name).click + end + end + def click_in_story_menu(story, item_name) within_story_menu(story) do |menu| menu.find(:menuitem, text: item_name).click @@ -119,6 +125,14 @@ module Pages end end + def sprint_names_in_order + page.find_all("#sprint_backlogs_container > section .CollapsibleHeader-title").map(&:text) + end + + def expect_sprint_names_in_order(*sprint_names) + expect(sprint_names_in_order).to eq(sprint_names) + end + def expect_sprint(sprint) expect(page) .to have_css("#sprint_backlogs_container #{backlog_selector(sprint)}") @@ -130,14 +144,28 @@ module Pages end def expect_story_in_sprint(story, sprint) - within_backlog(sprint) do + within_sprint(sprint) do + expect(page) + .to have_selector(work_package_selector(story).to_s) + end + end + + def expect_story_in_backlog(story, backlog) + within_backlog(backlog) do expect(page) .to have_selector(story_selector(story).to_s) end end def expect_story_not_in_sprint(story, sprint) - within_backlog(sprint) do + within_sprint(sprint) do + expect(page) + .to have_no_selector(work_package_selector(story).to_s) + end + end + + def expect_story_not_in_backlog(story, backlog) + within_backlog(backlog) do expect(page) .to have_no_selector(story_selector(story).to_s) end @@ -205,6 +233,30 @@ module Pages details_view end + def open_create_sprint_dialog + find(:button, accessible_name: "Create").click + + within(:menu) do |menu| + menu.find(:menuitem, "Sprint").click + end + end + + def expect_sprint_dialog + expect(page).to have_css("#new-sprint-dialog") + end + + def expect_create_work_package_dialog + expect(page).to have_css("#create-work-package-dialog") + end + + def within_sprint_menu(backlog, &) + within_sprint(backlog) do + find(:button, accessible_name: "Sprint actions").click + + within(:menu, &) + end + end + private def within_story(story, &) @@ -215,6 +267,14 @@ module Pages within(backlog_selector(backlog), &) end + def within_sprint(sprint, &) + within(sprint_selector(sprint), &) + end + + def sprint_selector(sprint) + "#agile_sprint_#{sprint.id}" + end + def backlog_selector(backlog) "#backlog_#{backlog.id}" end @@ -223,6 +283,10 @@ module Pages "#story_#{story.id}" end + def work_package_selector(story) + "#work_package_#{story.id}" + end + def within_menu_controlled_by(button) menu_id = button[:controls] || button["aria-controls"] diff --git a/modules/backlogs/spec/views/rb_master_backlogs/index.html.erb_spec.rb b/modules/backlogs/spec/views/rb_master_backlogs/index.html.erb_spec.rb index 251ffd4eb36..ae8002731e9 100644 --- a/modules/backlogs/spec/views/rb_master_backlogs/index.html.erb_spec.rb +++ b/modules/backlogs/spec/views/rb_master_backlogs/index.html.erb_spec.rb @@ -32,7 +32,7 @@ RSpec.describe "rb_master_backlogs/index" do let(:user) { create(:user) } let(:role_allowed) do create(:project_role, - permissions: %i[view_master_backlog view_taskboards]) + permissions: %i[view_sprints]) end let(:statuses) do [create(:status, is_default: true), diff --git a/modules/bim/config/locales/crowdin/fr.yml b/modules/bim/config/locales/crowdin/fr.yml index 717608ff597..288904f98e5 100644 --- a/modules/bim/config/locales/crowdin/fr.yml +++ b/modules/bim/config/locales/crowdin/fr.yml @@ -59,7 +59,7 @@ fr: perform_description: "Voulez-vous importer ou mettre à jour les problèmes repris ci-dessus ?" replace_with_system_user: 'Les remplacer par l''utilisateur "Système"' import_as_system_user: 'Les importer comme utilisateur "Système".' - what_to_do: "Que voulez-vous faire ?" + what_to_do: "Que voulez-vous faire?" work_package_has_newer_changes: "Obsolète ! Ce sujet n'a pas été mis à jour, car les derniers changements sur le serveur étaient plus récents que la \"ModifiedDate\" du sujet importé. Toutefois, les commentaires sur le sujet ont été importés." bcf_file_not_found: "Impossible de localiser le fichier BCF. Veuillez recommencer le processus de téléversement." export: diff --git a/modules/budgets/config/locales/crowdin/cs.yml b/modules/budgets/config/locales/crowdin/cs.yml index eb13e395f05..c3ec09e6661 100644 --- a/modules/budgets/config/locales/crowdin/cs.yml +++ b/modules/budgets/config/locales/crowdin/cs.yml @@ -27,7 +27,7 @@ cs: budget: author: "Autor" available: "Dostupné" - budget: "Plánované" + budget: "Rozpočet" budget_ratio: "Stráveno (poměr)" description: "Popis" spent: "Strávený čas" diff --git a/modules/costs/app/views/admin/costs_settings/show.html.erb b/modules/costs/app/views/admin/costs_settings/show.html.erb index dab2f65b85f..bce17b94489 100644 --- a/modules/costs/app/views/admin/costs_settings/show.html.erb +++ b/modules/costs/app/views/admin/costs_settings/show.html.erb @@ -42,8 +42,19 @@ See COPYRIGHT and LICENSE files for more details. subhead.with_description { I18n.t(:description_costs_settings) } end end - form.text_field(name: :costs_currency, input_width: :small) - form.text_field(name: :costs_currency_format, input_width: :small) + form.text_field( + name: :costs_currency, + input_width: :xsmall, + caption: I18n.t(:setting_costs_currency_caption) + ) + form.select_list( + name: :costs_currency_format, + values: [ + [I18n.t(:setting_costs_currency_format_prefix), "%u %n"], + [I18n.t(:setting_costs_currency_format_suffix), "%n %u"] + ], + input_width: :small + ) form.submit end diff --git a/modules/costs/config/locales/crowdin/af.yml b/modules/costs/config/locales/crowdin/af.yml index 47f433ce4a2..751d3217cb5 100644 --- a/modules/costs/config/locales/crowdin/af.yml +++ b/modules/costs/config/locales/crowdin/af.yml @@ -205,7 +205,10 @@ af: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ar.yml b/modules/costs/config/locales/crowdin/ar.yml index 4ed12ceec3f..da16cf9e48b 100644 --- a/modules/costs/config/locales/crowdin/ar.yml +++ b/modules/costs/config/locales/crowdin/ar.yml @@ -221,7 +221,10 @@ ar: project_module_costs: "الوقت والتكاليف" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "العملة" - setting_costs_currency_format: "شكل العملة" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/az.yml b/modules/costs/config/locales/crowdin/az.yml index 8dd172a1778..65ed5f3dc8d 100644 --- a/modules/costs/config/locales/crowdin/az.yml +++ b/modules/costs/config/locales/crowdin/az.yml @@ -205,7 +205,10 @@ az: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/be.yml b/modules/costs/config/locales/crowdin/be.yml index 1d3ed4a73f5..81fb4101aa6 100644 --- a/modules/costs/config/locales/crowdin/be.yml +++ b/modules/costs/config/locales/crowdin/be.yml @@ -213,7 +213,10 @@ be: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/bg.yml b/modules/costs/config/locales/crowdin/bg.yml index 04d8a0d1140..6333adb7007 100644 --- a/modules/costs/config/locales/crowdin/bg.yml +++ b/modules/costs/config/locales/crowdin/bg.yml @@ -205,7 +205,10 @@ bg: project_module_costs: "Време и разходи" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Валута" - setting_costs_currency_format: "Формат на валутата" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ca.yml b/modules/costs/config/locales/crowdin/ca.yml index 4cc7f217213..d821577627d 100644 --- a/modules/costs/config/locales/crowdin/ca.yml +++ b/modules/costs/config/locales/crowdin/ca.yml @@ -205,7 +205,10 @@ ca: project_module_costs: "Temps i costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Divisa" - setting_costs_currency_format: "Format de divisa" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ckb-IR.yml b/modules/costs/config/locales/crowdin/ckb-IR.yml index eec8fb8766c..7edece35005 100644 --- a/modules/costs/config/locales/crowdin/ckb-IR.yml +++ b/modules/costs/config/locales/crowdin/ckb-IR.yml @@ -205,7 +205,10 @@ ckb-IR: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/cs.yml b/modules/costs/config/locales/crowdin/cs.yml index 2542916b666..139bd7778c9 100644 --- a/modules/costs/config/locales/crowdin/cs.yml +++ b/modules/costs/config/locales/crowdin/cs.yml @@ -213,7 +213,10 @@ cs: project_module_costs: "Čas a náklady" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Měna" - setting_costs_currency_format: "Formát měny" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/da.yml b/modules/costs/config/locales/crowdin/da.yml index f7259e00f2c..7b576f85061 100644 --- a/modules/costs/config/locales/crowdin/da.yml +++ b/modules/costs/config/locales/crowdin/da.yml @@ -205,7 +205,10 @@ da: project_module_costs: "Tid og omkostninger" setting_allow_tracking_start_and_end_times: "Tillad start- og sluttider" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Valutaformat" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Kræv start- og sluttider" setting_enforce_without_allow: "Det er ikke muligt at kræve start- og sluttider uden at tillade dem" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/de.yml b/modules/costs/config/locales/crowdin/de.yml index f29aade3676..8d9d9183c51 100644 --- a/modules/costs/config/locales/crowdin/de.yml +++ b/modules/costs/config/locales/crowdin/de.yml @@ -205,7 +205,10 @@ de: project_module_costs: "Zeit und Kosten" setting_allow_tracking_start_and_end_times: "Start- und Endzeiten erlaubt" setting_costs_currency: "Währung" + setting_costs_currency_caption: "Dies ist die Einheit der Währung. Es kann ein ISO-Code mit drei Buchstaben wie EUR, USD oder JPY sein oder ein Symbol wie €, $ oder ¥." setting_costs_currency_format: "Format der Währung" + setting_costs_currency_format_prefix: "Vor der Zahl (z.B. EUR 100)" + setting_costs_currency_format_suffix: "Nach der Zahl (z.B. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Start- und Endzeiten erforderlich" setting_enforce_without_allow: "Start und Endzeiten können nur erforderlich sein, wenn ihre Angabe erlaubt ist" setting_allow_tracking_start_and_end_times_caption: "Erlaubt es bei der Zeitbuchung die genaue Start- und Endzeiten der Buchung zu erfassen." diff --git a/modules/costs/config/locales/crowdin/el.yml b/modules/costs/config/locales/crowdin/el.yml index 32a780bbc69..efef01b9b87 100644 --- a/modules/costs/config/locales/crowdin/el.yml +++ b/modules/costs/config/locales/crowdin/el.yml @@ -205,7 +205,10 @@ el: project_module_costs: "Χρόνος και κόστος" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Νόμισμα" - setting_costs_currency_format: "Μορφοποίηση του νομίσματος" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/eo.yml b/modules/costs/config/locales/crowdin/eo.yml index aa4d5f4f3db..841aa1a8e71 100644 --- a/modules/costs/config/locales/crowdin/eo.yml +++ b/modules/costs/config/locales/crowdin/eo.yml @@ -205,7 +205,10 @@ eo: project_module_costs: "Tempo kaj kostoj" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valuto" - setting_costs_currency_format: "Formato de valuto" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/es.yml b/modules/costs/config/locales/crowdin/es.yml index 2760820cf51..26f864fe58c 100644 --- a/modules/costs/config/locales/crowdin/es.yml +++ b/modules/costs/config/locales/crowdin/es.yml @@ -205,7 +205,10 @@ es: project_module_costs: "Tiempo y costos" setting_allow_tracking_start_and_end_times: "Permitir horas de inicio y finalización" setting_costs_currency: "Moneda" - setting_costs_currency_format: "Formato de moneda" + setting_costs_currency_caption: "Se refiere a la unidad monetaria. Puede ser un código ISO de tres letras, como EUR, USD o JPY, o un símbolo, como €, $ o ¥." + setting_costs_currency_format: "Formato de la moneda" + setting_costs_currency_format_prefix: "Antes del importe (por ejemplo, EUR 100)" + setting_costs_currency_format_suffix: "Después del importe (por ejemplo, 100 EUR)" setting_enforce_tracking_start_and_end_times: "Requerir horas de inicio y finalización" setting_enforce_without_allow: "No es posible requerir horas de inicio y finalización sin permitirlas antes" setting_allow_tracking_start_and_end_times_caption: "Habilita la introducción de las horas de inicio y finalización cuando se registra tiempo." diff --git a/modules/costs/config/locales/crowdin/et.yml b/modules/costs/config/locales/crowdin/et.yml index 7048db1c4f0..a16f0a3a120 100644 --- a/modules/costs/config/locales/crowdin/et.yml +++ b/modules/costs/config/locales/crowdin/et.yml @@ -205,7 +205,10 @@ et: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valuuta" - setting_costs_currency_format: "Valuuta vorming" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/eu.yml b/modules/costs/config/locales/crowdin/eu.yml index 85fa6da2032..b98c75d8e89 100644 --- a/modules/costs/config/locales/crowdin/eu.yml +++ b/modules/costs/config/locales/crowdin/eu.yml @@ -205,7 +205,10 @@ eu: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/fa.yml b/modules/costs/config/locales/crowdin/fa.yml index e34f625945c..887fb6e5159 100644 --- a/modules/costs/config/locales/crowdin/fa.yml +++ b/modules/costs/config/locales/crowdin/fa.yml @@ -205,7 +205,10 @@ fa: project_module_costs: "زمان و هزینه‌ها" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "واحد پول" - setting_costs_currency_format: "فرمت واحد پول" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "درخواست زمان های شروع و پایان" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/fi.yml b/modules/costs/config/locales/crowdin/fi.yml index 015e9ba4d91..2a157dd17f8 100644 --- a/modules/costs/config/locales/crowdin/fi.yml +++ b/modules/costs/config/locales/crowdin/fi.yml @@ -205,7 +205,10 @@ fi: project_module_costs: "Työaika ja kustannukset" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valuutta" - setting_costs_currency_format: "Valuuttamuotoilu" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/fil.yml b/modules/costs/config/locales/crowdin/fil.yml index db83689193b..85d811e3543 100644 --- a/modules/costs/config/locales/crowdin/fil.yml +++ b/modules/costs/config/locales/crowdin/fil.yml @@ -205,7 +205,10 @@ fil: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/fr.yml b/modules/costs/config/locales/crowdin/fr.yml index 0164a551c16..74712cab846 100644 --- a/modules/costs/config/locales/crowdin/fr.yml +++ b/modules/costs/config/locales/crowdin/fr.yml @@ -205,7 +205,10 @@ fr: project_module_costs: "Temps et coûts" setting_allow_tracking_start_and_end_times: "Autoriser les heures de début et de fin" setting_costs_currency: "Devise" + setting_costs_currency_caption: "Il s'agit de l'unité monétaire. Il peut s'agir d'un code ISO à trois lettres comme EUR, USD ou JPY ou d'un symbole comme €, $ ou ¥." setting_costs_currency_format: "Format de devise" + setting_costs_currency_format_prefix: "Avant le nombre (par exemple 100 EUR)" + setting_costs_currency_format_suffix: "Après le nombre (par exemple 100 EUR)" setting_enforce_tracking_start_and_end_times: "Exiger les heures de début et de fin" setting_enforce_without_allow: "Il est nécessaire d'autoriser les heures de début et de fin pour pouvoir les exiger" setting_allow_tracking_start_and_end_times_caption: "Active la saisie des heures de début et de fin lors de la journalisation." diff --git a/modules/costs/config/locales/crowdin/he.yml b/modules/costs/config/locales/crowdin/he.yml index 53257a7a46e..b4369f9a43c 100644 --- a/modules/costs/config/locales/crowdin/he.yml +++ b/modules/costs/config/locales/crowdin/he.yml @@ -213,7 +213,10 @@ he: project_module_costs: "עלויות" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/hi.yml b/modules/costs/config/locales/crowdin/hi.yml index 53b1f3f0ecf..19a1e050859 100644 --- a/modules/costs/config/locales/crowdin/hi.yml +++ b/modules/costs/config/locales/crowdin/hi.yml @@ -205,7 +205,10 @@ hi: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/hr.yml b/modules/costs/config/locales/crowdin/hr.yml index af8cb7a88c7..06377b288c7 100644 --- a/modules/costs/config/locales/crowdin/hr.yml +++ b/modules/costs/config/locales/crowdin/hr.yml @@ -209,7 +209,10 @@ hr: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Oblik valute" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/hu.yml b/modules/costs/config/locales/crowdin/hu.yml index b6c633e2a7e..ab70582879a 100644 --- a/modules/costs/config/locales/crowdin/hu.yml +++ b/modules/costs/config/locales/crowdin/hu.yml @@ -205,7 +205,10 @@ hu: project_module_costs: "Idő és költség" setting_allow_tracking_start_and_end_times: "Engedélyezze a kezdési és befejezési időpontokat" setting_costs_currency: "Pénznem" - setting_costs_currency_format: "Pénznem formátuma" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Kezdési és befejezési idő megadása kötelező" setting_enforce_without_allow: "A kezdési és befejezési idő megkövetelése nem lehetséges anélkül, hogy engedélyeznénk azok megadását" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/id.yml b/modules/costs/config/locales/crowdin/id.yml index 05efe611b10..fa8b2ca0329 100644 --- a/modules/costs/config/locales/crowdin/id.yml +++ b/modules/costs/config/locales/crowdin/id.yml @@ -201,7 +201,10 @@ id: project_module_costs: "Waktu dan biaya" setting_allow_tracking_start_and_end_times: "Izinkan waktu mulai dan waktu selesai" setting_costs_currency: "Mata Uang" - setting_costs_currency_format: "Format mata uang" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Butuh waktu mulai dan selesai" setting_enforce_without_allow: "Membutuhkan waktu mulai dan selesai tidak mungkin dilakukan tanpa mengizinkannya" setting_allow_tracking_start_and_end_times_caption: "Memungkinkan untuk memasukkan waktu mulai dan selesai saat mencatat waktu." diff --git a/modules/costs/config/locales/crowdin/it.yml b/modules/costs/config/locales/crowdin/it.yml index f7c0cf849d7..bd91938ee28 100644 --- a/modules/costs/config/locales/crowdin/it.yml +++ b/modules/costs/config/locales/crowdin/it.yml @@ -205,7 +205,10 @@ it: project_module_costs: "Tempi e costi" setting_allow_tracking_start_and_end_times: "Consenti orari di inizio e fine" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Formato della valuta" + setting_costs_currency_caption: "Questa è l'unità di valuta. Può essere un codice ISO a tre lettere come EUR, USD o JPY o un simbolo come €, $ o 한." + setting_costs_currency_format: "Formato valuta" + setting_costs_currency_format_prefix: "Prima del numero (ad esempio, EUR 100)" + setting_costs_currency_format_suffix: "Dopo il numero (ad esempio, 100 EUR)" setting_enforce_tracking_start_and_end_times: "Richiede orari di inizio e di fine" setting_enforce_without_allow: "Non è possibile richiedere orari di inizio e fine senza consentirli" setting_allow_tracking_start_and_end_times_caption: "Consente di inserire gli orari di inizio e di fine durante la registrazione dell'orario." diff --git a/modules/costs/config/locales/crowdin/ja.yml b/modules/costs/config/locales/crowdin/ja.yml index a5c5f462667..20532a2611f 100644 --- a/modules/costs/config/locales/crowdin/ja.yml +++ b/modules/costs/config/locales/crowdin/ja.yml @@ -201,11 +201,14 @@ ja: project_module_costs: "時間とコスト" setting_allow_tracking_start_and_end_times: "開始時間と終了時間を許可する" setting_costs_currency: "通貨" - setting_costs_currency_format: "通貨の形式" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "開始/終了時間を必須とする" setting_enforce_without_allow: "開始時間と終了時間を要求することは許可されていないとできません" setting_allow_tracking_start_and_end_times_caption: "時間を記録する際に、開始時間と終了時間を入力できるようにする。" - setting_enforce_tracking_start_and_end_times_caption: "時間を記録する際、開始時間と終了時間の入力を必須にします。" + setting_enforce_tracking_start_and_end_times_caption: "時間を記録する際、開始時間と終了時間の入力が必須となる。" text_assign_time_and_cost_entries_to_project: "報告された時間とコストをプロジェクトに割り当てる" text_destroy_cost_entries_question: "削除しようとしているワークパッケージが%{cost_entries} 件報告されました。どうしますか?" text_destroy_time_and_cost_entries: "報告された時間とコストを削除する" diff --git a/modules/costs/config/locales/crowdin/ka.yml b/modules/costs/config/locales/crowdin/ka.yml index 3db3c1d8f2a..82f1853414e 100644 --- a/modules/costs/config/locales/crowdin/ka.yml +++ b/modules/costs/config/locales/crowdin/ka.yml @@ -205,7 +205,10 @@ ka: project_module_costs: "დრო და ღირებულებები" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "ფული" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/kk.yml b/modules/costs/config/locales/crowdin/kk.yml index 57d9130f893..ad2aa157d7b 100644 --- a/modules/costs/config/locales/crowdin/kk.yml +++ b/modules/costs/config/locales/crowdin/kk.yml @@ -205,7 +205,10 @@ kk: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ko.yml b/modules/costs/config/locales/crowdin/ko.yml index 9d67d328248..3d1a75f7ab3 100644 --- a/modules/costs/config/locales/crowdin/ko.yml +++ b/modules/costs/config/locales/crowdin/ko.yml @@ -201,7 +201,10 @@ ko: project_module_costs: "시간 및 비용" setting_allow_tracking_start_and_end_times: "시작 및 완료 시간 허용" setting_costs_currency: "통화" + setting_costs_currency_caption: "통화 단위입니다. EUR, USD, JPY 같은 문자 세 개의 ISO 코드 또는 €, $, ¥ 같은 기호일 수 있습니다." setting_costs_currency_format: "통화 형식" + setting_costs_currency_format_prefix: "숫자 앞(예: EUR 100)" + setting_costs_currency_format_suffix: "숫자 뒤(예: 100 EUR)" setting_enforce_tracking_start_and_end_times: "시작 및 완료 시간 필요" setting_enforce_without_allow: "이 옵션을 허용하지 않으면 시작 및 완료 시간 요구가 가능하지 않습니다" setting_allow_tracking_start_and_end_times_caption: "시간을 기록할 때 시작 및 완료 시간 입력을 활성화합니다." diff --git a/modules/costs/config/locales/crowdin/lt.yml b/modules/costs/config/locales/crowdin/lt.yml index 31165c017ae..e8fac6161b9 100644 --- a/modules/costs/config/locales/crowdin/lt.yml +++ b/modules/costs/config/locales/crowdin/lt.yml @@ -213,7 +213,10 @@ lt: project_module_costs: "Laikas ir išlaidos" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valiuta" - setting_costs_currency_format: "Valiutos formatas" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/lv.yml b/modules/costs/config/locales/crowdin/lv.yml index 09984b6795f..c9acdae640b 100644 --- a/modules/costs/config/locales/crowdin/lv.yml +++ b/modules/costs/config/locales/crowdin/lv.yml @@ -209,7 +209,10 @@ lv: project_module_costs: "Laiks un izmaksas" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valūta" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/mn.yml b/modules/costs/config/locales/crowdin/mn.yml index c3962b2b876..f5ba49c5f68 100644 --- a/modules/costs/config/locales/crowdin/mn.yml +++ b/modules/costs/config/locales/crowdin/mn.yml @@ -205,7 +205,10 @@ mn: project_module_costs: "Цаг хугацаа ба зардал" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ms.yml b/modules/costs/config/locales/crowdin/ms.yml index 328e1d78d0c..17eb61b6ee3 100644 --- a/modules/costs/config/locales/crowdin/ms.yml +++ b/modules/costs/config/locales/crowdin/ms.yml @@ -201,7 +201,10 @@ ms: project_module_costs: "Masa dan kos" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Mata Wang" - setting_costs_currency_format: "Format mata wang" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ne.yml b/modules/costs/config/locales/crowdin/ne.yml index 29448b1bb88..3ec82ad2b7f 100644 --- a/modules/costs/config/locales/crowdin/ne.yml +++ b/modules/costs/config/locales/crowdin/ne.yml @@ -205,7 +205,10 @@ ne: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/nl.yml b/modules/costs/config/locales/crowdin/nl.yml index b8b072b92b7..657b6b1045c 100644 --- a/modules/costs/config/locales/crowdin/nl.yml +++ b/modules/costs/config/locales/crowdin/nl.yml @@ -205,7 +205,10 @@ nl: project_module_costs: "Tijd en kosten" setting_allow_tracking_start_and_end_times: "Begin- en eindtijden toestaan" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Formaat van valuta" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Begin- en eindtijden vereisen" setting_enforce_without_allow: "Het is niet mogelijk om begin- en eindtijden te vereisen zonder ze toe te staan" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/no.yml b/modules/costs/config/locales/crowdin/no.yml index 9b230900978..04be7016cc3 100644 --- a/modules/costs/config/locales/crowdin/no.yml +++ b/modules/costs/config/locales/crowdin/no.yml @@ -205,7 +205,10 @@ project_module_costs: "Tid og kostnader" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Formatet på valuta" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/pl.yml b/modules/costs/config/locales/crowdin/pl.yml index 21fdaf3ba2b..b5baf20cc3e 100644 --- a/modules/costs/config/locales/crowdin/pl.yml +++ b/modules/costs/config/locales/crowdin/pl.yml @@ -213,7 +213,10 @@ pl: project_module_costs: "Czas i koszty" setting_allow_tracking_start_and_end_times: "Zezwól na czasy rozpoczęcia i zakończenia" setting_costs_currency: "Waluta" + setting_costs_currency_caption: "Jest to jednostka waluty. Może to być trzyliterowy kod ISO, taki jak EUR, USD lub JPY albo symbol, taki jak €, $ lub ¥." setting_costs_currency_format: "Format waluty" + setting_costs_currency_format_prefix: "Przed liczbą (np. EUR 100)" + setting_costs_currency_format_suffix: "Po liczbie (np. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Wymagaj czasów rozpoczęcia i zakończenia" setting_enforce_without_allow: "Nie można wymagać czasów rozpoczęcia i zakończenia bez zezwolenia na nie" setting_allow_tracking_start_and_end_times_caption: "Umożliwia wprowadzenie czasów rozpoczęcia i zakończenia w razie rejestrowania czasu." diff --git a/modules/costs/config/locales/crowdin/pt-BR.yml b/modules/costs/config/locales/crowdin/pt-BR.yml index a80308e2a7b..85049ad6152 100644 --- a/modules/costs/config/locales/crowdin/pt-BR.yml +++ b/modules/costs/config/locales/crowdin/pt-BR.yml @@ -205,7 +205,10 @@ pt-BR: project_module_costs: "Tempo e custos" setting_allow_tracking_start_and_end_times: "Permitir horários de início e de término" setting_costs_currency: "Moeda" - setting_costs_currency_format: "Formato de moeda" + setting_costs_currency_caption: "Esta é a moeda utilizada. Pode ser representada por um código ISO de três letras, como EUR, USD ou JPY, ou por um símbolo, como €, $ ou ¥." + setting_costs_currency_format: "Formato da moeda" + setting_costs_currency_format_prefix: "Antes do valor (por exemplo: EUR 100)" + setting_costs_currency_format_suffix: "Após o valor (por exemplo: 100 EUR)" setting_enforce_tracking_start_and_end_times: "Exigir horários de início e término" setting_enforce_without_allow: "Exigir os horários de início e término não é possível sem habilitá-los" setting_allow_tracking_start_and_end_times_caption: "Ativa a inserção de horários de início e término ao registrar o tempo." diff --git a/modules/costs/config/locales/crowdin/pt-PT.yml b/modules/costs/config/locales/crowdin/pt-PT.yml index f54963fc1f2..80651578281 100644 --- a/modules/costs/config/locales/crowdin/pt-PT.yml +++ b/modules/costs/config/locales/crowdin/pt-PT.yml @@ -205,7 +205,10 @@ pt-PT: project_module_costs: "Tempo e custos" setting_allow_tracking_start_and_end_times: "Permitir horário de início e término" setting_costs_currency: "Moeda" - setting_costs_currency_format: "Formato de moeda" + setting_costs_currency_caption: "Esta é a unidade monetária. Pode ser um código ISO de três letras como EUR, USD ou JPY ou um símbolo como €, $ ou ¥." + setting_costs_currency_format: "Formato da moeda" + setting_costs_currency_format_prefix: "Antes do número (por exemplo, 100 euros)" + setting_costs_currency_format_suffix: "Após o número (por exemplo, 100 EUR)" setting_enforce_tracking_start_and_end_times: "Exigir hora de início e término" setting_enforce_without_allow: "Não é possível exigir horas de início e de fim sem as autorizar" setting_allow_tracking_start_and_end_times_caption: "Permite introduzir as horas de início e de término ao registar o tempo." diff --git a/modules/costs/config/locales/crowdin/ro.yml b/modules/costs/config/locales/crowdin/ro.yml index 3cce081e02b..e1e0ace856c 100644 --- a/modules/costs/config/locales/crowdin/ro.yml +++ b/modules/costs/config/locales/crowdin/ro.yml @@ -209,7 +209,10 @@ ro: project_module_costs: "Timp și costuri" setting_allow_tracking_start_and_end_times: "Permite orele de început și de sfârșit" setting_costs_currency: "Monedă" - setting_costs_currency_format: "Format monedă" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Cere orele de început și de sfârșit" setting_enforce_without_allow: "Solicitarea orelor de început și de sfârșit nu este posibilă fără a le permite" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/ru.yml b/modules/costs/config/locales/crowdin/ru.yml index 244a4596166..69f9ca319be 100644 --- a/modules/costs/config/locales/crowdin/ru.yml +++ b/modules/costs/config/locales/crowdin/ru.yml @@ -213,7 +213,10 @@ ru: project_module_costs: "Время и затраты" setting_allow_tracking_start_and_end_times: "Разрешить время начала и окончания" setting_costs_currency: "Валюта" + setting_costs_currency_caption: "Это единица валюты. Это может быть трехбуквенный код ISO, например, EUR, USD или JPY, или символ, например, €, $ или ¥." setting_costs_currency_format: "Формат валюты" + setting_costs_currency_format_prefix: "Перед числом (например, EUR 100)" + setting_costs_currency_format_suffix: "После числа (например, 100 EUR)" setting_enforce_tracking_start_and_end_times: "Требуется время начала и окончания" setting_enforce_without_allow: "Требование точного времени невозможно без соответствующего разрешения" setting_allow_tracking_start_and_end_times_caption: "Включает время начала и окончания учета." diff --git a/modules/costs/config/locales/crowdin/rw.yml b/modules/costs/config/locales/crowdin/rw.yml index 5e4e44e7a8e..20e2547d233 100644 --- a/modules/costs/config/locales/crowdin/rw.yml +++ b/modules/costs/config/locales/crowdin/rw.yml @@ -205,7 +205,10 @@ rw: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/si.yml b/modules/costs/config/locales/crowdin/si.yml index 2308be376ca..208b3042c6e 100644 --- a/modules/costs/config/locales/crowdin/si.yml +++ b/modules/costs/config/locales/crowdin/si.yml @@ -205,7 +205,10 @@ si: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/sk.yml b/modules/costs/config/locales/crowdin/sk.yml index 18439ac2f68..6c8bc919c75 100644 --- a/modules/costs/config/locales/crowdin/sk.yml +++ b/modules/costs/config/locales/crowdin/sk.yml @@ -213,7 +213,10 @@ sk: project_module_costs: "Čas a náklady" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Mena" - setting_costs_currency_format: "Formát meny" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/sl.yml b/modules/costs/config/locales/crowdin/sl.yml index 40c9a106483..304ccd44bb6 100644 --- a/modules/costs/config/locales/crowdin/sl.yml +++ b/modules/costs/config/locales/crowdin/sl.yml @@ -213,7 +213,10 @@ sl: project_module_costs: "Čas in stroški" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Oblika valute" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/sr.yml b/modules/costs/config/locales/crowdin/sr.yml index 5b4d2d2aca4..6638b4d33a3 100644 --- a/modules/costs/config/locales/crowdin/sr.yml +++ b/modules/costs/config/locales/crowdin/sr.yml @@ -209,7 +209,10 @@ sr: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/sv.yml b/modules/costs/config/locales/crowdin/sv.yml index bf37e762df9..bd40b4b80c9 100644 --- a/modules/costs/config/locales/crowdin/sv.yml +++ b/modules/costs/config/locales/crowdin/sv.yml @@ -205,7 +205,10 @@ sv: project_module_costs: "Tid och kostnader" setting_allow_tracking_start_and_end_times: "Tillåt start- och sluttider" setting_costs_currency: "Valuta" - setting_costs_currency_format: "Formatet för valuta" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Kräv start- och sluttider" setting_enforce_without_allow: "Det är inte möjligt att begära start- och sluttider utan att låta dem" setting_allow_tracking_start_and_end_times_caption: "Gör det möjligt att ange start- och sluttider vid tidsloggning." diff --git a/modules/costs/config/locales/crowdin/th.yml b/modules/costs/config/locales/crowdin/th.yml index 772a41d8e0c..9f04b5cf116 100644 --- a/modules/costs/config/locales/crowdin/th.yml +++ b/modules/costs/config/locales/crowdin/th.yml @@ -201,7 +201,10 @@ th: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/tr.yml b/modules/costs/config/locales/crowdin/tr.yml index c959bb4b05b..2b758d6a338 100644 --- a/modules/costs/config/locales/crowdin/tr.yml +++ b/modules/costs/config/locales/crowdin/tr.yml @@ -205,7 +205,10 @@ tr: project_module_costs: "Zaman ve maliyetler" setting_allow_tracking_start_and_end_times: "Başlangıç ve bitiş saatlerine izin verin" setting_costs_currency: "Para birimi" - setting_costs_currency_format: "Para biriminin biçimi" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Başlangıç ve bitiş saatleri gerektir" setting_enforce_without_allow: "Başlangıç ve bitiş saatlerini zorunlu tutmak, bunlara izin vermeden mümkün değildir" setting_allow_tracking_start_and_end_times_caption: "Zaman kaydı yaparken başlangıç ve bitiş zamanlarının girilmesini sağlar." diff --git a/modules/costs/config/locales/crowdin/uk.yml b/modules/costs/config/locales/crowdin/uk.yml index 95cb3cbd8dd..d0ca340d910 100644 --- a/modules/costs/config/locales/crowdin/uk.yml +++ b/modules/costs/config/locales/crowdin/uk.yml @@ -213,7 +213,10 @@ uk: project_module_costs: "Час і витрати" setting_allow_tracking_start_and_end_times: "Дозволити час початку й закінчення" setting_costs_currency: "Валюта" + setting_costs_currency_caption: "Це одиниця валюти. На її позначення може використовуватися трилітерний код ISO (наприклад, EUR, USD чи JPY) або символ (€, $, ¥ тощо)." setting_costs_currency_format: "Формат валюти" + setting_costs_currency_format_prefix: "Перед числом (наприклад, EUR 100)" + setting_costs_currency_format_suffix: "Після числа (наприклад, 100 EUR)" setting_enforce_tracking_start_and_end_times: "Вимагати час початку й закінчення" setting_enforce_without_allow: "Вимога часу початку й закінчення неможлива без відповідного налаштування" setting_allow_tracking_start_and_end_times_caption: "Дає змогу вводити час початку й закінчення в журнал обліку часу." diff --git a/modules/costs/config/locales/crowdin/uz.yml b/modules/costs/config/locales/crowdin/uz.yml index a75e486fe67..b245e161da9 100644 --- a/modules/costs/config/locales/crowdin/uz.yml +++ b/modules/costs/config/locales/crowdin/uz.yml @@ -205,7 +205,10 @@ uz: project_module_costs: "Time and costs" setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/config/locales/crowdin/vi.yml b/modules/costs/config/locales/crowdin/vi.yml index e7c0268d25b..bac89a48180 100644 --- a/modules/costs/config/locales/crowdin/vi.yml +++ b/modules/costs/config/locales/crowdin/vi.yml @@ -201,7 +201,10 @@ vi: project_module_costs: "Thời gian và chi phí" setting_allow_tracking_start_and_end_times: "Cho phép thời gian bắt đầu và kết thúc" setting_costs_currency: "tiền tệ" - setting_costs_currency_format: "Định dạng tiền tệ" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Yêu cầu thời gian bắt đầu và kết thúc" setting_enforce_without_allow: "Không thể yêu cầu thời gian bắt đầu và kết thúc nếu không cho phép chúng" setting_allow_tracking_start_and_end_times_caption: "Cho phép nhập thời gian bắt đầu và kết thúc khi ghi thời gian." diff --git a/modules/costs/config/locales/crowdin/zh-CN.yml b/modules/costs/config/locales/crowdin/zh-CN.yml index 8c5dff2879a..f221761a2e3 100644 --- a/modules/costs/config/locales/crowdin/zh-CN.yml +++ b/modules/costs/config/locales/crowdin/zh-CN.yml @@ -201,7 +201,10 @@ zh-CN: project_module_costs: "工时和成本" setting_allow_tracking_start_and_end_times: "允许开始和完成时间" setting_costs_currency: "货币" + setting_costs_currency_caption: "这是货币单位。可以是 EUR、USD 或 JPY 等三个字母的 ISO 代码,也可以是 €、$ 或 ¥ 等符号。" setting_costs_currency_format: "货币格式" + setting_costs_currency_format_prefix: "在数字前(例如 EUR 100)" + setting_costs_currency_format_suffix: "在数字后(例如 100 EUR)" setting_enforce_tracking_start_and_end_times: "需要开始和完成时间" setting_enforce_without_allow: "如果不允许,则无法要求开始和结束时间" setting_allow_tracking_start_and_end_times_caption: "启用在记录时间时输入开始和结束时间。" diff --git a/modules/costs/config/locales/crowdin/zh-TW.yml b/modules/costs/config/locales/crowdin/zh-TW.yml index 2209da088d6..d55463b7e1a 100644 --- a/modules/costs/config/locales/crowdin/zh-TW.yml +++ b/modules/costs/config/locales/crowdin/zh-TW.yml @@ -201,7 +201,10 @@ zh-TW: project_module_costs: "時間與費用" setting_allow_tracking_start_and_end_times: "允許起迄時間" setting_costs_currency: "貨幣" - setting_costs_currency_format: " 貨幣格式" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "需要起迄時間" setting_enforce_without_allow: "在不允許的情況下,要求開始和結束時間是不可能的" setting_allow_tracking_start_and_end_times_caption: "啟用記錄工時時輸入開始與結束時間的功能。" diff --git a/modules/costs/config/locales/en.yml b/modules/costs/config/locales/en.yml index f6d3a09f635..1ebae1bde1a 100644 --- a/modules/costs/config/locales/en.yml +++ b/modules/costs/config/locales/en.yml @@ -224,7 +224,10 @@ en: setting_allow_tracking_start_and_end_times: "Allow start and finish times" setting_costs_currency: "Currency" - setting_costs_currency_format: "Format of currency" + setting_costs_currency_caption: "This is the unit of currency. It can be a three-letter ISO code like EUR, USD or JPY or a symbol like €, $ or ¥." + setting_costs_currency_format: "Currency format" + setting_costs_currency_format_prefix: "Before the number (e.g. EUR 100)" + setting_costs_currency_format_suffix: "After the number (e.g. 100 EUR)" setting_enforce_tracking_start_and_end_times: "Require start and finish times" setting_enforce_without_allow: "Requiring start and finish times is not possible without allowing them" setting_allow_tracking_start_and_end_times_caption: "Enables entering start and finish times when logging time." diff --git a/modules/costs/db/migrate/20260303172517_suffix_or_prefix_currency_format.rb b/modules/costs/db/migrate/20260303172517_suffix_or_prefix_currency_format.rb new file mode 100644 index 00000000000..ee9ac1560b6 --- /dev/null +++ b/modules/costs/db/migrate/20260303172517_suffix_or_prefix_currency_format.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +class SuffixOrPrefixCurrencyFormat < ActiveRecord::Migration[8.1] + def up + execute <<~SQL.squish + UPDATE settings + SET value = '%n %u' + WHERE name = 'costs_currency_format' + AND value NOT IN ('%u %n', '%n %u'); + SQL + end +end diff --git a/modules/costs/lib/costs/engine.rb b/modules/costs/lib/costs/engine.rb index 698b425fba4..1fe57a590fd 100644 --- a/modules/costs/lib/costs/engine.rb +++ b/modules/costs/lib/costs/engine.rb @@ -167,7 +167,7 @@ module Costs initializer "costs.settings" do ::Settings::Definition.add "costs_currency", default: "€", format: :string - ::Settings::Definition.add "costs_currency_format", default: "%n %u", format: :string + ::Settings::Definition.add "costs_currency_format", default: "%n %u", format: :string, allowed: ["%u %n", "%n %u"] ::Settings::Definition.add "allow_tracking_start_and_end_times", default: false, format: :boolean ::Settings::Definition.add "enforce_tracking_start_and_end_times", default: false, format: :boolean end diff --git a/modules/costs/spec/features/my_time_tracking_spec.rb b/modules/costs/spec/features/my_time_tracking_spec.rb index b85bbab5108..bc18a135024 100644 --- a/modules/costs/spec/features/my_time_tracking_spec.rb +++ b/modules/costs/spec/features/my_time_tracking_spec.rb @@ -74,7 +74,7 @@ RSpec.describe "my time tracking", :js do end around do |example| - travel_to "2025-04-09T12:00:00Z" do # rubocop:disable RSpecRails/TravelAround + travel_to "2025-04-09T12:00:00Z" do example.run end end diff --git a/modules/github_integration/app/workers/cron/check_deploy_status_job.rb b/modules/github_integration/app/workers/cron/check_deploy_status_job.rb index 6a27e3e5bd6..f8ea9bae7a0 100644 --- a/modules/github_integration/app/workers/cron/check_deploy_status_job.rb +++ b/modules/github_integration/app/workers/cron/check_deploy_status_job.rb @@ -157,7 +157,7 @@ module Cron end def introspection_request(host, api_token) - OpenProject.httpx.basic_auth("apikey", api_token).get("https://#{host}/api/v3") + OpenProject.httpx.plugin(:basic_auth).basic_auth("apikey", api_token).get("https://#{host}/api/v3") end ## @@ -188,7 +188,7 @@ module Cron ahead_by = data["ahead_by"].presence behind_by = data["behind_by"].presence - status == "behind" && ahead_by == 0 && (behind_by.present? && behind_by > 0) + status == "behind" && ahead_by == 0 && behind_by.present? && behind_by > 0 end def compare_commits(sha_a, sha_b) diff --git a/modules/gitlab_integration/config/locales/crowdin/rw.yml b/modules/gitlab_integration/config/locales/crowdin/rw.yml index 4b58a358650..a5ae5154287 100644 --- a/modules/gitlab_integration/config/locales/crowdin/rw.yml +++ b/modules/gitlab_integration/config/locales/crowdin/rw.yml @@ -60,17 +60,23 @@ rw: merge_request_reopened_comment: > **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > - **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): %{commit_note} + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + %{commit_note} note_mr_referenced_comment: > - **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + %{mr_note} note_mr_commented_comment: > - **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + %{mr_note} note_issue_referenced_comment: > - **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): %{issue_note} + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + %{issue_note} note_issue_commented_comment: > - **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): %{issue_note} + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + %{issue_note} note_snippet_referenced_comment: > - **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): %{snippet_note} + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + %{snippet_note} issue_opened_referenced_comment: > **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been opened by [%{gitlab_user}](%{gitlab_user_url}). issue_closed_referenced_comment: > @@ -78,8 +84,11 @@ rw: issue_reopened_referenced_comment: > **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > - **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: %{commit_note} + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} push_single_commit_comment_with_ref: > - **Pushed in %{reference}:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: %{commit_note} + **Pushed in %{reference}:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} push_multiple_commits_comment: > - **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: %{commit_note} + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/modules/gitlab_integration/config/locales/crowdin/uz.yml b/modules/gitlab_integration/config/locales/crowdin/uz.yml index 8159949474f..26da96002b9 100644 --- a/modules/gitlab_integration/config/locales/crowdin/uz.yml +++ b/modules/gitlab_integration/config/locales/crowdin/uz.yml @@ -60,17 +60,23 @@ uz: merge_request_reopened_comment: > **MR Reopened:** Merge request %{mr_number} [%{mr_title}](%{mr_url}) for [%{repository}](%{repository_url}) has been reopened by [%{gitlab_user}](%{gitlab_user_url}). note_commit_referenced_comment: > - **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): %{commit_note} + **Referenced in Commit:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in a Commit Note [%{commit_id}](%{commit_url}) on [%{repository}](%{repository_url}): + %{commit_note} note_mr_referenced_comment: > - **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} + **Referenced in MR:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + %{mr_note} note_mr_commented_comment: > - **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): %{mr_note} + **Commented in MR:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Merge Request %{mr_number} [%{mr_title}](%{mr_url}) on [%{repository}](%{repository_url}): + %{mr_note} note_issue_referenced_comment: > - **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): %{issue_note} + **Referenced in Issue:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + %{issue_note} note_issue_commented_comment: > - **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): %{issue_note} + **Commented in Issue:** [%{gitlab_user}](%{gitlab_user_url}) commented this WP in Issue %{issue_number} [%{issue_title}](%{issue_url}) on [%{repository}](%{repository_url}): + %{issue_note} note_snippet_referenced_comment: > - **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): %{snippet_note} + **Referenced in Snippet:** [%{gitlab_user}](%{gitlab_user_url}) referenced this WP in Snippet %{snippet_number} [%{snippet_title}](%{snippet_url}) on [%{repository}](%{repository_url}): + %{snippet_note} issue_opened_referenced_comment: > **Issue Opened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been opened by [%{gitlab_user}](%{gitlab_user_url}). issue_closed_referenced_comment: > @@ -78,8 +84,11 @@ uz: issue_reopened_referenced_comment: > **Issue Reopened:** Issue %{issue_number} [%{issue_title}](%{issue_url}) for [%{repository}](%{repository_url}) has been reopened by [%{gitlab_user}](%{gitlab_user_url}). push_single_commit_comment: > - **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: %{commit_note} + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} push_single_commit_comment_with_ref: > - **Pushed in %{reference}:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: %{commit_note} + **Pushed in %{reference}:** [%{gitlab_user}](%{gitlab_user_url}) pushed [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} push_multiple_commits_comment: > - **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: %{commit_note} + **Pushed in MR:** [%{gitlab_user}](%{gitlab_user_url}) pushed multiple commits [%{commit_number}](%{commit_url}) to [%{repository}](%{repository_url}) at %{commit_timestamp}: + %{commit_note} diff --git a/modules/grids/app/components/grids/widgets/members.rb b/modules/grids/app/components/grids/widgets/members.rb index 7d5d07acc7b..8366b4ae2ed 100644 --- a/modules/grids/app/components/grids/widgets/members.rb +++ b/modules/grids/app/components/grids/widgets/members.rb @@ -49,7 +49,7 @@ module Grids .map do |role| members_query = members_for_role(role.id) total_count = members_query.count - members = members_query.limit(limit).to_a.map(&:user) + members = members_query.limit(limit).to_a.map(&:principal) { role:, diff --git a/modules/ldap_groups/config/locales/crowdin/zh-CN.yml b/modules/ldap_groups/config/locales/crowdin/zh-CN.yml index cec35d2ee7b..de9f185509c 100644 --- a/modules/ldap_groups/config/locales/crowdin/zh-CN.yml +++ b/modules/ldap_groups/config/locales/crowdin/zh-CN.yml @@ -6,7 +6,7 @@ zh-CN: description: '与 OpenProject 组同步 LDAP 组以管理用户,更改他们的权限以便不同组的用户管理。' plugin_openproject_ldap_groups: name: "OpenProject LDAP 组" - description: "LDAP组成员同步。" + description: "LDAP 组成员同步。" activerecord: attributes: ldap_groups/synchronized_group: diff --git a/modules/ldap_groups/config/locales/crowdin/zh-TW.yml b/modules/ldap_groups/config/locales/crowdin/zh-TW.yml index 1a4720ebcaf..15ce71c0dc6 100644 --- a/modules/ldap_groups/config/locales/crowdin/zh-TW.yml +++ b/modules/ldap_groups/config/locales/crowdin/zh-TW.yml @@ -15,7 +15,7 @@ zh-TW: ldap_auth_source: 'LDAP 連線' sync_users: '同步使用者' ldap_groups/synchronized_filter: - filter_string: 'LDAP篩選條件' + filter_string: '簡約登入目錄制約(LDAP)篩選' auth_source: '驗證來源' ldap_auth_source: 'LDAP 連線' group_name_attribute: "群組名字屬性" diff --git a/modules/meeting/app/components/meetings/index/form_component.rb b/modules/meeting/app/components/meetings/index/form_component.rb index 119128c8f43..6e3e1dfe2ed 100644 --- a/modules/meeting/app/components/meetings/index/form_component.rb +++ b/modules/meeting/app/components/meetings/index/form_component.rb @@ -83,7 +83,7 @@ module Meetings def available_templates return [] unless @project - @available_templates ||= Meeting.onetime_templates.where(project: @project).visible + @available_templates ||= Meeting.templates_visible_in_project(@project) end end end diff --git a/modules/meeting/app/components/meetings/side_panel/sharing_component.html.erb b/modules/meeting/app/components/meetings/side_panel/sharing_component.html.erb new file mode 100644 index 00000000000..b1364f077a5 --- /dev/null +++ b/modules/meeting/app/components/meetings/side_panel/sharing_component.html.erb @@ -0,0 +1,66 @@ +<%#-- 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. + +++#%> + +<%= + component_wrapper do + render(Primer::OpenProject::SidePanel::Section.new) do |section| + section.with_title { t(:label_meeting_template_sharing) } + + flex_layout do |content| + content.with_row do + render(Primer::Beta::Text.new(color: :subtle)) do + t(:text_meeting_template_sharing_description) + end + end + + content.with_row(mt: 3) do + render(Primer::Alpha::ActionMenu.new) do |menu| + menu.with_show_button(scheme: :secondary) do |button| + button.with_trailing_visual_icon(icon: :"triangle-down") + sharing_label(@meeting.sharing) + end + + Meeting.sharings.each_key do |sharing| + menu.with_item( + label: sharing_label(sharing), + tag: :a, + href: change_sharing_path(sharing), + content_arguments: { data: { + action: "click->meetings--submit#intercept", + href: change_sharing_path(sharing), + method: "PUT" + } } + ) + end + end + end + end + end + end +%> diff --git a/modules/meeting/app/components/meetings/side_panel/sharing_component.rb b/modules/meeting/app/components/meetings/side_panel/sharing_component.rb new file mode 100644 index 00000000000..89547b5620a --- /dev/null +++ b/modules/meeting/app/components/meetings/side_panel/sharing_component.rb @@ -0,0 +1,58 @@ +# 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 Meetings + class SidePanel::SharingComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + def initialize(meeting:) + super + + @meeting = meeting + @project = meeting.project + end + + def render? + User.current.allowed_in_project?(:edit_meetings, @project) + end + + private + + def sharing_label(sharing) + I18n.t("label_meeting_template_sharing_#{sharing}") + end + + def change_sharing_path(sharing) + change_sharing_project_meeting_path(@project, @meeting, sharing:) + end + end +end diff --git a/modules/meeting/app/components/meetings/side_panel_component.html.erb b/modules/meeting/app/components/meetings/side_panel_component.html.erb index c8ceb86e01f..1280f39c3b7 100644 --- a/modules/meeting/app/components/meetings/side_panel_component.html.erb +++ b/modules/meeting/app/components/meetings/side_panel_component.html.erb @@ -20,6 +20,10 @@ ) end + if @meeting.onetime_template? + panel.with_section(Meetings::SidePanel::SharingComponent.new(meeting: @meeting)) + end + panel.with_section( Meetings::SidePanel::AttachmentsComponent.new(meeting: @meeting), grid_row_arguments: desktop_grid_row_arguments diff --git a/modules/meeting/app/contracts/meetings/base_contract.rb b/modules/meeting/app/contracts/meetings/base_contract.rb index 8e51766fad3..25400c061fb 100644 --- a/modules/meeting/app/contracts/meetings/base_contract.rb +++ b/modules/meeting/app/contracts/meetings/base_contract.rb @@ -44,5 +44,16 @@ module Meetings attribute :start_time_hour attribute :template attribute :notify + attribute :sharing do + validate_sharing_only_on_onetime_templates + end + + private + + def validate_sharing_only_on_onetime_templates + return if model.onetime_template? + + errors.add :sharing, :not_allowed if model.sharing.present? + end end end diff --git a/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb b/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb index 4f6792e8d26..b38ec1790b5 100644 --- a/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb +++ b/modules/meeting/app/controllers/concerns/meetings/agenda_component_streams.rb @@ -89,6 +89,16 @@ module Meetings ) end + def update_sidebar_sharing_component_via_turbo_stream(meeting: @meeting) + return unless meeting.onetime_template? + + update_via_turbo_stream( + component: Meetings::SidePanel::SharingComponent.new( + meeting: + ) + ) + end + def update_add_user_form_component_via_turbo_stream(meeting: @meeting) update_via_turbo_stream( component: Meetings::Participants::AddUserFormComponent.new(meeting:) diff --git a/modules/meeting/app/controllers/meeting_templates_controller.rb b/modules/meeting/app/controllers/meeting_templates_controller.rb index 0e8b706fda4..0e6bb7074f4 100644 --- a/modules/meeting/app/controllers/meeting_templates_controller.rb +++ b/modules/meeting/app/controllers/meeting_templates_controller.rb @@ -40,11 +40,13 @@ class MeetingTemplatesController < ApplicationController menu_item :meetings def index - @templates = Meeting.onetime_templates - .visible - .order(:title) - - @templates = @templates.where(project_id: @project.id) if @project + @templates = if @project + Meeting.available_onetime_templates.where(project_id: @project.id).order(:title) + else + accessible_ids = Project.allowed_to(User.current, :view_meetings).select(:id) + base = Meeting.available_onetime_templates + base.where(project_id: accessible_ids).or(base.where(sharing: :system)).order(:title) + end render "meeting_templates/index", locals: { menu_name: project_or_global_menu } diff --git a/modules/meeting/app/controllers/meetings_controller.rb b/modules/meeting/app/controllers/meetings_controller.rb index e14950d30d3..3180d9a62f5 100644 --- a/modules/meeting/app/controllers/meetings_controller.rb +++ b/modules/meeting/app/controllers/meetings_controller.rb @@ -294,6 +294,23 @@ class MeetingsController < ApplicationController respond_with_turbo_streams end + def change_sharing + sharing = params[:sharing] + + if Meeting.sharings.key?(sharing) + call = ::Meetings::UpdateService + .new(user: current_user, model: @meeting) + .call(sharing:) + + render_base_error_in_flash_message_via_turbo_stream(call.errors) unless call.success? + end + + update_header_component_via_turbo_stream + update_sidebar_sharing_component_via_turbo_stream + + respond_with_turbo_streams + end + def download_ics ::Meetings::ICalService .new(user: current_user, meeting: @meeting) @@ -469,7 +486,7 @@ class MeetingsController < ApplicationController @meeting = call.result # When coming from the "Create from template" button, load the template to hide the form field - @copy_from = Meeting.onetime_templates.visible.find_by(id: params[:template_id]) if params[:template_id].present? + @copy_from = Meeting.templates_visible_in_project(@project).find_by(id: params[:template_id]) if params[:template_id].present? end def global_upcoming_meetings @@ -563,7 +580,7 @@ class MeetingsController < ApplicationController # Check for template selection from form submission template_id = params[:meeting][:template_id] if template_id.present? - @copy_from = Meeting.onetime_templates.visible.find_by(id: template_id) + @copy_from = Meeting.templates_visible_in_project(@project).find_by(id: template_id) return end diff --git a/modules/meeting/app/forms/meeting/template_autocompleter.rb b/modules/meeting/app/forms/meeting/template_autocompleter.rb index e236a3d1ec5..3a4f94ea3a0 100644 --- a/modules/meeting/app/forms/meeting/template_autocompleter.rb +++ b/modules/meeting/app/forms/meeting/template_autocompleter.rb @@ -61,6 +61,6 @@ class Meeting::TemplateAutocompleter < ApplicationForm private def templates - Meeting.onetime_templates.where(project: @project).visible.order(:title) + Meeting.templates_visible_in_project(@project).order(:title) end end diff --git a/modules/meeting/app/models/meeting.rb b/modules/meeting/app/models/meeting.rb index d5af7813fbf..8bc03bce7b7 100644 --- a/modules/meeting/app/models/meeting.rb +++ b/modules/meeting/app/models/meeting.rb @@ -91,6 +91,10 @@ class Meeting < ApplicationRecord joins(:participants).where(meeting_participants: { user_id: user.id }) } + scope :available_onetime_templates, -> { + onetime_templates.where(project_id: Project.active.select(:id)) + } + acts_as_attachable( after_remove: :attachments_changed, order: "#{Attachment.table_name}.file", @@ -118,6 +122,7 @@ class Meeting < ApplicationRecord accepts_nested_attributes_for :participants, allow_destroy: true validates :title, :project_id, presence: true + validates :sharing, absence: true, unless: :onetime_template? validates :duration, numericality: { greater_than: 0 } @@ -135,6 +140,21 @@ class Meeting < ApplicationRecord closed: 5 } + enum :sharing, { + none: "none", + descendants: "descendants", + system: "system" + }, prefix: :sharing, validate: { allow_nil: true } + + def self.templates_visible_in_project(project, user = User.current) + accessible_ids = Project.allowed_to(user, :view_meetings).select(:id) + + available_onetime_templates + .where(project_id: project.id).where(project_id: accessible_ids) + .or(available_onetime_templates.where(sharing: :descendants, project_id: project.ancestors.select(:id))) + .or(available_onetime_templates.where(sharing: :system)) + end + def recurring? recurring_meeting_id.present? end diff --git a/modules/meeting/app/services/meetings/copy_service.rb b/modules/meeting/app/services/meetings/copy_service.rb index 2d39db05136..eaa751e1e10 100644 --- a/modules/meeting/app/services/meetings/copy_service.rb +++ b/modules/meeting/app/services/meetings/copy_service.rb @@ -94,7 +94,7 @@ module Meetings end def writable_meeting_attributes(meeting) - instantiate_contract(meeting, user).writable_attributes - %w[start_date start_time_hour uid] + instantiate_contract(meeting, user).writable_attributes - %w[start_date start_time_hour uid sharing] end def copy_meeting_attachment(copy) diff --git a/modules/meeting/app/services/meetings/set_attributes_service.rb b/modules/meeting/app/services/meetings/set_attributes_service.rb index b8cd8b93a4b..dd5784d8586 100644 --- a/modules/meeting/app/services/meetings/set_attributes_service.rb +++ b/modules/meeting/app/services/meetings/set_attributes_service.rb @@ -41,12 +41,13 @@ module Meetings end end - def set_default_attributes(_params) + def set_default_attributes(_params) # rubocop:disable Metrics/AbcSize model.change_by_system do model.author = user model.duration ||= 1 model.state = "draft" if !model.recurring? || model.template? model.notify = false + model.sharing = "none" if model.onetime_template? end end diff --git a/modules/meeting/app/services/recurring_meetings/update_service.rb b/modules/meeting/app/services/recurring_meetings/update_service.rb index ea2dd48f60e..9f8db456784 100644 --- a/modules/meeting/app/services/recurring_meetings/update_service.rb +++ b/modules/meeting/app/services/recurring_meetings/update_service.rb @@ -162,10 +162,10 @@ module RecurringMeetings return if new_title == @old_title recurring_meeting - .scheduled_instances(upcoming: true) - .instantiated - .each do |scheduled| - scheduled.meeting.update_column(:title, new_title) + .scheduled_instances(upcoming: true) + .instantiated + .each do |scheduled| + scheduled.meeting.update_column(:title, new_title) end end diff --git a/modules/meeting/config/locales/crowdin/af.yml b/modules/meeting/config/locales/crowdin/af.yml index 2280c7bc4e4..b9944dd98a2 100644 --- a/modules/meeting/config/locales/crowdin/af.yml +++ b/modules/meeting/config/locales/crowdin/af.yml @@ -42,6 +42,7 @@ af: start_date: "Datum" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Titel" author: "Outeur" @@ -622,6 +623,11 @@ af: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ar.yml b/modules/meeting/config/locales/crowdin/ar.yml index 91d10268cfa..f5649e72c52 100644 --- a/modules/meeting/config/locales/crowdin/ar.yml +++ b/modules/meeting/config/locales/crowdin/ar.yml @@ -46,6 +46,7 @@ ar: start_date: "التاريخ" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "العنوان" author: "المؤلف" @@ -650,6 +651,11 @@ ar: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/az.yml b/modules/meeting/config/locales/crowdin/az.yml index 116e3ba1ccd..0080a00552b 100644 --- a/modules/meeting/config/locales/crowdin/az.yml +++ b/modules/meeting/config/locales/crowdin/az.yml @@ -42,6 +42,7 @@ az: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ az: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/be.yml b/modules/meeting/config/locales/crowdin/be.yml index 7a57c008f84..716ad1c6f06 100644 --- a/modules/meeting/config/locales/crowdin/be.yml +++ b/modules/meeting/config/locales/crowdin/be.yml @@ -44,6 +44,7 @@ be: start_date: "Дата" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -636,6 +637,11 @@ be: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/bg.yml b/modules/meeting/config/locales/crowdin/bg.yml index c5ce3462081..19becde207a 100644 --- a/modules/meeting/config/locales/crowdin/bg.yml +++ b/modules/meeting/config/locales/crowdin/bg.yml @@ -42,6 +42,7 @@ bg: start_date: "Дата" start_time: "Начален час" start_time_hour: "Начален час" + sharing: "Sharing" meeting_agenda_item: title: "Заглавие" author: "Автор" @@ -622,6 +623,11 @@ bg: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Тази среща вече не може да се редактира." text_meeting_not_present_anymore: "Тази среща е заличена. Моля, изберете друга среща." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ca.yml b/modules/meeting/config/locales/crowdin/ca.yml index 498d0dba6b6..bb9dfaa504e 100644 --- a/modules/meeting/config/locales/crowdin/ca.yml +++ b/modules/meeting/config/locales/crowdin/ca.yml @@ -42,6 +42,7 @@ ca: start_date: "Data" start_time: "Hora d'inici" start_time_hour: "Hora d'inici" + sharing: "Sharing" meeting_agenda_item: title: "Títol" author: "Autor/a" @@ -622,6 +623,11 @@ ca: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Aquesta reunió ja no es pot editar." text_meeting_not_present_anymore: "Aquesta reunió es va eliminar. Si us plau, selecciona una altra reunió." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ckb-IR.yml b/modules/meeting/config/locales/crowdin/ckb-IR.yml index c3db5261d4a..8bd93fc2b45 100644 --- a/modules/meeting/config/locales/crowdin/ckb-IR.yml +++ b/modules/meeting/config/locales/crowdin/ckb-IR.yml @@ -42,6 +42,7 @@ ckb-IR: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ ckb-IR: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/cs.yml b/modules/meeting/config/locales/crowdin/cs.yml index 62a64364176..9b7169ccdb0 100644 --- a/modules/meeting/config/locales/crowdin/cs.yml +++ b/modules/meeting/config/locales/crowdin/cs.yml @@ -44,6 +44,7 @@ cs: start_date: "Datum" start_time: "Čas zahájení" start_time_hour: "Čas zahájení" + sharing: "Sharing" meeting_agenda_item: title: "Název" author: "Autor" @@ -512,7 +513,7 @@ cs: notice_meeting_updated: "Tato stránka byla aktualizována někým jiným. Pro zobrazení změn znovu načtena." permission_create_meetings: "Vytvořit schůzku\n" permission_edit_meetings: "Upravit schůzku" - permission_delete_meetings: "Odstranit schůzky" + permission_delete_meetings: "Smazat schůzku" permission_view_meetings: "Zobrazit schůzky" permission_manage_agendas: "Správa zápisů" permission_manage_agendas_explanation: "Allows creating, editing and removing agenda items" @@ -636,6 +637,11 @@ cs: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Tato schůzka již není upravitelná." text_meeting_not_present_anymore: "Tato schůzka byla odstraněna. Vyberte prosím jinou schůzku." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/da.yml b/modules/meeting/config/locales/crowdin/da.yml index d478adfcc8d..21ebd450c36 100644 --- a/modules/meeting/config/locales/crowdin/da.yml +++ b/modules/meeting/config/locales/crowdin/da.yml @@ -42,6 +42,7 @@ da: start_date: "Dato" start_time: "Starttidspunkt" start_time_hour: "Starttidspunkt" + sharing: "Sharing" meeting_agenda_item: title: "Titel" author: "Forfatter" @@ -622,6 +623,11 @@ da: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/de.yml b/modules/meeting/config/locales/crowdin/de.yml index 74f623fe432..f47e26c306b 100644 --- a/modules/meeting/config/locales/crowdin/de.yml +++ b/modules/meeting/config/locales/crowdin/de.yml @@ -22,9 +22,9 @@ #English strings go here for Rails i18n de: plugin_openproject_meeting: - name: "OpenProject Besprechung" + name: "OpenProject Besprechungen" description: >- - Besprechungen für OpenProject + Dieses Modul fügt Funktionen zur Unterstützung von Besprechungen zu OpenProject hinzu. Sie können Besprechungen planen und dabei Teilnehmer aus demselben Projekt auswählen, die an der Besprechung teilnehmen sollen. Es kann eine Tagesordnung erstellt und an die Eingeladenen gesendet werden. Nach der Besprechung können die Teilnehmer ausgewählt und Protokolle auf der Grundlage der Tagesordnung erstellt werden. Schließlich kann das Protokoll an alle Teilnehmer und Eingeladenen gesendet werden. activerecord: attributes: meeting: @@ -42,6 +42,7 @@ de: start_date: "Datum" start_time: "Startzeit" start_time_hour: "Startzeit" + sharing: "Teilen" meeting_agenda_item: title: "Titel" author: "Autor" @@ -73,7 +74,7 @@ de: section_not_belong_to_meeting: "Die Sektion gehört nicht zur gleichen Besprechung." user_invalid: "ist kein gültiger Teilnehmer." recurring_meeting_interim_response: - not_an_occurrence: "ist keine gültige Uhrzeit für diese wiederkehrende Besprechung" + not_an_occurrence: "ist keine gültige Uhrzeit für dieses wiederkehrende Treffen" recurring_meeting: must_cover_existing_meetings: one: "Es gibt eine offene Besprechung in der Terminserie, die nicht durch den neuen Zeitplan abgedeckt ist. Passen Sie den Zeitplan an, um alle bestehenden Meetings einzuschließen." @@ -622,6 +623,11 @@ de: text_exit_draft_mode_dialog_subtitle: "Sie können nicht mehr zum Entwurfsmodus zurückkehren, sobald Sie die Besprechung eröffnen." text_exit_draft_mode_dialog_template_title: "Das erste Vorkommen dieser Terminserie öffnen?" text_exit_draft_mode_dialog_template_subtitle: "Sie können danach nicht mehr in den Entwurfsmodus zurückkehren." + label_meeting_template_sharing: "Teilen" + label_meeting_template_sharing_none: "Nur dieses Projekt" + label_meeting_template_sharing_descendants: "Unterprojekte" + label_meeting_template_sharing_system: "Alle Projekte" + text_meeting_template_sharing_description: "Diese Vorlage kann mit Unterprojekten oder anderen Projekten geteilt werden. Es werden nur die Tagesordnungspunkte und Anhänge kopiert." text_meeting_not_editable_anymore: "Diese Besprechung ist nicht mehr bearbeitbar." text_meeting_not_present_anymore: "Dieses Meeting wurde gelöscht. Bitte wählen Sie eine andere Besprechung." label_add_work_package_to_meeting_dialog_title: "Besprechung auswählen" diff --git a/modules/meeting/config/locales/crowdin/el.yml b/modules/meeting/config/locales/crowdin/el.yml index 35d2c1451a3..cceed748c90 100644 --- a/modules/meeting/config/locales/crowdin/el.yml +++ b/modules/meeting/config/locales/crowdin/el.yml @@ -42,6 +42,7 @@ el: start_date: "Ημερομηνία" start_time: "Ώρα έναρξης" start_time_hour: "Ώρα έναρξης" + sharing: "Sharing" meeting_agenda_item: title: "Τίτλος" author: "Συγγραφέας" @@ -622,6 +623,11 @@ el: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/eo.yml b/modules/meeting/config/locales/crowdin/eo.yml index df83d8804ef..9788d9f8586 100644 --- a/modules/meeting/config/locales/crowdin/eo.yml +++ b/modules/meeting/config/locales/crowdin/eo.yml @@ -42,6 +42,7 @@ eo: start_date: "Dato" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Titolo" author: "Aŭtoro" @@ -622,6 +623,11 @@ eo: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/es.yml b/modules/meeting/config/locales/crowdin/es.yml index 385a537fdad..f3445f3f7b1 100644 --- a/modules/meeting/config/locales/crowdin/es.yml +++ b/modules/meeting/config/locales/crowdin/es.yml @@ -42,6 +42,7 @@ es: start_date: "Fecha" start_time: "Hora de inicio" start_time_hour: "Hora de inicio" + sharing: "Compartiendo" meeting_agenda_item: title: "Título" author: "Autor" @@ -622,6 +623,11 @@ es: text_exit_draft_mode_dialog_subtitle: "Una vez programada una reunión, no puedes volver al modo Borrador." text_exit_draft_mode_dialog_template_title: "¿Abrir la primera repetición de esta serie de reuniones?" text_exit_draft_mode_dialog_template_subtitle: "No podrás volver al modo Borrador después de esto." + label_meeting_template_sharing: "Compartiendo" + label_meeting_template_sharing_none: "Solo este proyecto" + label_meeting_template_sharing_descendants: "Subproyectos" + label_meeting_template_sharing_system: "Todos los proyectos" + text_meeting_template_sharing_description: "Esta plantilla se puede compartir con subproyectos u otros proyectos en esta instancia. Solo se copiarán los elementos del orden del día y los archivos adjuntos." text_meeting_not_editable_anymore: "Esta reunión ya no es editable." text_meeting_not_present_anymore: "Esta reunión ha sido eliminada. Por favor, seleccione otra reunión." label_add_work_package_to_meeting_dialog_title: "Seleccionar reunión" diff --git a/modules/meeting/config/locales/crowdin/et.yml b/modules/meeting/config/locales/crowdin/et.yml index 13937747e2c..5a9aa0e2fd4 100644 --- a/modules/meeting/config/locales/crowdin/et.yml +++ b/modules/meeting/config/locales/crowdin/et.yml @@ -42,6 +42,7 @@ et: start_date: "Kuupäev" start_time: "Algusaeg" start_time_hour: "Algusaeg" + sharing: "Sharing" meeting_agenda_item: title: "Pealkiri" author: "Autor" @@ -622,6 +623,11 @@ et: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/eu.yml b/modules/meeting/config/locales/crowdin/eu.yml index 0ac3ac3193c..be9cf5b754a 100644 --- a/modules/meeting/config/locales/crowdin/eu.yml +++ b/modules/meeting/config/locales/crowdin/eu.yml @@ -42,6 +42,7 @@ eu: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ eu: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/fa.yml b/modules/meeting/config/locales/crowdin/fa.yml index 4ea7f27f7d7..7be582e9779 100644 --- a/modules/meeting/config/locales/crowdin/fa.yml +++ b/modules/meeting/config/locales/crowdin/fa.yml @@ -42,6 +42,7 @@ fa: start_date: "تاریخ" start_time: "زمان شروع" start_time_hour: "زمان شروع" + sharing: "Sharing" meeting_agenda_item: title: "عنوان" author: "نویسنده" @@ -622,6 +623,11 @@ fa: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/fi.yml b/modules/meeting/config/locales/crowdin/fi.yml index 747db12dc1e..33f17c593a8 100644 --- a/modules/meeting/config/locales/crowdin/fi.yml +++ b/modules/meeting/config/locales/crowdin/fi.yml @@ -42,6 +42,7 @@ fi: start_date: "Päivämäärä" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Otsikko" author: "Tekijä" @@ -622,6 +623,11 @@ fi: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/fil.yml b/modules/meeting/config/locales/crowdin/fil.yml index f2636920aa4..a94dda552b5 100644 --- a/modules/meeting/config/locales/crowdin/fil.yml +++ b/modules/meeting/config/locales/crowdin/fil.yml @@ -42,6 +42,7 @@ fil: start_date: "Petsa" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Pamagat" author: "May-akda" @@ -622,6 +623,11 @@ fil: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/fr.yml b/modules/meeting/config/locales/crowdin/fr.yml index e9c56b16e69..c296d40bbb4 100644 --- a/modules/meeting/config/locales/crowdin/fr.yml +++ b/modules/meeting/config/locales/crowdin/fr.yml @@ -42,6 +42,7 @@ fr: start_date: "Date" start_time: "Heure de début" start_time_hour: "Heure de début" + sharing: "Partage" meeting_agenda_item: title: "Titre" author: "Auteur" @@ -622,6 +623,11 @@ fr: text_exit_draft_mode_dialog_subtitle: "Vous ne pouvez pas revenir au mode brouillon une fois que vous avez planifié une réunion." text_exit_draft_mode_dialog_template_title: "Ouvrez la première occurrence de cette série de réunions ?" text_exit_draft_mode_dialog_template_subtitle: "Vous ne pouvez plus revenir au mode brouillon après cette opération." + label_meeting_template_sharing: "Partage" + label_meeting_template_sharing_none: "Seulement ce projet" + label_meeting_template_sharing_descendants: "Sous-projets" + label_meeting_template_sharing_system: "Tous les projets" + text_meeting_template_sharing_description: "Ce modèle peut être partagé avec des sous-projets ou d'autres projets dans cette instance. Seuls les points de l'ordre du jour et les pièces jointes seront copiés." text_meeting_not_editable_anymore: "Cette réunion n'est plus modifiable." text_meeting_not_present_anymore: "Cette réunion a été supprimée. Veuillez sélectionner une autre réunion." label_add_work_package_to_meeting_dialog_title: "Sélectionner la réunion" diff --git a/modules/meeting/config/locales/crowdin/he.yml b/modules/meeting/config/locales/crowdin/he.yml index 2a5c9078b99..77c964875b9 100644 --- a/modules/meeting/config/locales/crowdin/he.yml +++ b/modules/meeting/config/locales/crowdin/he.yml @@ -44,6 +44,7 @@ he: start_date: "תאריך" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "כותרת" author: "מחבר" @@ -636,6 +637,11 @@ he: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/hi.yml b/modules/meeting/config/locales/crowdin/hi.yml index 074ce5eb6b0..4ee59debe29 100644 --- a/modules/meeting/config/locales/crowdin/hi.yml +++ b/modules/meeting/config/locales/crowdin/hi.yml @@ -42,6 +42,7 @@ hi: start_date: "तिथि" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "शीर्षक" author: "Author" @@ -622,6 +623,11 @@ hi: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/hr.yml b/modules/meeting/config/locales/crowdin/hr.yml index 95c8d1347fd..8d4fc4b7392 100644 --- a/modules/meeting/config/locales/crowdin/hr.yml +++ b/modules/meeting/config/locales/crowdin/hr.yml @@ -43,6 +43,7 @@ hr: start_date: "Datum" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Naziv" author: "Autor" @@ -629,6 +630,11 @@ hr: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/hu.yml b/modules/meeting/config/locales/crowdin/hu.yml index 2a5fe60004c..5f01ebfc10e 100644 --- a/modules/meeting/config/locales/crowdin/hu.yml +++ b/modules/meeting/config/locales/crowdin/hu.yml @@ -42,6 +42,7 @@ hu: start_date: "dátum" start_time: "Kezdés" start_time_hour: "Kezdés" + sharing: "Sharing" meeting_agenda_item: title: "Cím" author: "Szerző" @@ -622,6 +623,11 @@ hu: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Ez a megbeszélés már nem szerkeszthető." text_meeting_not_present_anymore: "Ez a megbeszélés törlésre került. Kérjük, válasszon ki egy másik megbeszélést." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/id.yml b/modules/meeting/config/locales/crowdin/id.yml index 67981c91aee..2fcfd63659e 100644 --- a/modules/meeting/config/locales/crowdin/id.yml +++ b/modules/meeting/config/locales/crowdin/id.yml @@ -41,6 +41,7 @@ id: start_date: "Tanggal" start_time: "Waktu mulai" start_time_hour: "Waktu mulai" + sharing: "Sharing" meeting_agenda_item: title: "Judul" author: "Penulis" @@ -615,6 +616,11 @@ id: text_exit_draft_mode_dialog_subtitle: "Anda tidak dapat kembali ke mode draf setellah Anda menjadwalkan rapat." text_exit_draft_mode_dialog_template_title: "Buka acara yang pertama dalam seri rapat ini?" text_exit_draft_mode_dialog_template_subtitle: "Anda tidak dapat kembali ke mode draf setelah ini." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Rapat ini tidak dapat disunting kembali." text_meeting_not_present_anymore: "Rapat ini telah dihapus. Silakan pilih rapat yang lain." label_add_work_package_to_meeting_dialog_title: "Pilih rapat" diff --git a/modules/meeting/config/locales/crowdin/it.yml b/modules/meeting/config/locales/crowdin/it.yml index 53522226fc6..6c324b54246 100644 --- a/modules/meeting/config/locales/crowdin/it.yml +++ b/modules/meeting/config/locales/crowdin/it.yml @@ -42,6 +42,7 @@ it: start_date: "Data" start_time: "Ora di inizio" start_time_hour: "Ora di inizio" + sharing: "Condivisione" meeting_agenda_item: title: "Titolo" author: "Autore" @@ -622,6 +623,11 @@ it: text_exit_draft_mode_dialog_subtitle: "Non può tornare alla modalità bozza una volta programmata una riunione." text_exit_draft_mode_dialog_template_title: "Aprire la prima occorrenza di questa serie di riunioni?" text_exit_draft_mode_dialog_template_subtitle: "Non puoi tornare alla modalità bozza dopo averlo fatto." + label_meeting_template_sharing: "Condivisione" + label_meeting_template_sharing_none: "Solo questo progetto" + label_meeting_template_sharing_descendants: "Sottoprogetti" + label_meeting_template_sharing_system: "Tutti i progetti" + text_meeting_template_sharing_description: "In questa istanza, questo modello può essere condiviso con sottoprogetti o altri progetti. Verranno copiati solo gli elementi dell'ordine del giorno e gli allegati." text_meeting_not_editable_anymore: "Questa riunione non è più modificabile." text_meeting_not_present_anymore: "Questa riunione è stata eliminata. Seleziona un'altra riunione." label_add_work_package_to_meeting_dialog_title: "Seleziona riunione" diff --git a/modules/meeting/config/locales/crowdin/ja.yml b/modules/meeting/config/locales/crowdin/ja.yml index 1e454b00896..4c390388639 100644 --- a/modules/meeting/config/locales/crowdin/ja.yml +++ b/modules/meeting/config/locales/crowdin/ja.yml @@ -41,6 +41,7 @@ ja: start_date: "日付" start_time: "開始時間" start_time_hour: "開始時間" + sharing: "Sharing" meeting_agenda_item: title: "タイトル" author: "作成者" @@ -246,7 +247,7 @@ ja: summary_occurrence: "An occurrence of '%{title}' has been cancelled by %{actor}, or you have been removed as a participant" summary_series: "Meeting series '%{title}' has been cancelled by %{actor}, or you have been removed as a participant" summary: "'%{title}' has been cancelled by %{actor}, or you have been removed as a participant" - date_time: "予定日時" + date_time: "スケジュールされた日時" participant_added: header: "Meeting '%{title}' - Participant added" header_series: "Meeting series '%{title}' - Participant added" @@ -293,7 +294,7 @@ ja: title: "会議のキャンセル" heading: "この会議をキャンセルしますか?" confirmation_message_html: > - テンプレートにない会議情報は失われます。 続行しますか? + テンプレートにない会議情報は失われます。 続けますか? confirm_button: "発生をキャンセル" blankslate: title: "表示する会議がありません" @@ -485,7 +486,7 @@ ja: confirm_button: "この予定をキャンセル" end_series_dialog: title: "一連の会議を終了" - notice_successful_notification: "参加者全員にカレンダー更新の電子メールを送信" + notice_successful_notification: "すべての出席者にカレンダーの更新をメールしました" notice_meeting_template_created: "Template successfully created" notice_timezone_missing: タイムゾーンが設定されていない場合、%{zone} が使用されます。タイムゾーンを選択するには、ここをクリックしてください。 notice_meeting_updated: "このページは他の誰かによって更新されました。変更を表示するには再読み込みしてください。" @@ -573,7 +574,7 @@ ja: このバックログは、このワンタイム会議に固有のものです.アイテムをドラッグして追加または会議の議題から削除することができます. label_agenda_backlog_clear_title: "議題のバックログをクリアしますか?" text_agenda_backlog_clear_description: > - 現在アジェンダバックログにあるすべての項目を削除してもよろしいですか?このアクションは元に戻せません。 + 議題のバックログ内のすべての項目を削除してもよろしいですか?この操作は取り消せません。 label_series_backlog: "シリーズバックログ" text_series_backlog: > バックログはこのシリーズのすべての出現と共有されます。 項目をドラッグして、特定の会議から項目を追加または削除できます。 @@ -605,7 +606,7 @@ ja: text_meeting_closed_description: "この会議は終了しています。これ以上、議題項目の追加/削除はできません。" text_meeting_in_progress_description: "議題を変更したり、各項目のアウトカムを記録したり、参加者の出席を追跡することができます。 会議が完了すると、会議をクローズとしてマークしてロックできます。" text_meeting_open_dropdown_description: "既存の結果は残りますが、ユーザーは新しい結果を追加することはできません。" - text_meeting_in_progress_dropdown_description: "会議中に必要な情報や決定事項などの成果を文書化する。" + text_meeting_in_progress_dropdown_description: "会議中に取られた情報のニーズや意思決定などの成果を記録します。" text_meeting_closed_dropdown_description: "この会議は終了しました。これ以上、議題や結果を変更することはできません。" text_meeting_draft_banner: "現在下書きモードです。 会議の詳細を変更したり出席者を追加/削除したりしても,この会議はカレンダーの更新や招待状を送信しません。" text_onetime_meeting_template_banner: "You are currently editing a meeting template. You can use this template to create one-time meetings with a predefined agenda. Changes will not affect already-created meetings." @@ -615,6 +616,11 @@ ja: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "この会議はもう編集できません。" text_meeting_not_present_anymore: "この会議は削除されました。別の会議を選択してください。" label_add_work_package_to_meeting_dialog_title: "会議の選択" diff --git a/modules/meeting/config/locales/crowdin/ka.yml b/modules/meeting/config/locales/crowdin/ka.yml index 1fffeca46f5..bd2e6a5a181 100644 --- a/modules/meeting/config/locales/crowdin/ka.yml +++ b/modules/meeting/config/locales/crowdin/ka.yml @@ -42,6 +42,7 @@ ka: start_date: "თარიღი" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "სათაური" author: "ავტორი" @@ -622,6 +623,11 @@ ka: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/kk.yml b/modules/meeting/config/locales/crowdin/kk.yml index f30132835af..2a9a0513c55 100644 --- a/modules/meeting/config/locales/crowdin/kk.yml +++ b/modules/meeting/config/locales/crowdin/kk.yml @@ -42,6 +42,7 @@ kk: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ kk: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ko.yml b/modules/meeting/config/locales/crowdin/ko.yml index 98cd86be342..f624df287a7 100644 --- a/modules/meeting/config/locales/crowdin/ko.yml +++ b/modules/meeting/config/locales/crowdin/ko.yml @@ -41,6 +41,7 @@ ko: start_date: "날짜" start_time: "시작 시간" start_time_hour: "시작 시간" + sharing: "공유" meeting_agenda_item: title: "제목" author: "작성자" @@ -615,6 +616,11 @@ ko: text_exit_draft_mode_dialog_subtitle: "미팅을 예약한 후에는 초안 모드로 돌아갈 수 없습니다." text_exit_draft_mode_dialog_template_title: "이 미팅 시리즈의 첫 번째 항목을 열어보시겠습니까?" text_exit_draft_mode_dialog_template_subtitle: "이후에는 초안 모드로 돌아갈 수 없습니다." + label_meeting_template_sharing: "공유" + label_meeting_template_sharing_none: "이 프로젝트만" + label_meeting_template_sharing_descendants: "하위 프로젝트" + label_meeting_template_sharing_system: "모든 프로젝트" + text_meeting_template_sharing_description: "이 템플릿은 해당 인스턴스의 하위 프로젝트 또는 다른 프로젝트와 공유할 수 있습니다. 의제 항목과 첨부 파일만 복사됩니다." text_meeting_not_editable_anymore: "이 미팅은 더 이상 편집할 수 없습니다." text_meeting_not_present_anymore: "이 미팅은 삭제되었습니다. 다른 미팅을 선택해 주세요." label_add_work_package_to_meeting_dialog_title: "미팅 선택" diff --git a/modules/meeting/config/locales/crowdin/lt.yml b/modules/meeting/config/locales/crowdin/lt.yml index 525672df94f..91b3f801a5f 100644 --- a/modules/meeting/config/locales/crowdin/lt.yml +++ b/modules/meeting/config/locales/crowdin/lt.yml @@ -44,6 +44,7 @@ lt: start_date: "Data" start_time: "Pradžios laikas" start_time_hour: "Pradžios laikas" + sharing: "Sharing" meeting_agenda_item: title: "Pavadinimas" author: "Autorius" @@ -636,6 +637,11 @@ lt: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Šis susitikimas daugiau neberedaguojamas" text_meeting_not_present_anymore: "Šis susitikimas buvo ištrintas. Prašome parinkti kitą susitikimą." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/lv.yml b/modules/meeting/config/locales/crowdin/lv.yml index 217d983e044..5998f60280c 100644 --- a/modules/meeting/config/locales/crowdin/lv.yml +++ b/modules/meeting/config/locales/crowdin/lv.yml @@ -43,6 +43,7 @@ lv: start_date: "Datums" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Virsraksts" author: "Autors" @@ -629,6 +630,11 @@ lv: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/mn.yml b/modules/meeting/config/locales/crowdin/mn.yml index 0cad0db0661..c644fac93f1 100644 --- a/modules/meeting/config/locales/crowdin/mn.yml +++ b/modules/meeting/config/locales/crowdin/mn.yml @@ -42,6 +42,7 @@ mn: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ mn: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ms.yml b/modules/meeting/config/locales/crowdin/ms.yml index 0b532677465..66e80c49f9c 100644 --- a/modules/meeting/config/locales/crowdin/ms.yml +++ b/modules/meeting/config/locales/crowdin/ms.yml @@ -41,6 +41,7 @@ ms: start_date: "Tarikh" start_time: "Masa mula" start_time_hour: "Masa mula" + sharing: "Sharing" meeting_agenda_item: title: "Tajuk" author: "Pengarang" @@ -615,6 +616,11 @@ ms: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Mesyuarat ini tidak boleh diedit lagi." text_meeting_not_present_anymore: "Mesyuarat ini telah dipadamkan. Sila pilih mesyuarat lain." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ne.yml b/modules/meeting/config/locales/crowdin/ne.yml index 1ac65742bd4..bcc2c2aa0bc 100644 --- a/modules/meeting/config/locales/crowdin/ne.yml +++ b/modules/meeting/config/locales/crowdin/ne.yml @@ -42,6 +42,7 @@ ne: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ ne: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/nl.yml b/modules/meeting/config/locales/crowdin/nl.yml index eedbdaadb4d..75ee48f568c 100644 --- a/modules/meeting/config/locales/crowdin/nl.yml +++ b/modules/meeting/config/locales/crowdin/nl.yml @@ -42,6 +42,7 @@ nl: start_date: "Datum" start_time: "Starttijd" start_time_hour: "Starttijd" + sharing: "Sharing" meeting_agenda_item: title: "Titel" author: "Auteur" @@ -622,6 +623,11 @@ nl: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Deze vergadering kan niet meer bewerkt worden." text_meeting_not_present_anymore: "Deze vergadering is verwijderd. Selecteer een andere vergadering." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/no.yml b/modules/meeting/config/locales/crowdin/no.yml index a01f411dd63..315f1ff0db7 100644 --- a/modules/meeting/config/locales/crowdin/no.yml +++ b/modules/meeting/config/locales/crowdin/no.yml @@ -42,6 +42,7 @@ start_date: "Dato" start_time: "Starttid" start_time_hour: "Starttid" + sharing: "Sharing" meeting_agenda_item: title: "Tittel" author: "Forfatter" @@ -622,6 +623,11 @@ text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Dette møtet er ikke lenger redigerbart." text_meeting_not_present_anymore: "Dette møtet ble slettet. Velg et annet møte." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/pl.yml b/modules/meeting/config/locales/crowdin/pl.yml index 63058498157..9b1ae56c1df 100644 --- a/modules/meeting/config/locales/crowdin/pl.yml +++ b/modules/meeting/config/locales/crowdin/pl.yml @@ -44,6 +44,7 @@ pl: start_date: "Data" start_time: "Data rozpoczęcia" start_time_hour: "Godzina rozpoczęcia" + sharing: "Udostępnianie" meeting_agenda_item: title: "Tytuł" author: "Autor" @@ -636,6 +637,11 @@ pl: text_exit_draft_mode_dialog_subtitle: "Po zaplanowaniu spotkania nie można powrócić do trybu wersji roboczej." text_exit_draft_mode_dialog_template_title: "Otworzyć pierwsze wystąpienie z tej serii spotkań?" text_exit_draft_mode_dialog_template_subtitle: "Po tym nie możesz powrócić do trybu wersji roboczej." + label_meeting_template_sharing: "Udostępnianie" + label_meeting_template_sharing_none: "Tylko ten projekt" + label_meeting_template_sharing_descendants: "Podprojekty" + label_meeting_template_sharing_system: "Wszystkie projekty" + text_meeting_template_sharing_description: "Ten szablon można udostępniać podprojektom lub innym projektom w tym wystąpieniu. Skopiowane zostaną tylko pozycje planu spotkania i załączniki." text_meeting_not_editable_anymore: "Tego spotkania nie można już edytować." text_meeting_not_present_anymore: "To spotkanie zostało usunięte. Wybierz inne spotkanie." label_add_work_package_to_meeting_dialog_title: "Wybierz spotkanie" diff --git a/modules/meeting/config/locales/crowdin/pt-BR.yml b/modules/meeting/config/locales/crowdin/pt-BR.yml index f738c3ad94f..88f509605ac 100644 --- a/modules/meeting/config/locales/crowdin/pt-BR.yml +++ b/modules/meeting/config/locales/crowdin/pt-BR.yml @@ -42,6 +42,7 @@ pt-BR: start_date: "Data" start_time: "Hora de início" start_time_hour: "Hora de início" + sharing: "Compartilhamento" meeting_agenda_item: title: "Título" author: "Autor" @@ -622,6 +623,11 @@ pt-BR: text_exit_draft_mode_dialog_subtitle: "Não é possível voltar ao modo rascunho depois de agendar uma reunião." text_exit_draft_mode_dialog_template_title: "Abrir a primeira ocorrência desta série de reuniões?" text_exit_draft_mode_dialog_template_subtitle: "Você não pode retornar ao modo rascunho após isto." + label_meeting_template_sharing: "Compartilhamento" + label_meeting_template_sharing_none: "Apenas neste projeto" + label_meeting_template_sharing_descendants: "Subprojetos" + label_meeting_template_sharing_system: "Todos os projetos" + text_meeting_template_sharing_description: "Este modelo pode ser compartilhado com subprojetos ou outros projetos nesta instância. Apenas os itens de pauta e os anexos serão copiados." text_meeting_not_editable_anymore: "A reunião não pode mais ser editada." text_meeting_not_present_anymore: "Esta reunião foi excluída. Selecione outra reunião." label_add_work_package_to_meeting_dialog_title: "Selecionar reunião" diff --git a/modules/meeting/config/locales/crowdin/pt-PT.yml b/modules/meeting/config/locales/crowdin/pt-PT.yml index 4949f4cd24e..3ff2ab89a9d 100644 --- a/modules/meeting/config/locales/crowdin/pt-PT.yml +++ b/modules/meeting/config/locales/crowdin/pt-PT.yml @@ -42,6 +42,7 @@ pt-PT: start_date: "Data" start_time: "Hora de início" start_time_hour: "Hora de início" + sharing: "Partilhar" meeting_agenda_item: title: "Título" author: "Autor" @@ -622,6 +623,11 @@ pt-PT: text_exit_draft_mode_dialog_subtitle: "Não pode regressar ao modo de rascunho após agendar uma reunião." text_exit_draft_mode_dialog_template_title: "Abrir a primeira ocorrência desta série de reuniões?" text_exit_draft_mode_dialog_template_subtitle: "Depois disso, não pode voltar ao modo de rascunho." + label_meeting_template_sharing: "Partilhar" + label_meeting_template_sharing_none: "Apenas este projeto" + label_meeting_template_sharing_descendants: "Sub-projetos" + label_meeting_template_sharing_system: "Todos os projetos" + text_meeting_template_sharing_description: "Este modelo pode ser partilhado com sub-projetos ou outros projetos nesta instância. Só os elementos da agenda e os anexos serão copiados." text_meeting_not_editable_anymore: "Esta reunião não é mais editável." text_meeting_not_present_anymore: "Esta reunião foi excluída. Por favor, selecione outra reunião." label_add_work_package_to_meeting_dialog_title: "Selecionar reunião" diff --git a/modules/meeting/config/locales/crowdin/ro.yml b/modules/meeting/config/locales/crowdin/ro.yml index 827903fd42b..919502a6d40 100644 --- a/modules/meeting/config/locales/crowdin/ro.yml +++ b/modules/meeting/config/locales/crowdin/ro.yml @@ -43,6 +43,7 @@ ro: start_date: "Dată" start_time: "Dată început" start_time_hour: "Dată început" + sharing: "Sharing" meeting_agenda_item: title: "Titlu" author: "Autor" @@ -629,6 +630,11 @@ ro: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/ru.yml b/modules/meeting/config/locales/crowdin/ru.yml index 02cbc9c10de..3d54f2873fa 100644 --- a/modules/meeting/config/locales/crowdin/ru.yml +++ b/modules/meeting/config/locales/crowdin/ru.yml @@ -44,6 +44,7 @@ ru: start_date: "Дата" start_time: "Время начала" start_time_hour: "Время начала" + sharing: "Совместное использование" meeting_agenda_item: title: "Заголовок" author: "Автор" @@ -636,6 +637,11 @@ ru: text_exit_draft_mode_dialog_subtitle: "Вы не можете вернуться в режим черновика после того, как запланировали совещание." text_exit_draft_mode_dialog_template_title: "Открыть первое совещание из этой серии?" text_exit_draft_mode_dialog_template_subtitle: "После этого Вы не сможете вернуться в режим черновика." + label_meeting_template_sharing: "Совместное использование" + label_meeting_template_sharing_none: "Только этот проект" + label_meeting_template_sharing_descendants: "Подпроекты" + label_meeting_template_sharing_system: "Все проекты" + text_meeting_template_sharing_description: "Этот шаблон можно использовать совместно с подпроектами или другими проектами. Будут скопированы только пункты повестки дня и вложения." text_meeting_not_editable_anymore: "Это совещание больше нельзя редактировать." text_meeting_not_present_anymore: "Это совещание было удалено. Пожалуйста, выберите другое." label_add_work_package_to_meeting_dialog_title: "Выберите совещание" diff --git a/modules/meeting/config/locales/crowdin/rw.yml b/modules/meeting/config/locales/crowdin/rw.yml index d571d5898da..257865c0a66 100644 --- a/modules/meeting/config/locales/crowdin/rw.yml +++ b/modules/meeting/config/locales/crowdin/rw.yml @@ -42,6 +42,7 @@ rw: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ rw: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/si.yml b/modules/meeting/config/locales/crowdin/si.yml index 0a559a1c509..36d11e30472 100644 --- a/modules/meeting/config/locales/crowdin/si.yml +++ b/modules/meeting/config/locales/crowdin/si.yml @@ -42,6 +42,7 @@ si: start_date: "දිනය" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "මාතෘකාව" author: "කර්තෘ" @@ -622,6 +623,11 @@ si: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/sk.yml b/modules/meeting/config/locales/crowdin/sk.yml index 8627fc7c99a..25e8114fbb9 100644 --- a/modules/meeting/config/locales/crowdin/sk.yml +++ b/modules/meeting/config/locales/crowdin/sk.yml @@ -44,6 +44,7 @@ sk: start_date: "Dátum" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Nadpis" author: "Autor" @@ -636,6 +637,11 @@ sk: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/sl.yml b/modules/meeting/config/locales/crowdin/sl.yml index f1c13874f33..88cb32dc8a5 100644 --- a/modules/meeting/config/locales/crowdin/sl.yml +++ b/modules/meeting/config/locales/crowdin/sl.yml @@ -44,6 +44,7 @@ sl: start_date: "Datum" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Naslov" author: "Avtor" @@ -636,6 +637,11 @@ sl: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/sr.yml b/modules/meeting/config/locales/crowdin/sr.yml index cc001255629..86c904021be 100644 --- a/modules/meeting/config/locales/crowdin/sr.yml +++ b/modules/meeting/config/locales/crowdin/sr.yml @@ -43,6 +43,7 @@ sr: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -629,6 +630,11 @@ sr: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/sv.yml b/modules/meeting/config/locales/crowdin/sv.yml index 4ffa38c02b7..bb4ab06ea8b 100644 --- a/modules/meeting/config/locales/crowdin/sv.yml +++ b/modules/meeting/config/locales/crowdin/sv.yml @@ -42,6 +42,7 @@ sv: start_date: "Datum" start_time: "Starttid" start_time_hour: "Starttid" + sharing: "Sharing" meeting_agenda_item: title: "Titel" author: "Upphovsman" @@ -622,6 +623,11 @@ sv: text_exit_draft_mode_dialog_subtitle: "Du kan inte gå tillbaka till utkastet när du schemalägger ett möte." text_exit_draft_mode_dialog_template_title: "Öppna den första förekomsten av denna mötesserie?" text_exit_draft_mode_dialog_template_subtitle: "Du kan inte återgå till utkastläget efter detta." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Detta möte är inte längre redigerbart." text_meeting_not_present_anymore: "Detta möte har tagits bort. Vänligen välj ett annat möte." label_add_work_package_to_meeting_dialog_title: "Välj möte" diff --git a/modules/meeting/config/locales/crowdin/th.yml b/modules/meeting/config/locales/crowdin/th.yml index e6748e372cc..7e481e714e7 100644 --- a/modules/meeting/config/locales/crowdin/th.yml +++ b/modules/meeting/config/locales/crowdin/th.yml @@ -41,6 +41,7 @@ th: start_date: "วันที่" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "ชื่อเรื่อง" author: "ผู้เขียน" @@ -615,6 +616,11 @@ th: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/tr.yml b/modules/meeting/config/locales/crowdin/tr.yml index bc3c97dbf78..71a1418677a 100644 --- a/modules/meeting/config/locales/crowdin/tr.yml +++ b/modules/meeting/config/locales/crowdin/tr.yml @@ -42,6 +42,7 @@ tr: start_date: "Tarih" start_time: "Başlama tarihi" start_time_hour: "Başlama saati" + sharing: "Sharing" meeting_agenda_item: title: "Başlık" author: "Yazar" @@ -622,6 +623,11 @@ tr: text_exit_draft_mode_dialog_subtitle: "Bir toplantı planladıktan sonra taslak moduna geri dönemezsiniz." text_exit_draft_mode_dialog_template_title: "Bu toplantı serisinin ilkini açar mısınız?" text_exit_draft_mode_dialog_template_subtitle: "Bundan sonra taslak moduna geri dönemezsiniz." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Bu toplantıyı artık düzenleyemezsiniz." text_meeting_not_present_anymore: "Toplantı silinmiş. Başka bir toplantı seçin lütfen." label_add_work_package_to_meeting_dialog_title: "Toplantı seç" diff --git a/modules/meeting/config/locales/crowdin/uk.yml b/modules/meeting/config/locales/crowdin/uk.yml index fa8017c3968..3ab697eafbb 100644 --- a/modules/meeting/config/locales/crowdin/uk.yml +++ b/modules/meeting/config/locales/crowdin/uk.yml @@ -44,6 +44,7 @@ uk: start_date: "Дата" start_time: "Час початку" start_time_hour: "Час початку" + sharing: "Надання доступу" meeting_agenda_item: title: "Назва " author: "Автор" @@ -636,6 +637,11 @@ uk: text_exit_draft_mode_dialog_subtitle: "Запланувавши нараду, не можна повернутися в режим чернетки." text_exit_draft_mode_dialog_template_title: "Відкрити першу нараду цієї серії?" text_exit_draft_mode_dialog_template_subtitle: "Після цього не можна буде повернутися в режим чернетки." + label_meeting_template_sharing: "Надання доступу" + label_meeting_template_sharing_none: "Лише цей проєкт" + label_meeting_template_sharing_descendants: "Підпроєкти" + label_meeting_template_sharing_system: "Усі проєкти" + text_meeting_template_sharing_description: "Цим шаблоном можна поділитися з підпроєктами або іншими проєктами цього екземпляра. Буде скопійовано лише пункти порядку денного й вкладення." text_meeting_not_editable_anymore: "Цю нараду більше не можна редагувати." text_meeting_not_present_anymore: "Цю нараду видалено. Виберіть іншу нараду." label_add_work_package_to_meeting_dialog_title: "Вибрати нараду" diff --git a/modules/meeting/config/locales/crowdin/uz.yml b/modules/meeting/config/locales/crowdin/uz.yml index 4e1e7562442..274fb9613e5 100644 --- a/modules/meeting/config/locales/crowdin/uz.yml +++ b/modules/meeting/config/locales/crowdin/uz.yml @@ -42,6 +42,7 @@ uz: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -622,6 +623,11 @@ uz: text_exit_draft_mode_dialog_subtitle: "You cannot return to draft mode once you schedule a meeting." text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." label_add_work_package_to_meeting_dialog_title: "Select meeting" diff --git a/modules/meeting/config/locales/crowdin/vi.yml b/modules/meeting/config/locales/crowdin/vi.yml index f632f5f3599..19540bead09 100644 --- a/modules/meeting/config/locales/crowdin/vi.yml +++ b/modules/meeting/config/locales/crowdin/vi.yml @@ -41,6 +41,7 @@ vi: start_date: "ngày" start_time: "Thời gian bắt đầu" start_time_hour: "Thời gian bắt đầu" + sharing: "Sharing" meeting_agenda_item: title: "tiêu đề" author: "tác giả" @@ -615,6 +616,11 @@ vi: text_exit_draft_mode_dialog_subtitle: "Bạn không thể quay lại chế độ nháp sau khi lên lịch cuộc họp." text_exit_draft_mode_dialog_template_title: "Mở lần xuất hiện đầu tiên của chuỗi cuộc họp này?" text_exit_draft_mode_dialog_template_subtitle: "Bạn không thể quay lại chế độ nháp sau này." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "Cuộc họp này không thể chỉnh sửa được nữa." text_meeting_not_present_anymore: "Cuộc họp này đã bị xóa. Vui lòng chọn một cuộc họp khác." label_add_work_package_to_meeting_dialog_title: "Chọn cuộc họp" diff --git a/modules/meeting/config/locales/crowdin/zh-CN.yml b/modules/meeting/config/locales/crowdin/zh-CN.yml index 639016e481a..6ad4209c0e4 100644 --- a/modules/meeting/config/locales/crowdin/zh-CN.yml +++ b/modules/meeting/config/locales/crowdin/zh-CN.yml @@ -41,6 +41,7 @@ zh-CN: start_date: "日期" start_time: "开始时间" start_time_hour: "开始时间" + sharing: "共享中" meeting_agenda_item: title: "标题" author: "作者" @@ -615,6 +616,11 @@ zh-CN: text_exit_draft_mode_dialog_subtitle: "会议安排完成后,您将无法返回到草稿模式。" text_exit_draft_mode_dialog_template_title: "是否打开此会议系列的第一个会议?" text_exit_draft_mode_dialog_template_subtitle: "在此之后,您将无法返回到草稿模式。" + label_meeting_template_sharing: "共享中" + label_meeting_template_sharing_none: "仅此项目" + label_meeting_template_sharing_descendants: "子项目" + label_meeting_template_sharing_system: "所有项目" + text_meeting_template_sharing_description: "此模板可以与此实例中的子项目或其他项目共享。仅复制议程条目和附件。" text_meeting_not_editable_anymore: "本次会议已不可编辑。" text_meeting_not_present_anymore: "此次会议已被删除。请选择另一次会议。" label_add_work_package_to_meeting_dialog_title: "选择会议" diff --git a/modules/meeting/config/locales/crowdin/zh-TW.yml b/modules/meeting/config/locales/crowdin/zh-TW.yml index b26d49712c9..d922d064442 100644 --- a/modules/meeting/config/locales/crowdin/zh-TW.yml +++ b/modules/meeting/config/locales/crowdin/zh-TW.yml @@ -41,6 +41,7 @@ zh-TW: start_date: "日期" start_time: "開始時間" start_time_hour: "開始時間" + sharing: "Sharing" meeting_agenda_item: title: "標題" author: "會議發起者" @@ -615,6 +616,11 @@ zh-TW: text_exit_draft_mode_dialog_subtitle: "一旦排定會議,就無法返回草稿模式。" text_exit_draft_mode_dialog_template_title: "要開啟此會議系列的第一場會議嗎?" text_exit_draft_mode_dialog_template_subtitle: "之後您就無法回到草稿模式。" + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." text_meeting_not_editable_anymore: "本次會議已不可編輯。" text_meeting_not_present_anymore: "此次會議已被刪除。請選擇另一場會議。" label_add_work_package_to_meeting_dialog_title: "選擇會議" diff --git a/modules/meeting/config/locales/en.yml b/modules/meeting/config/locales/en.yml index ead5e53ff23..cb21d7eae97 100644 --- a/modules/meeting/config/locales/en.yml +++ b/modules/meeting/config/locales/en.yml @@ -53,6 +53,7 @@ en: start_date: "Date" start_time: "Start time" start_time_hour: "Start time" + sharing: "Sharing" meeting_agenda_item: title: "Title" author: "Author" @@ -693,6 +694,12 @@ en: text_exit_draft_mode_dialog_template_title: "Open the first occurrence of this meeting series?" text_exit_draft_mode_dialog_template_subtitle: "You cannot return to draft mode after this." + label_meeting_template_sharing: "Sharing" + label_meeting_template_sharing_none: "Only this project" + label_meeting_template_sharing_descendants: "Subprojects" + label_meeting_template_sharing_system: "All projects" + text_meeting_template_sharing_description: "This template can be shared with subprojects or other projects in this instance. Only the agenda items and attachments will be copied." + text_meeting_not_editable_anymore: "This meeting is not editable anymore." text_meeting_not_present_anymore: "This meeting was deleted. Please select another meeting." diff --git a/modules/meeting/config/routes.rb b/modules/meeting/config/routes.rb index 6679305e5f2..fecf73d4ee0 100644 --- a/modules/meeting/config/routes.rb +++ b/modules/meeting/config/routes.rb @@ -73,6 +73,7 @@ Rails.application.routes.draw do get :details_dialog put :update_details put :change_state + put :change_sharing post :notify get :history get :delete_dialog diff --git a/modules/meeting/db/migrate/20260302152255_add_sharing_to_meetings.rb b/modules/meeting/db/migrate/20260302152255_add_sharing_to_meetings.rb new file mode 100644 index 00000000000..96725388a20 --- /dev/null +++ b/modules/meeting/db/migrate/20260302152255_add_sharing_to_meetings.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 AddSharingToMeetings < ActiveRecord::Migration[8.1] + def change + add_column :meetings, :sharing, :string, null: true, default: nil + end +end diff --git a/modules/meeting/lib/open_project/meeting/engine.rb b/modules/meeting/lib/open_project/meeting/engine.rb index ea092bf9af0..da1df3a357e 100644 --- a/modules/meeting/lib/open_project/meeting/engine.rb +++ b/modules/meeting/lib/open_project/meeting/engine.rb @@ -65,7 +65,7 @@ module OpenProject::Meeting contract_actions: { meetings: %i[create] } permission :edit_meetings, { - meetings: %i[edit cancel_edit update update_title change_state toggle_notifications_dialog + meetings: %i[edit cancel_edit update update_title change_state change_sharing toggle_notifications_dialog details_dialog update_details toggle_notifications exit_draft_mode_dialog exit_draft_mode], recurring_meetings: %i[edit cancel_edit update update_title details_dialog update_details notify end_series end_series_dialog], diff --git a/modules/meeting/spec/contracts/meetings/create_contract_spec.rb b/modules/meeting/spec/contracts/meetings/create_contract_spec.rb index be7a26635ae..4858a65b5ae 100644 --- a/modules/meeting/spec/contracts/meetings/create_contract_spec.rb +++ b/modules/meeting/spec/contracts/meetings/create_contract_spec.rb @@ -44,6 +44,18 @@ RSpec.describe Meetings::CreateContract do end it_behaves_like "contract is valid" + + context "when setting sharing on a non-template" do + let(:meeting) { build(:meeting, project:, sharing: :none) } + + it_behaves_like "contract is invalid", sharing: %i[present not_allowed] + end + + context "when setting sharing on a onetime template" do + let(:meeting) { build(:onetime_template, project:, sharing: :none) } + + it_behaves_like "contract is valid" + end end context "without permission" do diff --git a/modules/meeting/spec/contracts/meetings/update_contract_spec.rb b/modules/meeting/spec/contracts/meetings/update_contract_spec.rb index 29d95655bfd..c8aef61331d 100644 --- a/modules/meeting/spec/contracts/meetings/update_contract_spec.rb +++ b/modules/meeting/spec/contracts/meetings/update_contract_spec.rb @@ -45,6 +45,20 @@ RSpec.describe Meetings::UpdateContract do it_behaves_like "contract is valid" + context "when setting sharing on a non-template" do + let(:meeting) { create(:meeting, project:, sharing: nil) } + + before { meeting.sharing = "none" } + + it_behaves_like "contract is invalid", sharing: %i[present not_allowed] + end + + context "when setting sharing on a onetime template" do + let(:meeting) { create(:onetime_template, project:, sharing: :none) } + + it_behaves_like "contract is valid" + end + context "when lock_version is changed" do before do allow(meeting).to receive(:lock_version_changed?).and_return(true) diff --git a/modules/meeting/spec/factories/meeting_factory.rb b/modules/meeting/spec/factories/meeting_factory.rb index 1711cc37caa..9aabde5eda9 100644 --- a/modules/meeting/spec/factories/meeting_factory.rb +++ b/modules/meeting/spec/factories/meeting_factory.rb @@ -67,6 +67,7 @@ FactoryBot.define do meeting.sequence(:title) { |n| "Onetime template #{n}" } template { true } recurring_meeting { nil } + sharing { :none } end end end diff --git a/modules/meeting/spec/features/meeting_backlogs_spec.rb b/modules/meeting/spec/features/meeting_backlogs_spec.rb index 88e22a76eea..be53398bb00 100644 --- a/modules/meeting/spec/features/meeting_backlogs_spec.rb +++ b/modules/meeting/spec/features/meeting_backlogs_spec.rb @@ -247,14 +247,6 @@ RSpec.describe "Meeting Backlogs", :js do end describe "for meeting series" do - before_all do - travel_to(Date.new(2024, 12, 1)) - end - - after(:all) do # rubocop:disable RSpec/BeforeAfterAll - travel_back - end - shared_let(:recurring_meeting) do create :recurring_meeting, project:, @@ -276,6 +268,10 @@ RSpec.describe "Meeting Backlogs", :js do RecurringMeetings::InitNextOccurrenceJob.perform_now(recurring_meeting, next_occurrence_time) end + after do + travel_back + end + describe "backlog visibility" do context "when the meeting is 'open'" do it "is expanded" do diff --git a/modules/meeting/spec/features/meeting_templates/template_sharing_spec.rb b/modules/meeting/spec/features/meeting_templates/template_sharing_spec.rb new file mode 100644 index 00000000000..8442ed84632 --- /dev/null +++ b/modules/meeting/spec/features/meeting_templates/template_sharing_spec.rb @@ -0,0 +1,159 @@ +# 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 "spec_helper" + +require_relative "../../support/pages/meetings/show" +require_relative "../../support/pages/meetings/index" + +RSpec.describe "Template sharing", :js do + shared_let(:project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:template) { create(:onetime_template, project:, title: "My template", sharing: :none) } + + let(:show_page) { Pages::Meetings::Show.new(template) } + + context "as user with edit_meetings permission" do + shared_let(:editor) do + create(:user, member_with_permissions: { project => %i[view_meetings edit_meetings] }) + end + + before do + login_as editor + show_page.visit! + end + + it "shows the sharing section in the sidebar" do + expect(page).to have_text(I18n.t(:label_meeting_template_sharing)) + expect(page).to have_text(I18n.t(:label_meeting_template_sharing_none)) + end + + it "can change sharing to subprojects" do + within("#meetings-side-panel-sharing-component") do + click_button I18n.t(:label_meeting_template_sharing_none) + click_link_or_button I18n.t(:label_meeting_template_sharing_descendants) + end + + wait_for_network_idle + + expect(page).to have_text(I18n.t(:label_meeting_template_sharing_descendants)) + expect(template.reload.sharing).to eq("descendants") + end + + it "can change sharing to all projects" do + within("#meetings-side-panel-sharing-component") do + click_button I18n.t(:label_meeting_template_sharing_none) + click_link_or_button I18n.t(:label_meeting_template_sharing_system) + end + + wait_for_network_idle + + expect(page).to have_text(I18n.t(:label_meeting_template_sharing_system)) + expect(template.reload.sharing).to eq("system") + end + + it "can change sharing back to only this project" do + template.update!(sharing: :descendants) + show_page.visit! + + within("#meetings-side-panel-sharing-component") do + click_button I18n.t(:label_meeting_template_sharing_descendants) + click_link_or_button I18n.t(:label_meeting_template_sharing_none) + end + + wait_for_network_idle + + expect(page).to have_text(I18n.t(:label_meeting_template_sharing_none)) + expect(template.reload.sharing).to eq("none") + end + end + + context "as user with view_meetings only" do + shared_let(:viewer) do + create(:user, member_with_permissions: { project => %i[view_meetings] }) + end + + before do + login_as viewer + show_page.visit! + end + + it "does not show the sharing section" do + expect(page).to have_no_css("#meetings-side-panel-sharing-component") + end + end + + # Happy path + context "when sharing level is changed", with_ee: [:meeting_templates] do + shared_let(:child_project) { create(:project, enabled_module_names: %i[meetings], parent: project) } + shared_let(:user) do + create(:user, member_with_permissions: { + project => %i[view_meetings edit_meetings], + child_project => %i[view_meetings create_meetings] + }) + end + + let(:child_meetings_page) { Pages::Meetings::Index.new(project: child_project) } + + before { login_as user } + + it "toggles visibility in child project's new meeting form" do + # Template not visible before sharing change + child_meetings_page.visit! + child_meetings_page.click_on "add-meeting-button" + child_meetings_page.click_on "One-time" + + within_dialog "New one-time meeting" do + expect(page).to have_no_css('[data-test-selector="template_id"]') + end + + # Change sharing to descendants + visit project_meeting_path(project, template) + + within("#meetings-side-panel-sharing-component") do + click_button I18n.t(:label_meeting_template_sharing_none) + click_link_or_button I18n.t(:label_meeting_template_sharing_descendants) + end + + wait_for_network_idle + + expect(template.reload.sharing).to eq("descendants") + + # Template is now visible in child project form + child_meetings_page.visit! + child_meetings_page.click_on "add-meeting-button" + child_meetings_page.click_on "One-time" + + within_dialog "New one-time meeting" do + find('[data-test-selector="template_id"]').click + expect(page).to have_text(template.title) + end + end + end +end diff --git a/modules/meeting/spec/features/meetings_index_spec.rb b/modules/meeting/spec/features/meetings_index_spec.rb index fae6aaaa574..88c1b3d81a6 100644 --- a/modules/meeting/spec/features/meetings_index_spec.rb +++ b/modules/meeting/spec/features/meetings_index_spec.rb @@ -32,6 +32,12 @@ require "spec_helper" require_relative "../support/pages/meetings/index" RSpec.describe "Meetings", "Index", :js do + shared_let(:business_day_at_noon) { Time.zone.local(2025, 1, 8, 12, 0, 0) } + + after do + travel_back + end + # The order the Projects are created in is important. By naming `project` alphanumerically # after `other_project`, we can ensure that subsequent specs that assert sorting is # correct for the right reasons (sorting by Project name and not id) @@ -55,14 +61,14 @@ RSpec.describe "Meetings", "Index", :js do :author_participates, project:, title: "Awesome meeting today!", - start_time: Time.current) + start_time: business_day_at_noon - 5.minutes) end shared_let(:tomorrows_meeting) do create(:meeting, :author_participates, project:, title: "Awesome meeting tomorrow!", - start_time: 1.day.from_now, + start_time: business_day_at_noon + 1.day, duration: 2.0, location: "no-protocol.com") end @@ -71,7 +77,7 @@ RSpec.describe "Meetings", "Index", :js do :author_participates, project:, title: "Boring meeting without a location!", - start_time: 1.day.from_now, + start_time: business_day_at_noon + 1.day + 5.minutes, location: "") end shared_let(:meeting_with_malicious_location) do @@ -79,7 +85,7 @@ RSpec.describe "Meetings", "Index", :js do :author_participates, project:, title: "Sneaky meeting!", - start_time: 1.day.from_now, + start_time: business_day_at_noon + 1.day + 10.minutes, location: "") end shared_let(:yesterdays_meeting) do @@ -87,7 +93,7 @@ RSpec.describe "Meetings", "Index", :js do :author_participates, project:, title: "Awesome meeting yesterday!", - start_time: 1.day.ago) + start_time: business_day_at_noon - 1.day) end shared_let(:other_project_meeting) do @@ -95,7 +101,7 @@ RSpec.describe "Meetings", "Index", :js do :author_participates, project: other_project, title: "Awesome other project meeting!", - start_time: 2.days.from_now, + start_time: business_day_at_noon + 2.days, duration: 2.0, location: "not-a-url") end @@ -104,7 +110,7 @@ RSpec.describe "Meetings", "Index", :js do :author_participates, project:, title: "Awesome ongoing meeting!", - start_time: 30.minutes.ago) + start_time: business_day_at_noon - 30.minutes) end def setup_meeting_involvement @@ -120,6 +126,7 @@ RSpec.describe "Meetings", "Index", :js do end before do + travel_to(business_day_at_noon) login_as user end diff --git a/modules/meeting/spec/features/move_to_section_spec.rb b/modules/meeting/spec/features/move_to_section_spec.rb index df2317e5133..64690b0f88a 100644 --- a/modules/meeting/spec/features/move_to_section_spec.rb +++ b/modules/meeting/spec/features/move_to_section_spec.rb @@ -52,6 +52,10 @@ RSpec.describe "Move agenda items to section", :js do login_as current_user end + after do + travel_back + end + describe "for one-time meetings" do shared_let(:meeting) do create :meeting, diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb index 81287ee636c..e91d16f84e6 100644 --- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb +++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_create_spec.rb @@ -74,6 +74,10 @@ RSpec.describe "Recurring meetings creation", travel_to(Date.new(2024, 12, 1)) end + after do + travel_back + end + context "with a user with permissions" do it "can create a recurring meeting" do login_as current_user diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb index a95a70bc171..b91732cfafb 100644 --- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb +++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_crud_spec.rb @@ -38,14 +38,6 @@ RSpec.describe "Recurring meetings CRUD", :js do include Components::Autocompleter::NgSelectAutocompleteHelpers - before_all do - travel_to(Date.new(2024, 12, 1)) - end - - after(:all) do # rubocop:disable RSpec/BeforeAfterAll - travel_back - end - shared_let(:project) { create(:project, enabled_module_names: %w[meetings]) } shared_let(:user) do create :user, @@ -85,6 +77,10 @@ RSpec.describe "Recurring meetings CRUD", RecurringMeetings::InitNextOccurrenceJob.perform_now(meeting, meeting.first_occurrence.to_time) end + after do + travel_back + end + it "can delete a recurring meeting from the show page and return to the index page" do show_page.visit! diff --git a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb index 6f03deb0517..010ae29a69b 100644 --- a/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb +++ b/modules/meeting/spec/features/recurring_meetings/recurring_meeting_global_crud_spec.rb @@ -37,14 +37,6 @@ require_relative "../../support/pages/meetings/index" RSpec.describe "Recurring meetings global CRUD", :js do include Components::Autocompleter::NgSelectAutocompleteHelpers - before_all do - travel_to(Date.new(2024, 12, 1)) - end - - after(:all) do # rubocop:disable RSpec/BeforeAfterAll - travel_back - end - shared_let(:project) { create(:project, enabled_module_names: %w[meetings]) } shared_let(:user) do create :user, @@ -84,6 +76,10 @@ RSpec.describe "Recurring meetings global CRUD", :js do RecurringMeetings::InitNextOccurrenceJob.perform_now(meeting, meeting.first_occurrence.to_time) end + after do + travel_back + end + it "can delete a recurring meeting from the show page and return to the index page" do show_page.visit! diff --git a/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb b/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb index 5bb68fe2392..77eb0ccf0f8 100644 --- a/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb +++ b/modules/meeting/spec/features/structured_meetings/structured_meeting_update_flash_spec.rb @@ -193,14 +193,6 @@ RSpec.describe "Meetings CRUD", end context "for meeting series, across the same occurrence" do - before_all do - travel_to(Date.new(2024, 12, 1)) - end - - after(:all) do # rubocop:disable RSpec/BeforeAfterAll - travel_back - end - let(:recurring_meeting) do create :recurring_meeting, project:, @@ -222,6 +214,10 @@ RSpec.describe "Meetings CRUD", RecurringMeetings::InitNextOccurrenceJob.perform_now(recurring_meeting, first_occurrence_time) end + after do + travel_back + end + it_behaves_like "no flash appears when interacting with backlog in multiple windows" end diff --git a/modules/meeting/spec/models/meeting_spec.rb b/modules/meeting/spec/models/meeting_spec.rb index 50e3395aff5..2e3b34d9222 100644 --- a/modules/meeting/spec/models/meeting_spec.rb +++ b/modules/meeting/spec/models/meeting_spec.rb @@ -1,4 +1,5 @@ # frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH @@ -189,4 +190,84 @@ RSpec.describe Meeting do end end end + + describe ".templates_visible_in_project" do + shared_let(:ancestor_project) { create(:project) } + shared_let(:current_project) { create(:project, parent: ancestor_project) } + shared_let(:descendant_project) { create(:project, parent: current_project) } + shared_let(:unrelated_project) { create(:project) } + + shared_let(:user) { create(:user, member_with_permissions: { current_project => [:view_meetings] }) } + + subject { described_class.templates_visible_in_project(current_project, user) } + + context "with templates in the same project" do + shared_let(:template_none) { create(:onetime_template, project: current_project, sharing: :none) } + shared_let(:template_descendants) { create(:onetime_template, project: current_project, sharing: :descendants) } + shared_let(:template_system) { create(:onetime_template, project: current_project, sharing: :system) } + + it { expect(subject).to include(template_none) } + it { expect(subject).to include(template_descendants) } + it { expect(subject).to include(template_system) } + end + + context "with templates in an unrelated project" do + shared_let(:template_none) { create(:onetime_template, project: unrelated_project, sharing: :none) } + shared_let(:template_descendants) { create(:onetime_template, project: unrelated_project, sharing: :descendants) } + shared_let(:template_system) { create(:onetime_template, project: unrelated_project, sharing: :system) } + + it { expect(subject).not_to include(template_none) } + it { expect(subject).not_to include(template_descendants) } + it { expect(subject).to include(template_system) } + end + + context "with templates in a descendant project" do + shared_let(:template_none) { create(:onetime_template, project: descendant_project, sharing: :none) } + shared_let(:template_descendants) { create(:onetime_template, project: descendant_project, sharing: :descendants) } + shared_let(:template_system) { create(:onetime_template, project: descendant_project, sharing: :system) } + + it { expect(subject).not_to include(template_none) } + it { expect(subject).not_to include(template_descendants) } + it { expect(subject).to include(template_system) } + end + + context "with templates in an ancestor project" do + shared_let(:template_none) { create(:onetime_template, project: ancestor_project, sharing: :none) } + shared_let(:template_descendants) { create(:onetime_template, project: ancestor_project, sharing: :descendants) } + shared_let(:template_system) { create(:onetime_template, project: ancestor_project, sharing: :system) } + + it { expect(subject).not_to include(template_none) } + it { expect(subject).to include(template_descendants) } + it { expect(subject).to include(template_system) } + end + end + + describe "sharing" do + context "for a regular meeting" do + let(:meeting) { build(:meeting, project:, sharing: :none) } + + it "is invalid" do + expect(meeting).not_to be_valid + expect(meeting.errors[:sharing]).to be_present + end + end + + context "for a series template" do + let(:recurring) { create(:recurring_meeting, project:) } + let(:meeting) { build(:meeting_template, sharing: :none, recurring_meeting: recurring) } + + it "is invalid" do + expect(meeting).not_to be_valid + expect(meeting.errors[:sharing]).to be_present + end + end + + context "for an onetime template" do + let(:meeting) { build(:onetime_template, project:, sharing: :none) } + + it "is valid" do + expect(meeting).to be_valid + end + end + end end diff --git a/modules/meeting/spec/requests/meeting_templates_spec.rb b/modules/meeting/spec/requests/meeting_templates_spec.rb index 10bbb49baed..0c0c9b1cc75 100644 --- a/modules/meeting/spec/requests/meeting_templates_spec.rb +++ b/modules/meeting/spec/requests/meeting_templates_spec.rb @@ -204,4 +204,74 @@ RSpec.describe "Meeting templates requests", end end end + + describe "GET /projects/:id/meetings/templates (project templates index page)" do + shared_let(:ancestor_project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:current_project) { create(:project, enabled_module_names: %i[meetings], parent: ancestor_project) } + shared_let(:descendant_project) { create(:project, enabled_module_names: %i[meetings], parent: current_project) } + shared_let(:unrelated_project) { create(:project, enabled_module_names: %i[meetings]) } + + shared_let(:user) do + create(:user, member_with_permissions: { current_project => %i[view_meetings] }) + end + + shared_let(:own_template) { create(:onetime_template, project: current_project, title: "Own template") } + shared_let(:ancestor_none_template) do + create(:onetime_template, project: ancestor_project, sharing: :none, title: "Ancestor none") + end + shared_let(:ancestor_descendants_template) do + create(:onetime_template, project: ancestor_project, sharing: :descendants, title: "Ancestor descendants") + end + shared_let(:descendant_template) do + create(:onetime_template, project: descendant_project, sharing: :descendants, title: "Descendant template") + end + shared_let(:system_template) do + create(:onetime_template, project: unrelated_project, sharing: :system, title: "System template") + end + + before { login_as user } + + it "shows only templates belonging to the project, regardless of sharing level" do + get templates_project_meetings_path(current_project) + + expect(response.body).to include("Own template") + + expect(response.body).not_to include("Ancestor none") + expect(response.body).not_to include("Ancestor descendants") + expect(response.body).not_to include("Descendant template") + expect(response.body).not_to include("System template") + end + end + + describe "GET /meetings/templates (global templates index page)" do + shared_let(:accessible_project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:inaccessible_project) { create(:project, enabled_module_names: %i[meetings]) } + + shared_let(:user) do + create(:user, member_with_permissions: { accessible_project => %i[view_meetings] }) + end + + shared_let(:own_template) { create(:onetime_template, project: accessible_project, title: "Own template") } + shared_let(:inaccessible_none_template) do + create(:onetime_template, project: inaccessible_project, sharing: :none, title: "Inaccessible none") + end + shared_let(:inaccessible_descendants_template) do + create(:onetime_template, project: inaccessible_project, sharing: :descendants, title: "Inaccessible descendants") + end + shared_let(:system_template) do + create(:onetime_template, project: inaccessible_project, sharing: :system, title: "System template") + end + + before { login_as user } + + it "shows system templates and templates from accessible projects" do + get templates_meetings_path + + expect(response.body).to include("Own template") + expect(response.body).to include("System template") + + expect(response.body).not_to include("Inaccessible none") + expect(response.body).not_to include("Inaccessible descendants") + end + end end diff --git a/modules/meeting/spec/requests/meetings_spec.rb b/modules/meeting/spec/requests/meetings_spec.rb index 86c060c4946..38b95110020 100644 --- a/modules/meeting/spec/requests/meetings_spec.rb +++ b/modules/meeting/spec/requests/meetings_spec.rb @@ -201,4 +201,50 @@ RSpec.describe "Meeting requests", end end end + + describe "GET new_dialog - template selector visibility", with_ee: [:meeting_templates] do + shared_let(:ancestor_project) { create(:project, enabled_module_names: %i[meetings]) } + shared_let(:current_project) { create(:project, enabled_module_names: %i[meetings], parent: ancestor_project) } + shared_let(:descendant_project) { create(:project, enabled_module_names: %i[meetings], parent: current_project) } + shared_let(:unrelated_project) { create(:project, enabled_module_names: %i[meetings]) } + + shared_let(:user) do + create(:user, member_with_permissions: { current_project => %i[view_meetings create_meetings] }) + end + + shared_let(:own_template) { create(:onetime_template, project: current_project, title: "Own template") } + shared_let(:ancestor_none_template) do + create(:onetime_template, project: ancestor_project, sharing: :none, title: "Ancestor none") + end + shared_let(:ancestor_descendants_template) do + create(:onetime_template, project: ancestor_project, sharing: :descendants, title: "Ancestor descendants") + end + shared_let(:descendant_none_template) do + create(:onetime_template, project: descendant_project, sharing: :none, title: "Descendant none") + end + shared_let(:descendant_descendants_template) do + create(:onetime_template, project: descendant_project, sharing: :descendants, title: "Descendant descendants") + end + shared_let(:unrelated_none_template) do + create(:onetime_template, project: unrelated_project, sharing: :none, title: "Unrelated none") + end + shared_let(:system_template) do + create(:onetime_template, project: unrelated_project, sharing: :system, title: "System template") + end + + before { login_as user } + + it "shows own and those shared with descendants and all projects" do + get new_dialog_project_meetings_path(current_project), as: :turbo_stream + + expect(response.body).to include("Own template") + expect(response.body).to include("Ancestor descendants") + expect(response.body).to include("System template") + + expect(response.body).not_to include("Ancestor none") + expect(response.body).not_to include("Descendant none") + expect(response.body).not_to include("Descendant descendants") + expect(response.body).not_to include("Unrelated none") + end + end end diff --git a/modules/meeting/spec/services/meetings/create_service_integration_spec.rb b/modules/meeting/spec/services/meetings/create_service_integration_spec.rb index 26dc2e991c4..508e3b60954 100644 --- a/modules/meeting/spec/services/meetings/create_service_integration_spec.rb +++ b/modules/meeting/spec/services/meetings/create_service_integration_spec.rb @@ -109,4 +109,22 @@ RSpec.describe Meetings::CreateService, "integration", type: :model do end end end + + describe "sharing" do + context "when creating a regular meeting" do + it "does not set sharing" do + expect(subject).to be_success + expect(subject.result.sharing).to be_nil + end + end + + context "when creating an onetime template" do + let(:default_params) { { project:, title: "My template", template: true } } + + it "sets sharing to none by default" do + expect(subject).to be_success + expect(subject.result.sharing).to eq("none") + end + end + end end diff --git a/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb b/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb index d9707c8db01..587b4d75100 100644 --- a/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb +++ b/modules/meeting/spec/services/recurring_meetings/create_service_integration_spec.rb @@ -35,6 +35,7 @@ RSpec.describe RecurringMeetings::CreateService, "integration", type: :model do shared_let(:user) do create(:user, member_with_permissions: { project => %i(view_meetings create_meetings) }) end + let(:business_day_at_noon) { Time.zone.parse("2025-01-08T12:00:00Z") } let(:instance) { described_class.new(user:) } let(:service_result) { subject } let(:series) { service_result.result } @@ -42,6 +43,14 @@ RSpec.describe RecurringMeetings::CreateService, "integration", type: :model do subject { instance.call(**params) } + before do + travel_to(business_day_at_noon) + end + + after do + travel_back + end + shared_examples "creates the series" do it "creates the series and template" do expect(service_result).to be_success diff --git a/modules/openid_connect/app/components/openid_connect/providers/side_panel/information_component.html.erb b/modules/openid_connect/app/components/openid_connect/providers/side_panel/information_component.html.erb index 0246817d18b..c22de3556b2 100644 --- a/modules/openid_connect/app/components/openid_connect/providers/side_panel/information_component.html.erb +++ b/modules/openid_connect/app/components/openid_connect/providers/side_panel/information_component.html.erb @@ -9,7 +9,7 @@ end collection.with_component( - OpPrimer::CopyToClipboardComponent.new(provider.slug, scheme: :input) + OpPrimer::CopyToClipboardComponent.new(provider.slug, scheme: :value) ) collection.with_component(Primer::Beta::Heading.new(tag: :h5, mt: 4, mb: 1)) do @@ -17,7 +17,7 @@ end collection.with_component( - OpPrimer::CopyToClipboardComponent.new(provider.callback_url, scheme: :input) + OpPrimer::CopyToClipboardComponent.new(provider.callback_url, scheme: :value) ) collection.with_component(Primer::Beta::Heading.new(tag: :h5, mt: 4, mb: 1)) do @@ -25,7 +25,7 @@ end collection.with_component( - OpPrimer::CopyToClipboardComponent.new(provider.backchannel_logout_url, scheme: :input) + OpPrimer::CopyToClipboardComponent.new(provider.backchannel_logout_url, scheme: :value) ) end end diff --git a/modules/openid_connect/app/services/openid_connect/user_tokens/token_request.rb b/modules/openid_connect/app/services/openid_connect/user_tokens/token_request.rb index f774185c546..3d43de64ec9 100644 --- a/modules/openid_connect/app/services/openid_connect/user_tokens/token_request.rb +++ b/modules/openid_connect/app/services/openid_connect/user_tokens/token_request.rb @@ -78,7 +78,7 @@ module OpenIDConnect # Client ID and Client Secret must be form-encoded. Otherwise characters such as colon (:) # would not be allowed in the Client ID, since HTTP Basic Auth does not support it # as per https://datatracker.ietf.org/doc/html/rfc7617#section-2 - OpenProject.httpx.basic_auth(CGI.escape(provider.client_id), CGI.escape(provider.client_secret)) + OpenProject.httpx.plugin(:basic_auth).basic_auth(CGI.escape(provider.client_id), CGI.escape(provider.client_secret)) end end end diff --git a/modules/overviews/spec/migrations/rename_manage_overview_to_manage_dashboards_spec.rb b/modules/overviews/spec/migrations/rename_manage_overview_to_manage_dashboards_spec.rb new file mode 100644 index 00000000000..2aff50081ef --- /dev/null +++ b/modules/overviews/spec/migrations/rename_manage_overview_to_manage_dashboards_spec.rb @@ -0,0 +1,67 @@ +# 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 "spec_helper" +require Rails.root.join("modules/overviews/db/migrate/20250910085916_rename_manage_overview_to_manage_dashboards") + +RSpec.describe RenameManageOverviewToManageDashboards, type: :model do + subject(:migrate) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } } + + let!(:empty_role) { create(:project_role) } + let!(:role_with_permission) do + create(:project_role, permissions: %i[manage_overview], add_public_permissions: false) + end + + describe "migrating up" do + it "does not add permissions to a role without manage_overview" do + expect { migrate }.not_to change { empty_role.reload.permissions } + end + + it "renames manage_overview to manage_dashboards" do + expect { migrate } + .to change { role_with_permission.reload.permissions } + .from(match_array(%i[manage_overview])) + .to(match_array(%i[manage_dashboards])) + end + end + + describe "migrating down" do + subject(:rollback) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:down) } } + + before { migrate } + + it "reverts manage_dashboards to manage_overview" do + expect { rollback } + .to change { role_with_permission.reload.permissions } + .from(match_array(%i[manage_dashboards])) + .to(match_array(%i[manage_overview])) + end + end +end diff --git a/modules/reporting/config/locales/crowdin/ro.yml b/modules/reporting/config/locales/crowdin/ro.yml index c113cc4a5d4..ddd90658fff 100644 --- a/modules/reporting/config/locales/crowdin/ro.yml +++ b/modules/reporting/config/locales/crowdin/ro.yml @@ -70,7 +70,7 @@ ro: label_filter: "Filtrează" label_filter_add: "Adaugă filtru" label_filter_plural: "Filtre" - label_group_by: "Grupează după" + label_group_by: "Grupare după" label_group_by_add: "Adaugă atributul Grupează-după" label_inactive: "Inactiv" label_no: "Nu" diff --git a/modules/reporting/config/locales/crowdin/vi.yml b/modules/reporting/config/locales/crowdin/vi.yml index 0b864f3df4a..43bd0a017d5 100644 --- a/modules/reporting/config/locales/crowdin/vi.yml +++ b/modules/reporting/config/locales/crowdin/vi.yml @@ -73,7 +73,7 @@ vi: label_group_by: "Nhóm theo" label_group_by_add: "Thêm thuộc tính theo nhóm" label_inactive: "«không hoạt động»" - label_no: "không" + label_no: "Không" label_none: "(không có dữ liệu)" label_no_reports: "Chưa có báo cáo chi phí." label_report: "Báo cáo" diff --git a/modules/reporting/config/locales/crowdin/zh-TW.yml b/modules/reporting/config/locales/crowdin/zh-TW.yml index 6a0980310d3..014919fb7f9 100644 --- a/modules/reporting/config/locales/crowdin/zh-TW.yml +++ b/modules/reporting/config/locales/crowdin/zh-TW.yml @@ -53,7 +53,7 @@ zh-TW: label_money: "金額" label_month_reporting: "月" label_new_report: "新建成本報表" - label_open: "開啟" + label_open: "開啟中" label_operator: "操作員" label_private_report_plural: "私密成本報告" label_progress_bar_explanation: "產生報告中..." @@ -70,7 +70,7 @@ zh-TW: label_filter: "篩選條件" label_filter_add: "新增篩選條件" label_filter_plural: "篩選條件" - label_group_by: "分類" + label_group_by: "分組依據" label_group_by_add: "新增群組欄位" label_inactive: "«不活動»" label_no: "否" diff --git a/modules/storages/app/common/storages/adapters/authentication_strategies/basic_auth.rb b/modules/storages/app/common/storages/adapters/authentication_strategies/basic_auth.rb index cea3bb61c9a..7a47f8684bd 100644 --- a/modules/storages/app/common/storages/adapters/authentication_strategies/basic_auth.rb +++ b/modules/storages/app/common/storages/adapters/authentication_strategies/basic_auth.rb @@ -40,7 +40,7 @@ module Storages return build_failure(storage) if username.blank? || password.blank? - yield OpenProject.httpx.basic_auth(username, password).with(http_options) + yield OpenProject.httpx.plugin(:basic_auth).basic_auth(username, password).with(http_options) end private diff --git a/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_client_credentials.rb b/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_client_credentials.rb index ac771777fdd..083c36ee3e0 100644 --- a/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_client_credentials.rb +++ b/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_client_credentials.rb @@ -39,21 +39,21 @@ module Storages @use_cache = use_cache end - def call(storage:, http_options: {}) + def call(storage:, http_options: {}) # rubocop:disable Metrics/AbcSize config = validate_configuration(storage).value_or { return Failure(it) } token_cache_key = TOKEN_CACHE_KEY % storage.id access_token = @use_cache ? Rails.cache.read(token_cache_key) : nil - http = build_http_session(access_token, config, http_options).value_or { return Failure(it) } + session = build_http_session(access_token, config, http_options).value_or { Failure(it) } - operation_result = yield http + operation_result = yield session return operation_result unless @use_cache case operation_result in Success if @use_cache && access_token.blank? - write_cache(token_cache_key, http) + write_cache(token_cache_key, session) in Failure(code: :forbidden) clear_cache(token_cache_key) else @@ -61,6 +61,18 @@ module Storages end operation_result + # HTTPX default behaviour is to return error responses and not raise errors unless + # explicitly asked by using the `#raise_for_status`method. + # + # On Storages codebase we handle the error responses, but the OAuth + # plugin raises and exception when it fails to get a Token.. + # The handling below will only apply to authentication errors. + rescue HTTPX::HTTPError => e + error("Error while refreshing OAuth token - Payload: #{e.response}") + + Failure(Results::Error.new(code: :unauthorized, payload: e.response, source: self.class)) + rescue HTTPX::TimeoutError => e + Failure(Results::Error.new(code: :timeout, payload: e.to_s, source: self.class)) end private @@ -73,35 +85,15 @@ module Storages end def write_cache(key, httpx_session) - access_token = httpx_session.instance_variable_get(:@options).oauth_session.access_token + access_token = httpx_session.send(:oauth_session).access_token Rails.cache.write(key, access_token, expires_in: 50.minutes) end def clear_cache(key) = Rails.cache.delete(key) def build_http_session(access_token, config, http_options) - if access_token.present? - http_with_current_token(access_token:, http_options:) - else - http_with_new_token(config:, http_options:) - end - end - - def http_with_current_token(access_token:, http_options:) - opts = http_options.deep_merge({ headers: { "Authorization" => "Bearer #{access_token}" } }) - Success(OpenProject.httpx.with(opts)) - end - - def http_with_new_token(config:, http_options:) - http = OpenProject.httpx - .oauth_auth(**config.to_h, token_endpoint_auth_method: "client_secret_post") - .with_access_token - .with(http_options) - Success(http) - rescue HTTPX::HTTPError => e - Failure(Results::Error.new(code: :unauthorized, payload: e.response, source: self.class)) - rescue HTTPX::TimeoutError => e - Failure(Results::Error.new(code: :timeout, payload: e.to_s, source: self.class)) + Success(OpenProject.httpx.plugin(:oauth) + .with(**http_options, oauth_options: { **config, access_token: access_token })) end end end diff --git a/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_configuration.rb b/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_configuration.rb index 3eb74f53a28..cddf79f3077 100644 --- a/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_configuration.rb +++ b/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_configuration.rb @@ -49,6 +49,7 @@ module Storages def to_h { issuer:, client_id:, client_secret:, scope: } end + alias_method :to_hash, :to_h end end end diff --git a/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_user_token.rb b/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_user_token.rb index 650f4951fc6..036841b5fe7 100644 --- a/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_user_token.rb +++ b/modules/storages/app/common/storages/adapters/authentication_strategies/oauth_user_token.rb @@ -39,22 +39,15 @@ module Storages @error_data = Results::Error.new(source: self.class, code: :error) end - # rubocop:disable Metrics/AbcSize def call(storage:, http_options: {}, &) oauth_client = validate_oauth_client(storage).value_or { return Failure(it) } token = current_token(oauth_client).value_or { return Failure(it) } - original_response = yield(httpx_with_auth(token.access_token, http_options)) - - case original_response - in Failure(code: :unauthorized) - updated_token = refresh_token!(storage.oauth_configuration.to_httpx_oauth_config.to_h, - http_options, - token).value_or { return Failure(it) } - yield(httpx_with_auth(updated_token.access_token, http_options)) - else - original_response - end + perform_request( + httpx_oauth_session(storage.oauth_configuration.to_httpx_oauth_config, token, http_options), + token, + & + ) rescue ActiveRecord::StaleObjectError => e raise e if @retried @@ -62,14 +55,33 @@ module Storages @retried = true retry end - # rubocop:enable Metrics/AbcSize private - def httpx_with_auth(access_token, http_options) - OpenProject - .httpx - .with(http_options.deep_merge(headers: { "Authorization" => "Bearer #{access_token}" })) + def perform_request(session, token, &) + response = yield(session) + + response.bind { update_token(session, token) } + + response + rescue HTTPX::TimeoutError => e + handle_timeout(token, e) + rescue HTTPX::Error => e + handle_http_error(token, e) + end + + def update_token(session, token) + oauth_session = session.send(:oauth_session) + token.update!(access_token: oauth_session.access_token, refresh_token: oauth_session.refresh_token) + end + + def httpx_oauth_session(oauth_config, token, http_options) + OpenProject.httpx + .plugin(:retries) + .plugin(:oauth) + .with(**http_options, + oauth_options: { **oauth_config, + access_token: token.access_token, refresh_token: token.refresh_token }) end def validate_oauth_client(storage) @@ -78,34 +90,16 @@ module Storages Failure(@error_data.with(code: :missing_oauth_client, payload: storage)) end - def refresh_token!(oauth_config, http_options, token) - oauth_session = OpenProject - .httpx - .oauth_auth(**oauth_config, - refresh_token: token.refresh_token, - token_endpoint_auth_method: "client_secret_post") - .with(http_options) - .with_access_token - .instance_variable_get(:@options) - .oauth_session - token.update!(access_token: oauth_session.access_token, refresh_token: oauth_session.refresh_token) - Success(token) - rescue HTTPX::HTTPError => e - handle_http_error(token, e) - rescue HTTPX::TimeoutError => e - handle_timeout(token, e) - end - def handle_timeout(token, exception) - Rails.logger.error("Timeout while refreshing OAuth token. - Payload: #{exception.message}") - token.destroy + error("Timeout while refreshing OAuth token. - Payload: #{exception.message}") + token.destroy! Failure(@error_data.with(error: :timeout_on_refresh, payload: exception)) end - def handle_http_error(token, error) - Rails.logger.error("Error while refreshing OAuth token - Payload: #{error.response}") - token.destroy - Failure(@error_data.with(code: :unauthorized, payload: error.response)) + def handle_http_error(token, exception) + error("Error while refreshing OAuth token - Payload: #{exception.response}") + token.destroy! + Failure(@error_data.with(code: :unauthorized, payload: exception.response)) end def current_token(client) diff --git a/modules/storages/app/common/storages/adapters/providers/nextcloud/queries/download_link_query.rb b/modules/storages/app/common/storages/adapters/providers/nextcloud/queries/download_link_query.rb index 5c5d34ad56b..7ee86871c57 100644 --- a/modules/storages/app/common/storages/adapters/providers/nextcloud/queries/download_link_query.rb +++ b/modules/storages/app/common/storages/adapters/providers/nextcloud/queries/download_link_query.rb @@ -35,11 +35,9 @@ module Storages module Queries class DownloadLinkQuery < Base def call(auth_strategy:, input_data:) - Authentication[auth_strategy].call(storage: @storage, http_options:) do |http| - fetch_origin_name(input_data, auth_strategy).bind do |origin_name| - fetch_download_token(auth_strategy, input_data.file_id).fmap do |token| - URI(download_link(token, origin_name)) - end + fetch_origin_name(input_data, auth_strategy).bind do |origin_name| + fetch_download_token(auth_strategy, input_data.file_id).fmap do |token| + URI(download_link(token, origin_name)) end end end @@ -55,11 +53,7 @@ module Storages case response in { status: 200..299 } - if response.body.blank? - Failure(error.with(code: :unauthorized)) - else - build_download_link(response, error) - end + build_download_link(response, error) in { status: 404 } Failure(error.with(code: :not_found)) in { status: 401 } @@ -70,14 +64,12 @@ module Storages end def fetch_origin_name(input_data, auth_strategy) - FileInfoQuery.call(storage: @storage, auth_strategy:, input_data:) - .bind do |file_info| - file_name = file_info.name - return Success(file_name) if file_name.present? + FileInfoQuery.call(storage: @storage, auth_strategy:, input_data:).bind do |file_info| + file_name = file_info.name + return Success(file_name) if file_name.present? - error = Results::Error.new(source: self.class, payload: file_info) - Failure(error.with(code: :not_found)) - end + Failure(Results::Error.new(source: self.class, payload: file_info, code: :not_found)) + end end def fetch_download_token(auth_strategy, file_id) @@ -100,6 +92,8 @@ module Storages return parsing_error if token.blank? Success(token) + rescue HTTPX::Error + parsing_error end def download_link(token, origin_name) diff --git a/modules/storages/app/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator.rb b/modules/storages/app/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator.rb index f93515ea20f..75daf1b9368 100644 --- a/modules/storages/app/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator.rb +++ b/modules/storages/app/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator.rb @@ -40,13 +40,21 @@ module Storages def validate register_checks( - :team_folder_app, :files_request, :userless_access, :team_folder_presence, :team_folder_contents + :team_folder_app, + :files_request, + :userless_access, + :team_folder_presence, + :project_folders_linked, + :project_folders_exist, + :team_folder_contents ) team_folder_app_checks files_request_failed_with_unknown_error userless_access_denied team_folder_not_found + project_folders_linked + project_folders_exist with_unexpected_content end @@ -66,7 +74,7 @@ module Storages if capabilities.group_folder_disabled? fail_check(:team_folder_app, :nc_dependency_missing, context: { dependency: }) elsif capabilities.group_folder_version < required_version - fail_check(:team_folder_app, :nc_dependency_version_mismatch, context: { dependency: }) + warn_check(:team_folder_app, :nc_dependency_version_mismatch, context: { dependency: }) else pass_check(:team_folder_app) end @@ -92,12 +100,33 @@ module Storages pass_check(:files_request) end + def project_folders_linked + ampf_project_storages = @storage.project_storages.active.automatic + expected = ampf_project_storages.count + actual = ampf_project_storages.with_project_folder.count + return pass_check(:project_folders_linked) if actual == expected + + warn_check(:project_folders_linked, :nc_unlinked_project_folders, context: { actual:, expected: }) + end + + def project_folders_exist + @storage.project_storages.active.automatic.with_project_folder.each do |project_storage| + next if existing_folder_ids.include?(project_storage.project_folder_id) + + return fail_check( + :project_folders_exist, :nc_project_folder_missing, context: { project: project_storage.project.name } + ) + end + + pass_check(:project_folders_exist) + end + def with_unexpected_content unexpected_files = files.value!.reject { managed_project_folder_ids.include?(it.id) } return pass_check(:team_folder_contents) if unexpected_files.empty? log_extraneous_files(unexpected_files) - warn_check(:team_folder_contents, :nc_unexpected_content) + warn_check(:team_folder_contents, :nc_unexpected_files, context: { sample: unexpected_files.sample.name }) end def log_extraneous_files(unexpected_files) @@ -121,6 +150,10 @@ module Storages end end + def existing_folder_ids + @existing_folder_ids ||= files.value!.all_folders.to_set(&:id) + end + def noop = Input::Strategy.build(key: :noop) def nextcloud_dependencies diff --git a/modules/storages/app/components/storages/admin/side_panel/health_notifications_component.html.erb b/modules/storages/app/components/storages/admin/side_panel/health_notifications_component.html.erb index f0272abe837..0e9592207f0 100644 --- a/modules/storages/app/components/storages/admin/side_panel/health_notifications_component.html.erb +++ b/modules/storages/app/components/storages/admin/side_panel/health_notifications_component.html.erb @@ -36,7 +36,7 @@ See COPYRIGHT and LICENSE files for more details. notifications_container.with_row do concat( render(Primer::Beta::Text.new(pr: 2, test_selector: "storage-health-checked-at")) do - I18n.t("storages.health.checked", datetime: helpers.format_time(@storage.health_checked_at)) + I18n.t("storages.health.synced", datetime: helpers.format_time(@storage.health_checked_at)) end ) diff --git a/modules/storages/app/validator/nextcloud_application_credentials_validator.rb b/modules/storages/app/validator/nextcloud_application_credentials_validator.rb index 794a10937b6..8aa7e1e5ed9 100644 --- a/modules/storages/app/validator/nextcloud_application_credentials_validator.rb +++ b/modules/storages/app/validator/nextcloud_application_credentials_validator.rb @@ -40,6 +40,7 @@ class NextcloudApplicationCredentialsValidator response = OpenProject .httpx + .plugin(:basic_auth) .basic_auth(contract.username, contract.password) .head(Storages::UrlBuilder.url(contract.model.uri, "remote.php/dav")) case response diff --git a/modules/storages/config/locales/crowdin/af.yml b/modules/storages/config/locales/crowdin/af.yml index a3e1eae664d..b28bb59ee62 100644 --- a/modules/storages/config/locales/crowdin/af.yml +++ b/modules/storages/config/locales/crowdin/af.yml @@ -279,6 +279,8 @@ af: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ af: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ af: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ar.yml b/modules/storages/config/locales/crowdin/ar.yml index 35136372029..748f12662da 100644 --- a/modules/storages/config/locales/crowdin/ar.yml +++ b/modules/storages/config/locales/crowdin/ar.yml @@ -279,6 +279,8 @@ ar: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -330,8 +332,10 @@ ar: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -380,6 +384,7 @@ ar: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/az.yml b/modules/storages/config/locales/crowdin/az.yml index 837192548b5..99a1caa5947 100644 --- a/modules/storages/config/locales/crowdin/az.yml +++ b/modules/storages/config/locales/crowdin/az.yml @@ -279,6 +279,8 @@ az: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ az: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ az: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/be.yml b/modules/storages/config/locales/crowdin/be.yml index e5b4ae686bc..8bd71dbceb2 100644 --- a/modules/storages/config/locales/crowdin/be.yml +++ b/modules/storages/config/locales/crowdin/be.yml @@ -279,6 +279,8 @@ be: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -326,8 +328,10 @@ be: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -376,6 +380,7 @@ be: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/bg.yml b/modules/storages/config/locales/crowdin/bg.yml index f776454f834..1f7e130df70 100644 --- a/modules/storages/config/locales/crowdin/bg.yml +++ b/modules/storages/config/locales/crowdin/bg.yml @@ -279,6 +279,8 @@ bg: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ bg: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ bg: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ca.yml b/modules/storages/config/locales/crowdin/ca.yml index 73998b14972..a06891cbc2e 100644 --- a/modules/storages/config/locales/crowdin/ca.yml +++ b/modules/storages/config/locales/crowdin/ca.yml @@ -279,6 +279,8 @@ ca: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ ca: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ ca: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ckb-IR.yml b/modules/storages/config/locales/crowdin/ckb-IR.yml index 8ac9d49712f..813a87713e2 100644 --- a/modules/storages/config/locales/crowdin/ckb-IR.yml +++ b/modules/storages/config/locales/crowdin/ckb-IR.yml @@ -279,6 +279,8 @@ ckb-IR: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ ckb-IR: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ ckb-IR: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/cs.yml b/modules/storages/config/locales/crowdin/cs.yml index 4b8eb5d866e..052866f7691 100644 --- a/modules/storages/config/locales/crowdin/cs.yml +++ b/modules/storages/config/locales/crowdin/cs.yml @@ -279,6 +279,8 @@ cs: drive_contents: Drive content files_request: Fetching team folder files header: Automaticky spravované projektové složky + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -326,8 +328,10 @@ cs: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Nakonfigurované heslo aplikace je neplatné. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -376,6 +380,7 @@ cs: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/da.yml b/modules/storages/config/locales/crowdin/da.yml index e95ef9f15e2..90153e877a4 100644 --- a/modules/storages/config/locales/crowdin/da.yml +++ b/modules/storages/config/locales/crowdin/da.yml @@ -279,6 +279,8 @@ da: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ da: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ da: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/de.yml b/modules/storages/config/locales/crowdin/de.yml index 7abfc15b703..52a0cf0a924 100644 --- a/modules/storages/config/locales/crowdin/de.yml +++ b/modules/storages/config/locales/crowdin/de.yml @@ -279,6 +279,8 @@ de: drive_contents: Speicherinhalt files_request: Teamordnerdateien werden abgerufen header: Automatisch verwaltete Projektordner + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Abhängigkeit: Teamordner' team_folder_contents: Inhalt des Teamordners team_folder_presence: Teamordner existiert @@ -322,8 +324,10 @@ de: nc_oauth_request_not_found: Der Endpunkt für den Abruf des aktuell verbundenen Benutzers wurde nicht gefunden. Bitte überprüfen Sie die Serverprotokolle für weitere Informationen. nc_oauth_request_unauthorized: Der aktuelle Benutzer ist nicht berechtigt, auf den Remote-Datei-Speicher zuzugreifen. Bitte überprüfen Sie die Server-Protokolle für weitere Informationen. nc_oauth_token_missing: OpenProject kann die Kommunikation auf Benutzerebene mit Nextcloud nicht testen, da der Benutzer sein Nextcloud Konto noch nicht verknüpft hat. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Der Teamordner konnte nicht gefunden werden. - nc_unexpected_content: Unerwarteter Inhalt im verwalteten Teamordner gefunden. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Das konfigurierte App-Passwort ist ungültig. not_configured: Die Verbindung konnte nicht validiert werden. Bitte schließen Sie zuerst die Konfiguration ab. od_client_cant_delete_folder: Der Client hat Probleme beim Löschen von Ordnern. Bitte überprüfen Sie die Setup-Dokumentation für Ihren Speicher. @@ -372,6 +376,7 @@ de: failure: Einige Überprüfungen sind fehlgeschlagen und das System funktioniert nicht wie erwartet. success: Alle Verbindungen und Systeme funktionieren wie erwartet. warning: Einige Überprüfungen ergaben eine Warnung. Dies kann zu unerwartetem Verhalten führen. + synced: 'Zuletzt aktualisiert: %{datetime}' title: Gesundheitsstatusbericht health_email_notifications: description_disabled: Administrator:innen erhalten keine E-Mail-Updates, wenn es wichtige Änderungen gibt. diff --git a/modules/storages/config/locales/crowdin/el.yml b/modules/storages/config/locales/crowdin/el.yml index 1204fd9f2d1..12b2669b557 100644 --- a/modules/storages/config/locales/crowdin/el.yml +++ b/modules/storages/config/locales/crowdin/el.yml @@ -279,6 +279,8 @@ el: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ el: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ el: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/eo.yml b/modules/storages/config/locales/crowdin/eo.yml index b14e36bc75d..6f0ad947e98 100644 --- a/modules/storages/config/locales/crowdin/eo.yml +++ b/modules/storages/config/locales/crowdin/eo.yml @@ -279,6 +279,8 @@ eo: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ eo: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ eo: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/es.yml b/modules/storages/config/locales/crowdin/es.yml index 8e66eb7b4ce..5d8e0c2e60b 100644 --- a/modules/storages/config/locales/crowdin/es.yml +++ b/modules/storages/config/locales/crowdin/es.yml @@ -279,6 +279,8 @@ es: drive_contents: Contenido de la unidad files_request: Obteniendo archivos de carpetas de equipo header: Carpetas de proyecto gestionadas automáticamente + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependencia: Carpetas de equipo' team_folder_contents: Contenido de la carpeta de equipo team_folder_presence: La carpeta del equipo existe @@ -322,8 +324,10 @@ es: nc_oauth_request_not_found: No se ha encontrado el terminal para obtener el usuario actualmente conectado. Consulte los registros del servidor para obtener más información. nc_oauth_request_unauthorized: El usuario actual no está autorizado a acceder al almacenamiento remoto de archivos. Consulte los registros del servidor para obtener más información. nc_oauth_token_missing: OpenProject no puede probar la comunicación a nivel de usuario con Nextcloud, ya que el usuario aún no ha vinculado su cuenta de Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: No se ha podido encontrar la carpeta del equipo. - nc_unexpected_content: Se ha encontrado contenido inesperado en la carpeta del equipo gestionada. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: La contraseña configurada de la aplicación no es válida. not_configured: No se ha podido validar la conexión. Por favor, termine primero la configuración. od_client_cant_delete_folder: El cliente tiene problemas para eliminar carpetas. Consulte la documentación de configuración de su almacenamiento. @@ -372,6 +376,7 @@ es: failure: Algunas comprobaciones han fallado y el sistema no funciona como se esperaba. success: Todas las conexiones y sistemas funcionan según lo previsto. warning: Algunas comprobaciones devolvieron una advertencia. Esto puede dar lugar a un comportamiento inesperado. + synced: 'Last sync: %{datetime}' title: Informe de estado de salud health_email_notifications: description_disabled: Los administradores no recibirán actualizaciones por correo electrónico cuando haya actualizaciones importantes. diff --git a/modules/storages/config/locales/crowdin/et.yml b/modules/storages/config/locales/crowdin/et.yml index 9f55d41dffc..04e14a57ece 100644 --- a/modules/storages/config/locales/crowdin/et.yml +++ b/modules/storages/config/locales/crowdin/et.yml @@ -279,6 +279,8 @@ et: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ et: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ et: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/eu.yml b/modules/storages/config/locales/crowdin/eu.yml index 2d727afd2cb..71b214592a8 100644 --- a/modules/storages/config/locales/crowdin/eu.yml +++ b/modules/storages/config/locales/crowdin/eu.yml @@ -279,6 +279,8 @@ eu: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ eu: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ eu: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/fa.yml b/modules/storages/config/locales/crowdin/fa.yml index 7b258d9059c..345e54afedd 100644 --- a/modules/storages/config/locales/crowdin/fa.yml +++ b/modules/storages/config/locales/crowdin/fa.yml @@ -279,6 +279,8 @@ fa: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ fa: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ fa: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/fi.yml b/modules/storages/config/locales/crowdin/fi.yml index aff63ffd958..d03517b3564 100644 --- a/modules/storages/config/locales/crowdin/fi.yml +++ b/modules/storages/config/locales/crowdin/fi.yml @@ -279,6 +279,8 @@ fi: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ fi: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ fi: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/fil.yml b/modules/storages/config/locales/crowdin/fil.yml index 260e7617dc2..e4313441255 100644 --- a/modules/storages/config/locales/crowdin/fil.yml +++ b/modules/storages/config/locales/crowdin/fil.yml @@ -279,6 +279,8 @@ fil: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ fil: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ fil: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/fr.yml b/modules/storages/config/locales/crowdin/fr.yml index d858daae69b..0bd06148587 100644 --- a/modules/storages/config/locales/crowdin/fr.yml +++ b/modules/storages/config/locales/crowdin/fr.yml @@ -279,6 +279,8 @@ fr: drive_contents: Contenu du lecteur files_request: Récupération des fichiers du dossier de l'équipe header: Dossiers de projets gérés automatiquement + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dépendance : dossiers d''équipe' team_folder_contents: Contenu du dossier de l'équipe team_folder_presence: Le dossier de l'équipe existe @@ -322,8 +324,10 @@ fr: nc_oauth_request_not_found: Le point de terminaison pour récupérer l'utilisateur actuellement connecté n'a pas été trouvé. Veuillez vérifier les journaux du serveur pour obtenir plus d'informations. nc_oauth_request_unauthorized: L'utilisateur actuel n'est pas autorisé à accéder à l'espace de stockage de fichiers distant. Veuillez consulter les journaux du serveur pour obtenir plus d'informations. nc_oauth_token_missing: OpenProject ne peut pas tester la communication au niveau utilisateur avec Nextcloud, car l'utilisateur n'a pas encore lié son compte Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Le dossier de l'équipe est introuvable. - nc_unexpected_content: Contenu inattendu trouvé dans le dossier d'équipe géré. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Le mot de passe de l'application configurée n'est pas valide. not_configured: La connexion n'a pas pu être validée. Veuillez d'abord terminer la configuration. od_client_cant_delete_folder: Le client rencontre des difficultés pour supprimer des dossiers. Veuillez consulter la documentation d'installation de votre espace de stockage. @@ -372,6 +376,7 @@ fr: failure: Certaines vérifications ont échoué et le système ne fonctionne pas comme prévu. success: Toutes les connexions et les systèmes fonctionnent comme prévu. warning: Certaines vérifications ont renvoyé un avertissement. Cela peut entraîner un comportement inattendu. + synced: 'Dernière synchronisation : %{datetime}' title: Rapport sur l'état de santé health_email_notifications: description_disabled: Les administrateurs ne recevront pas de mises à jour par e-mail lors des mises à jour importantes. diff --git a/modules/storages/config/locales/crowdin/he.yml b/modules/storages/config/locales/crowdin/he.yml index bdcb7832e4e..ef759a21e2d 100644 --- a/modules/storages/config/locales/crowdin/he.yml +++ b/modules/storages/config/locales/crowdin/he.yml @@ -279,6 +279,8 @@ he: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -326,8 +328,10 @@ he: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -376,6 +380,7 @@ he: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/hi.yml b/modules/storages/config/locales/crowdin/hi.yml index 48343bc1d5b..297c71b0cc3 100644 --- a/modules/storages/config/locales/crowdin/hi.yml +++ b/modules/storages/config/locales/crowdin/hi.yml @@ -279,6 +279,8 @@ hi: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ hi: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ hi: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/hr.yml b/modules/storages/config/locales/crowdin/hr.yml index 5b26f9ccc46..f10343005e9 100644 --- a/modules/storages/config/locales/crowdin/hr.yml +++ b/modules/storages/config/locales/crowdin/hr.yml @@ -279,6 +279,8 @@ hr: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -324,8 +326,10 @@ hr: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -374,6 +378,7 @@ hr: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/hu.yml b/modules/storages/config/locales/crowdin/hu.yml index f46bc6960d2..45366821369 100644 --- a/modules/storages/config/locales/crowdin/hu.yml +++ b/modules/storages/config/locales/crowdin/hu.yml @@ -279,6 +279,8 @@ hu: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ hu: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ hu: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/id.yml b/modules/storages/config/locales/crowdin/id.yml index d2ed1045168..41a4cd5d2b9 100644 --- a/modules/storages/config/locales/crowdin/id.yml +++ b/modules/storages/config/locales/crowdin/id.yml @@ -279,6 +279,8 @@ id: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -320,8 +322,10 @@ id: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -370,6 +374,7 @@ id: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/it.yml b/modules/storages/config/locales/crowdin/it.yml index 93567eeaa53..a8d4059080f 100644 --- a/modules/storages/config/locales/crowdin/it.yml +++ b/modules/storages/config/locales/crowdin/it.yml @@ -279,6 +279,8 @@ it: drive_contents: Contenuto dell'unità files_request: Recupero dei file delle cartelle di team header: Cartelle di progetto gestite automaticamente + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dipendenza: Cartelle di team' team_folder_contents: Contenuto della cartella di team team_folder_presence: La cartella di team esiste @@ -322,8 +324,10 @@ it: nc_oauth_request_not_found: L'endpoint per recuperare l'utente attualmente connesso non è stato trovato. Per ulteriori informazioni, consulta i log del server. nc_oauth_request_unauthorized: L'utente attuale non è autorizzato ad accedere all'archivio file remoto. Per ulteriori informazioni, consulta i log del server. nc_oauth_token_missing: OpenProject non può testare la comunicazione a livello utente con Nextcloud poiché l'utente non ha ancora collegato il proprio account Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Impossibile trovare la cartella di team. - nc_unexpected_content: Contenuto inatteso trovato nella cartella di team gestita. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: La password dell'app configurata non è valida. not_configured: Non è stato possibile verificare la connessione. Prima è necessario completare la configurazione. od_client_cant_delete_folder: Il cliente riscontra problemi con l'eliminazione delle cartelle. Consulta la documentazione di configurazione del tuo archivio. @@ -372,6 +376,7 @@ it: failure: Alcuni controlli non sono riusciti e il sistema non funziona come previsto. success: Tutte le connessioni e i sistemi funzionano come previsto. warning: Alcuni controlli hanno restituito un avviso. Questo può causare comportamenti imprevisti. + synced: 'Last sync: %{datetime}' title: Report sullo stato di salute health_email_notifications: description_disabled: Gli amministratori non riceveranno aggiornamenti via email quando ci sono aggiornamenti importanti. diff --git a/modules/storages/config/locales/crowdin/ja.yml b/modules/storages/config/locales/crowdin/ja.yml index 86bc0b213bc..3b9839f81ab 100644 --- a/modules/storages/config/locales/crowdin/ja.yml +++ b/modules/storages/config/locales/crowdin/ja.yml @@ -18,7 +18,7 @@ ja: token_exchange_scope: ストレージスコープ storages/project_storage: project_folder: プロジェクトフォルダ - project_folder_mode: プロジェクトフォルダーモード + project_folder_mode: プロジェクトフォルダモード storage: ストレージ storage_url: ストレージURL storages/sharepoint_storage: @@ -29,53 +29,53 @@ ja: storages/storage: authentication_method: 認証方法 creator: 作成者 - drive: ドライブID + drive: ドライブ ID host: ホスト name: 名称 password: アプリケーションのパスワード - provider_type: プロバイダー・タイプ - tenant: ディレクトリ(テナント)ID + provider_type: プロバイダーの種類 + tenant: ディレクトリ (テナント) ID errors: messages: invalid_host_url: は有効な URL ではありません。 - invalid_sharepoint_url: は有効なSharePointサイト、ライブラリ、ドキュメントのURLではありません。 - not_linked_to_project: はプロジェクトにリンクされていない。 + invalid_sharepoint_url: は有効なSharePointサイト、ライブラリ、またはドキュメントのURLではありません。 + not_linked_to_project: はプロジェクトにリンクされていません。 models: storages/file_link: attributes: origin_id: - only_numeric_or_uuid: には数値かuuidしか指定できない。 + only_numeric_or_uuid: は数値またはuuidのみとなります。 storages/project_storage: attributes: project_folder_id: blank: フォルダーを選択してください。 project_folder_mode: - mode_unavailable: はこのストレージでは使用できない。 + mode_unavailable: このストレージでは使用できません。 project_ids: blank: プロジェクトを選択してください。 storages/storage: attributes: host: - authorization_header_missing: が完全にセットアップされていません。APIリクエストのベアラートークンベースの認証に必要な "Authorization "ヘッダーをNextcloudインスタンスが受け取っていません。HTTPサーバーの設定を再度ご確認ください。 - cannot_be_connected_to: に到達できませんでした。ホストに到達可能で、OpenProject 統合アプリがインストールされていることを確認してください。 - minimal_nextcloud_version_unmet: 最小バージョン要件を満たしていない(Nextcloud 23以上である必要があります。) - not_nextcloud_server: はNextcloudサーバーではありません。 - op_application_not_installed: は、アプリ「OpenProject integration」がインストールされていないようです。インストールしてからもう一度お試しください。 + authorization_header_missing: 完全には設定されていません。 Nextcloudインスタンスは、APIリクエストのベアラートークンベースの認可に必要な「Authorization」ヘッダーを受け取りません。 HTTPサーバーの設定を再確認してください。 + cannot_be_connected_to: に到達できませんでした。ホストが到達可能で、OpenProject 統合アプリがインストールされていることを確認してください。 + minimal_nextcloud_version_unmet: 最小バージョン要件を満たしていません(Nextcloud23以上でなければなりません) + not_nextcloud_server: はNextcloudサーバーではありません + op_application_not_installed: アプリ「OpenProject統合」がインストールされていません。最初にインストールしてからもう一度お試しください。 password: - invalid_password: は無効である。 + invalid_password: は無効です。 unknown_error: could not be validated with the file storage provider. Please verify that the connection is functioning properly. models: file_link: ファイル storages/storage: ストレージ api_v3: errors: - too_many_elements_created_at_once: 一度に作成される要素が多すぎる。最大でも %{max} 、 %{actual}。 + too_many_elements_created_at_once: 一度に作成された要素が多すぎます。 %{max} の期待値は %{actual} です。 external_file_storages: 外部ファイルストレージ permission_create_files: '自動的に管理されたプロジェクトフォルダ: ファイルの作成' permission_create_files_explanation: この権限はNextcloudストレージでのみ利用できます permission_delete_files: '自動的に管理されたプロジェクトフォルダ: ファイルの削除' permission_delete_files_explanation: この権限はNextcloudストレージでのみ利用できます - permission_header_for_project_module_storages: 自動的に管理されるプロジェクトフォルダ + permission_header_for_project_module_storages: 自動的に管理されたプロジェクトフォルダ permission_manage_file_links: ファイルへのリンク管理 permission_manage_files_in_project: プロジェクト内のファイル管理 permission_read_files: '自動的に管理されたプロジェクトフォルダ: ファイルの読み込み' @@ -86,16 +86,16 @@ ja: project_module_storages: ファイルを添付する project_storages: edit_project_folder: - label: プロジェクトフォルダの編集 + label: プロジェクトフォルダを編集 open: - contact_admin: このエラーを解決するには、管理者に連絡してください。 - remote_identity_error: ストレージへの接続中に予期せぬエラーが発生しました。 + contact_admin: このエラーを解決するには管理者に問い合わせてください。 + remote_identity_error: ストレージへの接続中に予期しないエラーが発生しました。 project_folder_mode: - automatic: 自動的に管理される - inactive: 特定のフォルダなし + automatic: 自動的に管理 + inactive: 特定のフォルダがありません manual: 既存のフォルダを手動で管理 remove_project: - deletion_failure_flash: ストレージからのプロジェクトの削除に失敗しました。 %{error} + deletion_failure_flash: プロジェクトをストレージから削除できませんでした。 %{error} label: プロジェクトを削除 services: attributes: @@ -110,7 +110,7 @@ ja: one_drive_sync_service: create_folder: 'プロジェクトフォルダの作成を管理:' ensure_root_folder_permissions: 'ベースフォルダの権限を設定:' - hide_inactive_folders: '非アクティブフォルダを隠す ステップ:' + hide_inactive_folders: '非アクティブフォルダを隠す ステップ' remote_folders: 'Read contents of the drive root folder:' rename_project_folder: '管理プロジェクトフォルダの名前を変更します:' sharepoint_sync_service: @@ -121,16 +121,16 @@ ja: rename_project_folder: '管理プロジェクトフォルダの名前を変更します:' errors: messages: - error: 予期しないエラーが発生しました。OpenProject のログを確認するか、管理者に連絡してください + error: 予期しないエラーが発生しました。OpenProject のログを確認するか、管理者に連絡してください。 forbidden: OpenProject could not access the requested resource. Please check your permissions configuration on the Storage Provider. unauthorized: OpenProjectはストレージプロバイダと認証できませんでした。アクセスできることを確認してください。 models: copy_project_folders_service: conflict: フォルダ %{destination_path} は既に存在する。上書きを避けるために処理を中断しています。 - error: 予期しないエラーが発生しました。OpenProject のログを確認するか、管理者に連絡してください - forbidden: OpenProject はソースフォルダにアクセスできませんでした。ストレージ・プロバイダの権限設定を確認してください + error: 予期しないエラーが発生しました。OpenProject のログを確認するか、管理者に連絡してください。 + forbidden: OpenProject はソースフォルダにアクセスできませんでした。ストレージ・プロバイダの権限設定を確認してください。 not_found: ソース・テンプレートの場所 %{source_path} が見つかりませんでした。 - unauthorized: OpenProject はストレージプロバイダと認証できませんでした。ストレージの設定を確認してください + unauthorized: OpenProject はストレージプロバイダと認証できませんでした。ストレージの設定を確認してください。 nextcloud_sync_service: attributes: add_user_to_group: @@ -151,31 +151,31 @@ ja: conflict: '以下の理由により、 %{user} のユーザーを %{group} グループから削除できませんでした: %{reason}' failed_to_remove: '以下の理由により、 %{user} のユーザーを %{group} グループから削除できませんでした: %{reason}' rename_project_folder: - conflict: OpenProjectは、同じ名前のフォルダが既に存在するため、プロジェクトフォルダの名前を %{current_path} に変更できませんでした - forbidden: OpenProject ユーザーは %{current_path} フォルダにアクセスできません。 - not_found: "%{current_path} は見つからなかった。" + conflict: OpenProjectは、同じ名前のフォルダが既に存在するため、プロジェクトフォルダの名前を %{current_path} に変更できませんでした。 + forbidden: OpenProjectユーザーは %{current_path} フォルダにアクセスできません。 + not_found: "%{current_path} は見つかりませんでした。" set_folders_permissions: - permission_not_set: '%{path}にパーミッションを設定できなかった。' - error: 予期しないエラーが発生しました。Nextcloud インスタンスに到達可能であることを確認し、OpenProject ワーカーのログを確認してください + permission_not_set: '%{path} に権限を設定できませんでした。' + error: 予期しないエラーが発生しました。Nextcloudインスタンスがアクセス可能であることを確認し、詳細についてはOpenProjectワーカーログを確認してください。 group_does_not_exist: "%{group} は存在しません。Nextcloudインスタンスの設定を確認してください。" - insufficient_privileges: OpenProjectには、 %{group}に %{user} を追加するのに十分な権限がありません。Nextcloudのグループ設定を確認してください。 - not_allowed: ネクストクラウドはリクエストをブロックする。 + insufficient_privileges: OpenProjectには %{user} を %{group}に追加するための十分な権限がありません。Nextcloudでグループ設定を確認してください。 + not_allowed: Nextcloudはリクエストをブロックします。 not_found: OpenProject could not find the file on the Nextcloud Storage Provider. Please check if it wasn't deleted. unauthorized: OpenProjectがNextcloudと同期できませんでした。ストレージとNextcloudの設定を確認してください。 - user_does_not_exist: "%{user} はNextcloudには存在しません。" + user_does_not_exist: "Nextcloudには%{user} は存在しません。" one_drive_sync_service: attributes: create_folder: - conflict: '%{folder_name} はすでに %{parent_location}に存在している。' - not_found: "%{parent_location} は見つからなかった。" + conflict: '%{folder_name} は %{parent_location} に既に存在します。' + not_found: "%{parent_location} は見つかりませんでした。" hide_inactive_folders: - permission_not_set: '%{path}にパーミッションを設定できなかった。' + permission_not_set: '%{path} に権限を設定できませんでした。' remote_folders: - request_error: OpenProject は %{drive_id}ドライブにアクセスできませんでした。ストレージの設定が正しいかどうか確認してください。 + request_error: OpenProjectがドライブ %{drive_id}にアクセスできませんでした。ストレージの設定が正しいか確認してください。 rename_project_folder: conflict: OpenProject could not rename the folder %{current_path} to %{project_folder_name} as a folder with the same name already exists. - forbidden: OpenProject は、 %{current_path} にアクセスできず、名前を変更できません。 - not_found: "%{current_path} は見つからなかった。" + forbidden: OpenProject は名前を変更するために %{current_path} にアクセスできません。 + not_found: "%{current_path} は見つかりませんでした。" set_folders_permissions: permission_not_set: '%{path} に権限を設定できませんでした。' error: An unexpected error occurred. Please ensure that OneDrive is reachable and check OpenProject worker logs for more information. @@ -279,6 +279,8 @@ ja: drive_contents: Drive content files_request: Fetching team folder files header: 自動的に管理されるプロジェクトフォルダ + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -303,41 +305,43 @@ ja: drive_id_format: ドライブIDフォーマット header: 構成 host: ホスト URL - host_url_accessible: アクセス可能なホストURL + host_url_accessible: ホスト URL アクセス storage_configured: 設定完了 - tenant_id: テナントID + tenant_id: Tenant ID failures: - other: "%{count} チェック失敗" + other: "%{count} チェックに失敗しました" success: すべてのチェックに合格 warnings: other: "%{count} は警告を返しました" connection_validation: client_id_invalid: 設定されたOAuth 2クライアントIDが無効です。設定を確認してください。 client_secret_invalid: 設定されたOAuth 2クライアントシークレットが無効です。設定を確認してください。 - nc_dependency_missing: 'ファイルストレージに必要な依存関係がありません。次の依存関係を追加してください: %{dependency}。' + nc_dependency_missing: 'ファイルストレージに必要な依存関係がありません。次の依存関係を追加してください: %{dependency}。' nc_dependency_version_mismatch: '%{dependency} アプリのバージョンがサポートされていません。Nextcloudサーバーをアップデートしてください。' nc_host_not_found: 設定されたホストURLにNextcloudサーバーが見つかりません。設定を確認してください。 nc_oauth_request_not_found: 現在接続しているユーザーを取得するエンドポイントが見つかりませんでした。詳細については、サーバーのログを確認してください。 nc_oauth_request_unauthorized: 現在のユーザーにはリモートファイルストレージにアクセスする権限がありません。サーバーのログを確認してください。 - nc_oauth_token_missing: OpenProjectでは、ユーザーがNextcloudアカウントをリンクしていないため、ユーザーレベルのNextcloudとの通信をテストできません。 + nc_oauth_token_missing: OpenProject は、Nextcloudアカウントへのリンクがまだないため、Nextcloudとのユーザーレベルの通信をテストできません。 + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: 設定されているアプリのパスワードが無効です。 not_configured: 接続を検証できませんでした。先に設定を完了してください。 - od_client_cant_delete_folder: クライアントがフォルダの削除に失敗しています。お使いのストレージのセットアップドキュメントを確認してください。 - od_client_write_permission_missing: クライアントの書き込み権限が不足しているようです。お使いのストレージのセットアップドキュメントを確認してください。 - od_drive_id_invalid: 設定されたドライブIDが無効のようです。設定を確認してください。 - od_drive_id_not_found: 設定されたドライブIDが見つかりません。設定を確認してください。 - od_oauth_request_not_found: 現在接続しているユーザーを取得するエンドポイントが見つかりませんでした。詳細については、サーバーのログを確認してください。 - od_oauth_request_unauthorized: 現在のユーザーにはリモートファイルストレージにアクセスする権限がありません。サーバーのログを確認してください。 - od_oauth_token_missing: OpenProjectは、ユーザーがまだMicrosoftアカウントをリンクしていないため、OneDriveとのユーザーレベルの通信をテストできません。 - od_tenant_id_wrong: 設定されたディレクトリ(テナント)IDが無効です。設定を確認してください。 + od_client_cant_delete_folder: クライアントがフォルダを削除できません。ストレージのセットアップドキュメントを確認してください。 + od_client_write_permission_missing: クライアントは書き込み権限がありません。ストレージの設定ドキュメントを確認してください。 + od_drive_id_invalid: 設定されたドライブ ID が無効です。設定を確認してください。 + od_drive_id_not_found: 設定されたドライブ ID が見つかりません。設定を確認してください。 + od_oauth_request_not_found: 現在接続されているユーザーを取得するエンドポイントが見つかりませんでした。詳細についてはサーバーログを確認してください。 + od_oauth_request_unauthorized: 現在のユーザーはリモートファイルストレージにアクセスする権限がありません。詳細についてはサーバーログを確認してください。 + od_oauth_token_missing: OpenProject は、ユーザーが Microsoft アカウントをまだリンクしていないため、OneDrive とのユーザー レベルの通信をテストできません。 + od_tenant_id_wrong: 設定されたディレクトリ (テナント) IDは無効です。設定を確認してください。 od_test_folder_exists: テストに必要なフォルダ %{folder_name} はすでに存在します。削除して再度お試しください。 od_unexpected_content: ドライブに予期しないコンテンツが見つかりました。 - offline_access_scope_missing: OpenID Connectプロバイダがoffline_accessスコープを要求するように設定することをお勧めします。統合はまだ機能するかもしれませんが、リフレッシュトークンの有効期限が切れていないことを確認してください。 + offline_access_scope_missing: offline_access スコープを要求するために OpenID Connect プロバイダを設定することをお勧めします。 統合はまだ動作するかもしれませんが、更新トークンが期限切れでないことを確認してください。 oidc_cant_refresh_token: ストレージへのアクセスを確認中にエラーが発生しました。詳細についてはサーバーログを確認してください。 - oidc_non_oidc_user: 現在のユーザーはプロビジョニングされていますが、OpenID Connect (OIDC) Identity Providerによってプロビジョニングされていません。OIDCプロビジョニングされたユーザーでチェックを再実行してください。 - oidc_non_provisioned_user: 現在のユーザはOpenID Connect Identity Providerから提供されていません。提供されたユーザーでチェックを再実行してください。 + oidc_non_oidc_user: 現在のユーザは、プロビジョニング中にOpenID Connect(OIDC)アイデンティティプロバイダによってプロビジョニングされていませんでした。OIDCプロビジョニングされたユーザでチェックを再実行してください。 + oidc_non_provisioned_user: 現在のユーザーはOpenID Connectアイデンティティプロバイダーによって提供されていません。指定されたユーザーとチェックを再実行してください。 oidc_provider_cant_exchange: OpenID Connectプロバイダはトークン交換をサポートしていないようですが、トークン交換はストレージ用に設定されています。 oidc_token_acquisition_failed: OpenID Connectのセットアップでは、必要なオーディエンスが提供されておらず、トークン交換機能も提供されていません。詳しくはドキュメントをご覧ください。 oidc_token_exchange_failed: OpenID Connect ProviderのToken Exchange設定に問題があるようです。設定を確認し、再度お試しください。 @@ -352,7 +356,7 @@ ja: sp_oauth_token_missing: OpenProject は、ユーザーがまだ SharePoint アカウントをリンクしていないため、ユーザーレベルの SharePoint との通信をテストできません。 sp_tenant_id_missing: 構成されたディレクトリ(テナント)IDがSharePointにありません。設定を確認してください。 sp_unexpected_content: Unexpected content found in the SharePoint Document Library. - unknown_error: 接続を検証できませんでした。不明なエラーが発生しました。詳細については、サーバーのログを確認してください。 + unknown_error: 接続を検証できませんでした。不明なエラーが発生しました。詳細についてはサーバーログを確認してください。 label_error: エラー label_failed: 失敗しました label_healthy: 健康的 @@ -360,55 +364,56 @@ ja: label_pending: 保留中 label_skipped: スキップ label_warning: 注意 - no_report: 報告書なし - no_report_description: 今すぐチェックを実行し、このファイル・ストレージの完全な健全性ステータスをレポートする。 + no_report: 利用可能なレポートがありません + no_report_description: 今すぐこのファイルストレージの完全な健康状態レポートを確認します。 open_report: 完全な健康報告を開く project_folders: subtitle: 自動的に管理されるプロジェクトフォルダ - since: '%{datetime}より' + since: '%{datetime} 以降' summary: - failure: いくつかのチェックに失敗し、システムが期待通りに機能しない。 - success: すべての接続とシステムは期待通りに機能している。 - warning: いくつかのチェックは警告を返した。これは予期せぬ動作につながる可能性がある。 - title: 健康状態報告 + failure: いくつかのチェックに失敗し、システムが期待どおりに動作しません。 + success: すべての接続とシステムは期待どおりに動作しています。 + warning: いくつかのチェックが警告を返しました。これは予期しない動作につながる可能性があります。 + synced: 'Last sync: %{datetime}' + title: 健康状態レポート health_email_notifications: description_disabled: 管理者は、重要なアップデートがあった場合、メールでアップデートを受け取ることはできません。 description_enabled: 管理者は、重要なアップデートがあった場合、メールで最新情報を受け取ります。 - error_could_not_be_saved: 電子メール通知の設定を保存できませんでした。もう一度お試しください。 + error_could_not_be_saved: メール通知設定を保存できませんでした。もう一度やり直してください。 title: 管理者にメールで更新する help_texts: - project_folder: プロジェクトフォルダは、このプロジェクトのファイルアップロード用のデフォルトフォルダです。それでも、ユーザーは他の場所にファイルをアップロードすることができます。 - project_folder_bulk: プロジェクトフォルダは、選択したすべてのプロジェクトのファイルアップロード用のデフォルトフォルダです。これは、各プロジェクト設定で個別に変更できます。それでも、ユーザーは他の場所にファイルをアップロードすることができます。 + project_folder: プロジェクトフォルダは、このプロジェクトのファイルアップロードのデフォルトフォルダです。ただし、ユーザーは他の場所にファイルをアップロードすることができます。 + project_folder_bulk: プロジェクトフォルダは、選択したすべてのプロジェクトのファイルアップロードのデフォルトフォルダです。 プロジェクトごとの設定で個別に変更することができますが、ユーザーは別の場所にファイルをアップロードすることもできます。 instructions: - all_available_storages_already_added: 利用可能なすべてのストレージはすでにプロジェクトに追加されている。 - authentication_method: OpenProject とストレージ間のリクエストの認証方法。 - automatic_folder: これにより、このプロジェクトのルート・フォルダーが自動的に作成され、各プロジェクト・メンバーのアクセス権が管理されます。 - empty_project_folder_validation: 続行するには、フォルダの選択が必須です。 - existing_manual_folder: 既存のフォルダをこのプロジェクトのルートフォルダとして指定することができます。ただし、パーミッションは自動的に管理されないため、管理者は関連するユーザーがアクセスできることを手動で確認する必要があります。選択したフォルダは、複数のプロジェクトで使用できます。 - host: https:// を含むストレージのホスト・アドレスを追加してください。255文字以内にしてください。 - managed_project_folders_application_password_caption: '%{provider_type_link}からこの値をコピーして、自動管理フォルダを有効にする。' - name: ユーザーが複数のストレージを区別できるように、ストレージに名前を付ける。 + all_available_storages_already_added: 利用可能なすべてのストレージが既にプロジェクトに追加されています。 + authentication_method: OpenProjectとストレージ間のリクエストは認証されます。 + automatic_folder: これにより、このプロジェクトのルートフォルダが自動的に作成され、各プロジェクトメンバーのアクセス権限が管理されます。 + empty_project_folder_validation: フォルダの選択は必須です。 + existing_manual_folder: このプロジェクトのルートフォルダとして既存のフォルダを指定できます。 ただし、権限は自動的に管理されておらず、管理者は関連するユーザーに手動でアクセス権があることを確認する必要があります。 選択したフォルダは複数のプロジェクトで使用できます。 + host: https://を含むストレージのホストアドレスを追加してください。255文字以内にしてください。 + managed_project_folders_application_password_caption: '%{provider_type_link} からこの値をコピーすることで、自動管理フォルダを有効にします。' + name: ユーザーが複数のストレージを区別できるように、ストレージに名前を付けます。 new_storage: 詳しくは、 %{provider_name} ファイルストレージ統合の設定に関するドキュメントをお読みください。 nextcloud: application_link_text: アプリケーション "Integration OpenProject" - integration: ネクストクラウド管理 / OpenProject + integration: Nextcloudの管理 / OpenProject oauth_configuration: '%{application_link_text} からこれらの値をコピーします。' - provider_configuration: セットアップを行う前に、Nextcloudインスタンスの管理者権限があり、 %{application_link_text} がインストールされていることを確認してください。 - storage_audience: Nextcloud インスタンスが ID プロバイダとの通信に使用するクライアント ID。 - storage_audience_placeholder: 例:ネクストクラウド - token_exchange_scope: トークン交換時に要求するスコープを、それぞれスペースで区切って指定する。 - no_specific_folder: デフォルトでは、ファイルをアップロードすると、各ユーザーは自分のホームフォルダから開始します。 - no_storage_set_up: ファイルストレージはまだ設定されていない。 - not_logged_into_storage: プロジェクトフォルダを選択するには、まずログインしてください。 + provider_configuration: Nextcloudインスタンスに管理権限があり、設定を行う前に %{application_link_text} がインストールされていることを確認してください。 + storage_audience: NextcloudインスタンスがIDプロバイダーと通信するために使用するクライアントID。 + storage_audience_placeholder: 例:nextcloud + token_exchange_scope: トークン交換中に要求されるべきスコープは、それぞれスペースで区切られています。 + no_specific_folder: デフォルトでは、各ユーザーはファイルをアップロードしたときに自分のホームフォルダから開始します。 + no_storage_set_up: まだ設定されているファイルストレージがありません。 + not_logged_into_storage: プロジェクトフォルダを選択するには、最初にログインしてください oauth_application_details: クライアントシークレットの値は、このウィンドウを閉じた後は二度とアクセスできなくなります。これらの値を %{oauth_application_details_link}にコピーしてください。 - oauth_application_details_link_text: NextcloudのOpenProject統合設定 + oauth_application_details_link_text: Nextcloud OpenProjectインテグレーション設定 one_drive: application_link_text: Azure Portal copy_redirect_uri: リダイレクトURIをコピーする documentation_link_text: OneDriveファイルストレージのドキュメント drive_id: '%{drive_id_link_text} の手順に従って、目的のドライブからIDをコピーしてください。' - integration: ワンドライブ - missing_client_id_for_redirect_uri: OAuthの値を入力してURIを生成してください。 + integration: OneDrive + missing_client_id_for_redirect_uri: OAuthの値を入力してURIを生成してください oauth_client_redirect_uri: この値を「リダイレクト URIs」にある新しい Web リダイレクト URI にコピーしてください。 oauth_client_secret: Client 資格情報にアプリケーション クライアント シークレットがない場合は、新しいシークレットを作成してください。 oauth_configuration: '%{application_link_text}、目的のアプリケーションからこれらの値をコピーします。' @@ -480,13 +485,13 @@ ja: login_button_aria_label: '%{storage} にログイン' login_button_label: "%{provider_type} ログイン" project_settings: - description: プロジェクトフォルダにアクセスするには、 %{storage}にログインする必要があります。 + description: プロジェクトフォルダにアクセスするには、 %{storage} にログインする必要があります。 requesting_access_to: '%{storage} へのアクセスをリクエストしています' storage_admin: description: このストレージにプロジェクトを追加するには、 %{provider_type}にログインする必要があります。ログインしてもう一度やり直してください。 open_project_storage_modal: success: - subtitle: リダイレクトされます + subtitle: リダイレクトしています title: 連携のセットアップが完了しました timeout: link_text: ファイルストレージセットアップの状態の状態 @@ -505,8 +510,8 @@ ja: subtitle_short: OpenProjectにプロジェクトごとにフォルダを自動的に作成させます。 title: 自動的に管理されるプロジェクトフォルダ project_settings: - edit: このプロジェクトのファイル・ストレージを編集する - members_connection_status: メンバーの接続状況 + edit: このプロジェクトのファイルストレージを編集 + members_connection_status: 会員の接続状況 new: このプロジェクトにファイルストレージを追加する project_storage_members: subtitle: 全プロジェクトメンバーのストレージ %{storage_name_link} の接続状態を確認する。 @@ -517,14 +522,14 @@ ja: provider_types: label: プロバイダー・タイプ nextcloud: - label_oauth_client_id: NextcloudのOAuthクライアントID - label_oauth_client_secret: NextcloudOAuthクライアントシークレット + label_oauth_client_id: Nextcloud OAuthクライアントID + label_oauth_client_secret: Nextcloud OAuth クライアントシークレット name: ネクストクラウド name_placeholder: 例:ネクストクラウド one_drive: - label_oauth_client_id: Azure OAuthアプリケーション(クライアント)ID + label_oauth_client_id: Azure OAuth アプリケーション (クライアント) ID label_oauth_client_secret: Azure OAuth クライアントの秘密値 - name: ワンドライブ + name: OneDrive name_placeholder: '例: OneDrive' sharepoint: drive_description: OpenProject access-managed document library @@ -534,18 +539,18 @@ ja: name_placeholder: 例:シェアポイント show_attachments_toggle: description: このオプションを無効にすると、ワークパッケージのファイルタブの添付ファイルリストが非表示になります。ワークパッケージの説明に添付されたファイルは、内部添付ファイルストレージにアップロードされます。 - label: ワークパッケージのファイルタブに添付ファイルを表示 + label: ワークパッケージファイルタブに添付ファイルを表示 storage_audience: - documentation_intro: 以下のオプションと ID プロバイダの設定の詳細については、当社のドキュメントをお読みください。 + documentation_intro: アイデンティティプロバイダの以下のオプションと設定については、当社のドキュメントをお読みください。 idp: - helptext: OpenProjectは、ストレージへのリクエストを認証するために、ログイン時にIDプロバイダから受け取ったアクセストークンを使用します。別のトークンを取得しようとすることはありません。 - label: ユーザーログイン時に取得したアクセストークンを使用する + helptext: OpenProjectはログイン中にIDプロバイダーが受け取ったアクセストークンを使用して、ストレージへのリクエストを認証します。 別のトークンを取得しようとしません。 + label: ログイン中に取得したアクセストークンを使用する manual: - helptext: OpenProjectは、指定されたオーディエンスのIDプロバイダとトークンを交換します。 + helptext: OpenProject は、特定のオーディエンスの ID プロバイダーとトークンを交換します。 label: Manually specify audience for which to exchange access token (Recommended) storage_list_blank_slate: - description: ストレージを追加して、ここで見ることができる。 - heading: あなたはまだ倉庫を持っていない。 + description: ここにそれらを見るためにストレージを追加します。 + heading: まだストレージがありません。 successful_storage_connection: ストレージが正常に接続されました! 使用する各プロジェクトの「プロジェクト」タブでストレージをアクティブにすることを忘れないでください。 upsell: one_drive: diff --git a/modules/storages/config/locales/crowdin/js-ja.yml b/modules/storages/config/locales/crowdin/js-ja.yml index 04cb36084f5..35563523140 100644 --- a/modules/storages/config/locales/crowdin/js-ja.yml +++ b/modules/storages/config/locales/crowdin/js-ja.yml @@ -3,14 +3,14 @@ ja: js: storages: authentication_error: "%{storageType} での認証に失敗しました" - link_files_in_storage: "リンクファイル %{storageType}" - link_existing_files: "既存のファイルをリンク" - upload_files: "ファイルのアップロード" + link_files_in_storage: "%{storageType}のファイルをリンクする" + link_existing_files: "既存のファイルをリンクする" + upload_files: "ログファイル" drop_files: "ここにファイルをドロップして、 %{name} にアップロードします。" drop_or_click_files: "ここにファイルをドロップするか、クリックして %{name} にアップロードします。" login: "%{storageType} ログイン" login_to: "%{storageType}にログイン" - no_connection: "%{storageType} 接続がありません" + no_connection: "%{storageType} 接続なし" open_storage: "%{storageType} を開く" select_location: "場所を選択" choose_location: "場所を選ぶ" @@ -24,7 +24,7 @@ ja: authentication_error: "%{storageType} へのリクエストを認証できませんでした。これはエラーです。" connection_error: > %{storageType} の設定が一部機能していません。 %{storageType} 管理者にお問い合わせください。 - live_data_error: "ファイルの詳細の取得に失敗しました" + live_data_error: "ファイル詳細の取得エラー" live_data_error_description: > 一部の %{storageType} データを取得できませんでした。このページを再読み込みするか、 %{storageType} 管理者にお問い合わせください。 no_file_links: "このワークパッケージにファイルをリンクするには、 %{storageType}を使用してください。" @@ -33,7 +33,7 @@ ja: suggest_logout: ログアウトしてログインし直すと、この問題が解決するかどうか試してみてください。 suggest_relink: 以下のログインボタンからアカウントを再リンクすると、この問題が解決するかどうか試してみてください。 files: - already_existing_header: "このファイルはすでに存在する" + already_existing_header: "このファイルは既に存在します" already_existing_body: > このファイルをアップロードしようとしている場所に、"%{fileName}"という名前のファイルがすでに存在します。どうしますか? directory_not_writeable: "このフォルダにファイルを追加する権限がありません。" @@ -41,7 +41,7 @@ ja: dragging_folder: "%{storageType} へのアップロードはフォルダをサポートしていません。" empty_folder: "このフォルダは空です。" empty_folder_location_hint: "下のボタンをクリックして、この場所にファイルをアップロードしてください。" - file_not_selectable_location: "場所を選択する過程でファイルを選択することはできない。" + file_not_selectable_location: "ファイルを選択することは、場所を選択する過程ではできません。" project_folder_no_access: > プロジェクトフォルダにアクセスできません。管理者に連絡してアクセス権を取得するか、別の場所にファイルをアップロードしてください。 managed_project_folder_not_available: > @@ -79,9 +79,9 @@ ja: ファイル (%{fileName}) の容量がストレージ・クォータの許容量を超えています。管理者に連絡して、このクォータを変更してください。 detail: nextcloud: > - 最新版のNextcloudアプリ「OpenProject Integration」がインストールされていることを確認し、管理者にお問い合わせください。 + Nextcloudアプリ「OpenProject統合」の最新バージョンがインストールされていることを確認し、詳細については管理者にお問い合わせください。 link_uploaded_file_error: > - 最近アップロードされたファイル '%{fileName}' をワークパッケージ %{workPackageId}にリンクするエラーが発生しました。 + 最近アップロードされたファイル '%{fileName}' をワークパッケージ %{workPackageId} にリンクしてエラーが発生しました。 tooltip: not_logged_in: "このファイルにアクセスするには、ストレージにログインしてください。" view_not_allowed: "このファイルを閲覧する権限がありません。" diff --git a/modules/storages/config/locales/crowdin/ka.yml b/modules/storages/config/locales/crowdin/ka.yml index c883721ba5a..c5fbcf72786 100644 --- a/modules/storages/config/locales/crowdin/ka.yml +++ b/modules/storages/config/locales/crowdin/ka.yml @@ -279,6 +279,8 @@ ka: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ ka: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ ka: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/kk.yml b/modules/storages/config/locales/crowdin/kk.yml index 531e32cca16..44e49d37233 100644 --- a/modules/storages/config/locales/crowdin/kk.yml +++ b/modules/storages/config/locales/crowdin/kk.yml @@ -279,6 +279,8 @@ kk: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ kk: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ kk: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ko.yml b/modules/storages/config/locales/crowdin/ko.yml index 3f79855017b..5740e8dfe55 100644 --- a/modules/storages/config/locales/crowdin/ko.yml +++ b/modules/storages/config/locales/crowdin/ko.yml @@ -279,6 +279,8 @@ ko: drive_contents: 드라이브 콘텐츠 files_request: 팀 폴더 파일 가져오기 header: 자동으로 관리되는 프로젝트 폴더 + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: '종속성: 팀 폴더' team_folder_contents: 팀 폴더 콘텐츠 team_folder_presence: 팀 폴더가 존재합니다 @@ -320,8 +322,10 @@ ko: nc_oauth_request_not_found: 현재 연결된 사용자를 가져올 엔드포인트를 찾을 수 없습니다. 자세한 내용은 서버 로그를 확인하세요. nc_oauth_request_unauthorized: 현재 사용자는 원격 파일 저장소에 액세스할 수 있는 권한이 없습니다. 자세한 내용은 서버 로그를 확인하세요. nc_oauth_token_missing: 사용자가 아직 Nextcloud 계정을 링크하지 않았기 때문에 OpenProject가 Nextcloud와의 사용자 수준 통신을 테스트할 수 없습니다. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: 팀 폴더를 찾을 수 없습니다. - nc_unexpected_content: 관리되는 팀 폴더에서 예기치 않은 콘텐츠가 발견되었습니다. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: 구성된 앱 암호가 잘못되었습니다. not_configured: 연결에 대한 유효성 검사를 할 수 없습니다. 먼저 구성을 완료하세요. od_client_cant_delete_folder: 클라이언트에서 폴더를 삭제하는 중에 문제가 발생했습니다. 저장소에 대한 설정 설명서를 확인하세요. @@ -370,6 +374,7 @@ ko: failure: 일부 검사가 실패하여 시스템이 예상대로 작동되지 않습니다. success: 모든 연결과 시스템이 예상대로 작동 중입니다. warning: 일부 검사에서 경고가 반환되었습니다. 이로 인해 예상치 못한 동작이 발생할 수 있습니다. + synced: 'Last sync: %{datetime}' title: 상태 보고서 health_email_notifications: description_disabled: 중요 업데이트가 있을 때 관리자에게 이메일로 업데이트가 전송되지 않습니다. diff --git a/modules/storages/config/locales/crowdin/lt.yml b/modules/storages/config/locales/crowdin/lt.yml index 2e3b9618829..8d2fffc5287 100644 --- a/modules/storages/config/locales/crowdin/lt.yml +++ b/modules/storages/config/locales/crowdin/lt.yml @@ -279,6 +279,8 @@ lt: drive_contents: Drive content files_request: Fetching team folder files header: Automatiškai valdomi projekto aplankai + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -326,8 +328,10 @@ lt: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -376,6 +380,7 @@ lt: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/lv.yml b/modules/storages/config/locales/crowdin/lv.yml index 093dec25b5c..df157562a29 100644 --- a/modules/storages/config/locales/crowdin/lv.yml +++ b/modules/storages/config/locales/crowdin/lv.yml @@ -279,6 +279,8 @@ lv: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -324,8 +326,10 @@ lv: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -374,6 +378,7 @@ lv: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/mn.yml b/modules/storages/config/locales/crowdin/mn.yml index 48e64819cfd..500878f5d56 100644 --- a/modules/storages/config/locales/crowdin/mn.yml +++ b/modules/storages/config/locales/crowdin/mn.yml @@ -279,6 +279,8 @@ mn: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ mn: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ mn: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ms.yml b/modules/storages/config/locales/crowdin/ms.yml index e1eee9dc10f..07c68083cb2 100644 --- a/modules/storages/config/locales/crowdin/ms.yml +++ b/modules/storages/config/locales/crowdin/ms.yml @@ -279,6 +279,8 @@ ms: drive_contents: Drive content files_request: Fetching team folder files header: Folder projek yang dikendalikan secara automatik + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -320,8 +322,10 @@ ms: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Kata laluan apl yang dikonfigurasikan adalah tidak sah. not_configured: Sambungan tidak dapat disahkan. Sila selesaikan konfigurasi dahulu. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -370,6 +374,7 @@ ms: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ne.yml b/modules/storages/config/locales/crowdin/ne.yml index 94d3120d062..12e1b811827 100644 --- a/modules/storages/config/locales/crowdin/ne.yml +++ b/modules/storages/config/locales/crowdin/ne.yml @@ -279,6 +279,8 @@ ne: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ ne: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ ne: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/nl.yml b/modules/storages/config/locales/crowdin/nl.yml index 3b6609aea4f..53118fc8189 100644 --- a/modules/storages/config/locales/crowdin/nl.yml +++ b/modules/storages/config/locales/crowdin/nl.yml @@ -279,6 +279,8 @@ nl: drive_contents: Drive content files_request: Fetching team folder files header: Automatisch beheerde projectmappen + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ nl: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: De verbinding kon niet gevalideerd worden. Voltooi eerst de configuratie. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ nl: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/no.yml b/modules/storages/config/locales/crowdin/no.yml index d661f246c74..d36b1352d19 100644 --- a/modules/storages/config/locales/crowdin/no.yml +++ b/modules/storages/config/locales/crowdin/no.yml @@ -279,6 +279,8 @@ drive_contents: Drive content files_request: Fetching team folder files header: Automatisk administrerte prosjektmapper + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/pl.yml b/modules/storages/config/locales/crowdin/pl.yml index 340b15a72d4..85cc52c569b 100644 --- a/modules/storages/config/locales/crowdin/pl.yml +++ b/modules/storages/config/locales/crowdin/pl.yml @@ -279,6 +279,8 @@ pl: drive_contents: Zawartość dysku files_request: Pobieranie plików folderu zespołu header: Automatycznie zarządzane foldery projektu + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Zależność: foldery zespołów' team_folder_contents: Zawartość folderu zespołu team_folder_presence: Folder zespołu istnieje @@ -326,8 +328,10 @@ pl: nc_oauth_request_not_found: Nie znaleziono punktu końcowego, z którego można pobrać informacje o aktualnie połączonym użytkowniku. Aby uzyskać więcej informacji, sprawdź dzienniki serwera. nc_oauth_request_unauthorized: Bieżący użytkownik nie ma uprawnień dostępu do zdalnego magazynu plików. Aby uzyskać więcej informacji, sprawdź dzienniki serwera. nc_oauth_token_missing: Aplikacja OpenProject nie może przetestować komunikacji z usługą Nextcloud na poziomie użytkownika, ponieważ użytkownik nie powiązał jeszcze swojego konta Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Nie można znaleźć folderu zespołu. - nc_unexpected_content: W folderze zarządzanego zespołu znaleziono nieoczekiwaną zawartość. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Skonfigurowane hasło aplikacji jest nieprawidłowe. not_configured: Nie można zweryfikować połączenia. Najpierw zakończ konfigurację. od_client_cant_delete_folder: Klient ma problemy z usunięciem folderów. Sprawdź dokumentację konfiguracji swojej pamięci masowej. @@ -376,6 +380,7 @@ pl: failure: Niektóre kontrole nie powiodły się i system nie działa zgodnie z oczekiwaniami. success: Wszystkie połączenia i systemy działają zgodnie z oczekiwaniami. warning: Niektóre kontrole zwróciły ostrzeżenie. Może to prowadzić do nieoczekiwanego sposobu działania. + synced: 'Last sync: %{datetime}' title: Raport o stanie health_email_notifications: description_disabled: Administratorzy nie będą otrzymywać aktualizacji pocztą elektroniczną, gdy pojawią się ważne aktualizacje. diff --git a/modules/storages/config/locales/crowdin/pt-BR.yml b/modules/storages/config/locales/crowdin/pt-BR.yml index 89796811996..c23f5735a9b 100644 --- a/modules/storages/config/locales/crowdin/pt-BR.yml +++ b/modules/storages/config/locales/crowdin/pt-BR.yml @@ -279,6 +279,8 @@ pt-BR: drive_contents: Conteúdo da unidade files_request: Buscando arquivos da pasta da equipe header: Pastas de projeto gerenciadas automaticamente + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependência: Pastas da equipe' team_folder_contents: Conteúdo da pasta da equipe team_folder_presence: A pasta da equipe existe @@ -322,8 +324,10 @@ pt-BR: nc_oauth_request_not_found: O endpoint para recuperar o usuário conectado não foi encontrado. Verifique os logs do servidor para mais detalhes. nc_oauth_request_unauthorized: O usuário atual não está autorizado a acessar o armazenamento remoto de arquivos. Verifique os logs do servidor para mais informações. nc_oauth_token_missing: O OpenProject não pode testar a comunicação do usuário com o Nextcloud, pois a conta do Nextcloud ainda não foi vinculada. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: A pasta da equipe não pôde ser localizada. - nc_unexpected_content: Conteúdo inesperado encontrado na pasta da equipe gerenciado. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: A senha configurada do aplicativo não é válida. not_configured: A conexão não pôde ser validada. Conclua a configuração primeiro. od_client_cant_delete_folder: O cliente está com dificuldades para excluir pastas. Verifique a documentação de configuração do seu armazenamento. @@ -372,6 +376,7 @@ pt-BR: failure: Alguns testes não passaram e o sistema não está funcionando corretamente. success: Todas as conexões e sistemas estão operando normalmente. warning: Alguns testes retornaram um alerta. Isso pode causar comportamentos inesperados. + synced: 'Last sync: %{datetime}' title: Relatório de status de integridade health_email_notifications: description_disabled: Os administradores não receberão atualizações por e-mail quando houver atualizações importantes. diff --git a/modules/storages/config/locales/crowdin/pt-PT.yml b/modules/storages/config/locales/crowdin/pt-PT.yml index c19fcc03323..e6c2356933a 100644 --- a/modules/storages/config/locales/crowdin/pt-PT.yml +++ b/modules/storages/config/locales/crowdin/pt-PT.yml @@ -279,6 +279,8 @@ pt-PT: drive_contents: Conteúdo da unidade files_request: A obter ficheiros da pasta de equipa header: Pastas do projeto geridas automaticamente + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependência: Pastas de equipa' team_folder_contents: Conteúdo da pasta de equipa team_folder_presence: A pasta de equipa existe @@ -322,8 +324,10 @@ pt-PT: nc_oauth_request_not_found: O terminal para o utilizador com sessão iniciada não foi encontrado. Consulte os registos do servidor para mais informações. nc_oauth_request_unauthorized: O utilizador atual não está autorizado a aceder ao armazenamento de ficheiros remoto. Verifique os registos do servidor para mais informações. nc_oauth_token_missing: O OpenProject não pode testar a comunicação de nível de utilizador com o Nextcloud uma vez que o utilizador ainda não associou a sua conta Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Não foi possível encontrar a pasta de equipa. - nc_unexpected_content: Conteúdo inesperado encontrado na pasta de equipa gerida. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: A palavra-passe da aplicação configurada é inválida. not_configured: Não foi possível validar a ligação.Termine, primeiro, a configuração. od_client_cant_delete_folder: O cliente está a ter problemas a eliminar pastas. Consulte a documentação de configuração para o seu armazenamento. @@ -372,6 +376,7 @@ pt-PT: failure: Algumas verificações não foram aprovadas e o sistema não funciona como esperado. success: Todas as ligações e sistemas estão a funcionar como esperado. warning: Algumas verificações foram devolvidas com um aviso. Isso pode resultar num comportamento inesperado. + synced: 'Last sync: %{datetime}' title: Relatório de estado de saúde health_email_notifications: description_disabled: Os administradores não receberão atualizações por e-mail quando houver atualizações importantes. diff --git a/modules/storages/config/locales/crowdin/ro.yml b/modules/storages/config/locales/crowdin/ro.yml index 80cff04813f..f28909c0e97 100644 --- a/modules/storages/config/locales/crowdin/ro.yml +++ b/modules/storages/config/locales/crowdin/ro.yml @@ -279,6 +279,8 @@ ro: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -324,8 +326,10 @@ ro: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: Utilizatorul curent nu este autorizat să acceseze spațiul de stocare de la distanță. Te rog să verifici jurnalele serverului pentru informații suplimentare. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -374,6 +378,7 @@ ro: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Raport privind starea de sănătate health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/ru.yml b/modules/storages/config/locales/crowdin/ru.yml index 1cc7b7c77f9..7e8a96aecee 100644 --- a/modules/storages/config/locales/crowdin/ru.yml +++ b/modules/storages/config/locales/crowdin/ru.yml @@ -279,6 +279,8 @@ ru: drive_contents: Содержимое диска files_request: Получение файлов папки команды header: Автоматически управляемые папки проекта + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Зависимость: папки команды' team_folder_contents: Содержимое папки команды team_folder_presence: Папка команды существует @@ -326,8 +328,10 @@ ru: nc_oauth_request_not_found: Конечная точка для получения данных о текущем подключенном пользователе не найдена. Пожалуйста, проверьте журналы сервера для получения дополнительной информации. nc_oauth_request_unauthorized: Текущий пользователь не авторизован для доступа к удаленному файловому хранилищу. Пожалуйста, проверьте журналы сервера для получения дополнительной информации. nc_oauth_token_missing: OpenProject не может протестировать пользовательскую связь с Nextcloud, так как пользователь еще не связал свою учетную запись Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Папка команды не найдена. - nc_unexpected_content: Непредвиденное содержимое найдено в папке команды. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Указанный пароль приложения неверен. not_configured: Соединение не удалось подтвердить. Пожалуйста, сначала завершите настройку. od_client_cant_delete_folder: У клиента возникли проблемы с удалением папок. Пожалуйста, проверьте документацию по конфигурации для вашего хранилища. @@ -376,6 +380,7 @@ ru: failure: Некоторые проверки не удались, и система работает не так, как ожидалось. success: Все соединения и системы работают, как и ожидалось. warning: Некоторые проверки вернули предупреждение. Это может привести к неожиданному поведению. + synced: 'Last sync: %{datetime}' title: Отчёт о состоянии хранилища health_email_notifications: description_disabled: Администраторы не будут получать обновления по электронной почте, когда выходят важные обновления. diff --git a/modules/storages/config/locales/crowdin/rw.yml b/modules/storages/config/locales/crowdin/rw.yml index 77291057820..1ec973eee78 100644 --- a/modules/storages/config/locales/crowdin/rw.yml +++ b/modules/storages/config/locales/crowdin/rw.yml @@ -279,6 +279,8 @@ rw: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ rw: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ rw: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/si.yml b/modules/storages/config/locales/crowdin/si.yml index 921673d4932..daca008ee39 100644 --- a/modules/storages/config/locales/crowdin/si.yml +++ b/modules/storages/config/locales/crowdin/si.yml @@ -279,6 +279,8 @@ si: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ si: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ si: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/sk.yml b/modules/storages/config/locales/crowdin/sk.yml index d5a202cefc8..f3d4b5a32ab 100644 --- a/modules/storages/config/locales/crowdin/sk.yml +++ b/modules/storages/config/locales/crowdin/sk.yml @@ -279,6 +279,8 @@ sk: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -326,8 +328,10 @@ sk: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -376,6 +380,7 @@ sk: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/sl.yml b/modules/storages/config/locales/crowdin/sl.yml index 1e5c6b58768..1b460882ece 100644 --- a/modules/storages/config/locales/crowdin/sl.yml +++ b/modules/storages/config/locales/crowdin/sl.yml @@ -279,6 +279,8 @@ sl: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -326,8 +328,10 @@ sl: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -376,6 +380,7 @@ sl: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/sr.yml b/modules/storages/config/locales/crowdin/sr.yml index 8976b18ec45..304c7d4510e 100644 --- a/modules/storages/config/locales/crowdin/sr.yml +++ b/modules/storages/config/locales/crowdin/sr.yml @@ -279,6 +279,8 @@ sr: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -324,8 +326,10 @@ sr: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -374,6 +378,7 @@ sr: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/sv.yml b/modules/storages/config/locales/crowdin/sv.yml index 20189052160..fa3373bca6d 100644 --- a/modules/storages/config/locales/crowdin/sv.yml +++ b/modules/storages/config/locales/crowdin/sv.yml @@ -279,6 +279,8 @@ sv: drive_contents: Diskinnehåll files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ sv: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ sv: failure: Vissa kontroller misslyckades och systemet fungerar inte som förväntat. success: Alla anslutningar och system fungerar som förväntat. warning: Vissa kontroller gav tillbaka en varning. Detta kan leda till oväntat beteende. + synced: 'Last sync: %{datetime}' title: Rapport om hälsostatus health_email_notifications: description_disabled: Administratörer kommer inte att få uppdateringar via e-post när det finns viktiga uppdateringar. diff --git a/modules/storages/config/locales/crowdin/th.yml b/modules/storages/config/locales/crowdin/th.yml index e8840da033c..87735d3236c 100644 --- a/modules/storages/config/locales/crowdin/th.yml +++ b/modules/storages/config/locales/crowdin/th.yml @@ -279,6 +279,8 @@ th: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -320,8 +322,10 @@ th: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -370,6 +374,7 @@ th: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/tr.yml b/modules/storages/config/locales/crowdin/tr.yml index ec1eb6a9f7f..723f543f3c3 100644 --- a/modules/storages/config/locales/crowdin/tr.yml +++ b/modules/storages/config/locales/crowdin/tr.yml @@ -279,6 +279,8 @@ tr: drive_contents: Sücürü içeriği files_request: Takım klasöründeki dosyaları getir header: Otomatik olarak yönetilen proje klasörleri + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Bağımlılık: Ekip Klasörleri' team_folder_contents: Ekip klasörü içeriği team_folder_presence: Ekip klasörü mevcut @@ -322,8 +324,10 @@ tr: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Ekip klasörü bulunamadı. - nc_unexpected_content: Yönetilen ekip klasöründe beklenmeyen içerik bulundu. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Yapılandırılan uygulama parolası geçersiz. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ tr: failure: Bazı kontroller başarısız oldu ve sistem beklendiği gibi çalışmıyor. success: Tüm bağlantılar ve sistemler beklendiği gibi çalışıyor. warning: Bazı kontroller bir uyarı döndürdü. Bu beklenmedik davranışlara yol açabilir. + synced: 'Last sync: %{datetime}' title: Sağlık durumu raporu health_email_notifications: description_disabled: Önemli güncellemeler olduğunda yöneticiler e-posta ile güncelleme almayacaktır. diff --git a/modules/storages/config/locales/crowdin/uk.yml b/modules/storages/config/locales/crowdin/uk.yml index 9a8cd28b187..8a7822561e2 100644 --- a/modules/storages/config/locales/crowdin/uk.yml +++ b/modules/storages/config/locales/crowdin/uk.yml @@ -279,6 +279,8 @@ uk: drive_contents: Вміст диска files_request: Отримання файлів із папок команди header: Папки проєкту з автоматичним керуванням + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Залежність: папки команди' team_folder_contents: Вміст папки команди team_folder_presence: Папка команди існує @@ -326,8 +328,10 @@ uk: nc_oauth_request_not_found: Кінцеву точку для отримання підключеного зараз користувача не знайдено. Щоб дізнатися більше, перевірте журнали сервера. nc_oauth_request_unauthorized: Поточний користувач не має дозволу на доступ до віддаленого файлового сховища. Щоб дізнатися більше, перевірте журнали сервера. nc_oauth_token_missing: OpenProject не може перевірити з’єднання на рівні користувача з Nextcloud, оскільки користувач досі не зв’язав свій обліковий запис Nextcloud. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Папку команди не знайдено. - nc_unexpected_content: У папці керованої команди знайдено неочікуваний вміст. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Налаштований пароль застосунку недійсний. not_configured: Не вдалося перевірити підключення. Спочатку налаштуйте конфігурацію. od_client_cant_delete_folder: Клієнту не вдалося видалити папки. Ознайомтеся з документацією щодо конфігурації для свого сховища. @@ -376,6 +380,7 @@ uk: failure: Деякі перевірки не пройдено, і система працює неналежним чином. success: Усі підключення й системи працють належним чином. warning: Деякі перевірки завершилися з попередженням. Це може призвести до неочікуваної поведінки. + synced: 'Last sync: %{datetime}' title: Звіт про стан справності health_email_notifications: description_disabled: Адміністратори не отримуватимуть електронні листи про важливі оновлення. diff --git a/modules/storages/config/locales/crowdin/uz.yml b/modules/storages/config/locales/crowdin/uz.yml index e9dd7686422..4eae84ecff5 100644 --- a/modules/storages/config/locales/crowdin/uz.yml +++ b/modules/storages/config/locales/crowdin/uz.yml @@ -279,6 +279,8 @@ uz: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -322,8 +324,10 @@ uz: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -372,6 +376,7 @@ uz: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/config/locales/crowdin/vi.yml b/modules/storages/config/locales/crowdin/vi.yml index 215249d05c8..597521e54c3 100644 --- a/modules/storages/config/locales/crowdin/vi.yml +++ b/modules/storages/config/locales/crowdin/vi.yml @@ -279,6 +279,8 @@ vi: drive_contents: Nội dung ổ đĩa files_request: Tải xuống các tệp trong thư mục của nhóm header: Thư mục dự án được quản lý tự động + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Phụ thuộc: Thư mục nhóm' team_folder_contents: Nội dung thư mục của nhóm team_folder_presence: Thư mục nhóm đã tồn tại @@ -320,8 +322,10 @@ vi: nc_oauth_request_not_found: Không tìm thấy điểm cuối để tìm nạp người dùng hiện được kết nối. Vui lòng kiểm tra nhật ký máy chủ để biết thêm thông tin. nc_oauth_request_unauthorized: Người dùng hiện tại không được phép truy cập vào bộ lưu trữ tệp từ xa. Vui lòng kiểm tra nhật ký máy chủ để biết thêm thông tin. nc_oauth_token_missing: OpenProject không thể kiểm tra giao tiếp ở cấp độ người dùng với Nextcloud vì người dùng chưa liên kết tài khoản Nextcloud của họ. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: Thư mục nhóm không thể được tìm thấy. - nc_unexpected_content: Nội dung không mong đợi được tìm thấy trong thư mục của nhóm được quản lý. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: Mật khẩu ứng dụng đã định cấu hình không hợp lệ. not_configured: Kết nối không thể được xác nhận. Vui lòng hoàn tất cấu hình trước. od_client_cant_delete_folder: Máy khách đang gặp sự cố khi xóa các thư mục. Vui lòng kiểm tra tài liệu thiết lập cho bộ nhớ của bạn. @@ -370,6 +374,7 @@ vi: failure: Một số lần kiểm tra không thành công và hệ thống không hoạt động như mong đợi. success: Tất cả các kết nối và hệ thống đều hoạt động như mong đợi. warning: Một số kiểm tra trả lại một cảnh báo. Điều này có thể dẫn đến hành vi không mong muốn. + synced: 'Last sync: %{datetime}' title: Báo cáo tình trạng sức khoẻ health_email_notifications: description_disabled: Quản trị viên sẽ không nhận được thông tin cập nhật qua email khi có thông tin cập nhật quan trọng. diff --git a/modules/storages/config/locales/crowdin/zh-CN.yml b/modules/storages/config/locales/crowdin/zh-CN.yml index e227630b5e2..3f3410abbab 100644 --- a/modules/storages/config/locales/crowdin/zh-CN.yml +++ b/modules/storages/config/locales/crowdin/zh-CN.yml @@ -279,6 +279,8 @@ zh-CN: drive_contents: 驱动器内容 files_request: 正在获取团队文件夹文件 header: 自动托管的项目文件夹 + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: '依赖关系:团队文件夹' team_folder_contents: 团队文件夹内容 team_folder_presence: 团队文件夹已存在 @@ -320,8 +322,10 @@ zh-CN: nc_oauth_request_not_found: 未找到获取当前连接用户的端点。请检查服务器日志以获取更多信息。 nc_oauth_request_unauthorized: 当前用户无权访问远程文件存储。请检查服务器日志以获取更多信息。 nc_oauth_token_missing: OpenProject 无法测试用户与 Nextcloud 之间的通信,因为用户尚未链接他们的 Nextcloud 帐户。 + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: 找不到该团队文件夹。 - nc_unexpected_content: 在受管理的团队文件夹中找到非预期内容。 + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: 已配置的应用密码无效。 not_configured: 无法验证连接。请先完成配置。 od_client_cant_delete_folder: 客户端在删除文件夹时遇到问题。请检查您的存储的设置文档。 @@ -370,6 +374,7 @@ zh-CN: failure: 有些检查失败,系统无法按预期运行。 success: 所有连接和系统都按预期运行。 warning: 某些检查返回了警告。这可能导致异常行为。 + synced: 'Last sync: %{datetime}' title: 健康状况报告 health_email_notifications: description_disabled: 当有重要更新时,管理员将不会通过电子邮件收到更新提醒。 diff --git a/modules/storages/config/locales/crowdin/zh-TW.yml b/modules/storages/config/locales/crowdin/zh-TW.yml index c2e5cb16b0f..78788b4ad73 100644 --- a/modules/storages/config/locales/crowdin/zh-TW.yml +++ b/modules/storages/config/locales/crowdin/zh-TW.yml @@ -279,6 +279,8 @@ zh-TW: drive_contents: 磁碟內容 files_request: 擷取團隊資料夾檔案 header: 自動管理的專案資料夾 + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: '依賴性:團隊文件夾' team_folder_contents: 團隊資料夾內容 team_folder_presence: 團隊資料夾存在 @@ -320,8 +322,10 @@ zh-TW: nc_oauth_request_not_found: 無法找到取得目前連線使用者的端點。請檢查伺服器日誌以取得更多資訊。 nc_oauth_request_unauthorized: 當前使用者未獲授權存取遠端檔案儲存空間。請檢查伺服器日誌以取得進一步資訊。 nc_oauth_token_missing: OpenProject 無法測試與 Nextcloud 的用戶級連線,因為沒有當前用戶的令牌(Token)。 + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: 找不到團隊資料夾。 - nc_unexpected_content: 在管理的團隊資料夾中發現意外內容。 + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: 設定的應用程式密碼無效。 not_configured: 連線無法驗證。請先完成設定。 od_client_cant_delete_folder: 用戶刪除資料夾失敗,請檢查儲存空間的文件設定。 @@ -370,6 +374,7 @@ zh-TW: failure: 部分檢查失敗,系統無法如預期運作。 success: 所有連線與系統均如預期運作。 warning: 某些檢查返回警告,可能會引發預期外的行為。 + synced: 'Last sync: %{datetime}' title: 健康狀況報告 health_email_notifications: description_disabled: 當有重要更新時,管理員將不會透過電子郵件收到更新。 diff --git a/modules/storages/config/locales/en.yml b/modules/storages/config/locales/en.yml index 495e3d32266..aa907a61bfc 100644 --- a/modules/storages/config/locales/en.yml +++ b/modules/storages/config/locales/en.yml @@ -280,6 +280,8 @@ en: drive_contents: Drive content files_request: Fetching team folder files header: Automatically managed project folders + project_folders_exist: Project folders exist + project_folders_linked: Project folders linked team_folder_app: 'Dependency: Team Folders' team_folder_contents: Team folder content team_folder_presence: Team folder exists @@ -323,8 +325,10 @@ en: nc_oauth_request_not_found: The endpoint to fetch the currently connected user was not found. Please check the server logs for further information. nc_oauth_request_unauthorized: The current user isn't authorized to access the remote file storage. Please check the server logs for further information. nc_oauth_token_missing: OpenProject cannot test the user level communication with Nextcloud as the user did not yet link their Nextcloud account. + nc_project_folder_missing: The previously created project folder for project "%{project}" could not be found. nc_team_folder_not_found: The team folder could not be found. - nc_unexpected_content: Unexpected content found in the managed team folder. + nc_unexpected_files: 'Unexpected files found in the managed team folder. For example: %{sample}' + nc_unlinked_project_folders: Not all project folders have been created yet (%{actual} / %{expected}). This can indicate errors during the AMPF background synchronization. nc_userless_access_denied: The configured app password is invalid. not_configured: The connection could not be validated. Please finish configuration first. od_client_cant_delete_folder: The client is having trouble deleting folders. Please check the setup documentation for your storage. @@ -373,6 +377,7 @@ en: failure: Some checks failed and the system does not work as expected. success: All connections and systems are working as expected. warning: Some checks returned a warning. This can lead to unexpected behaviour. + synced: 'Last sync: %{datetime}' title: Health status report health_email_notifications: description_disabled: Admins will not receive updates by email when there are important updates. diff --git a/modules/storages/spec/common/storages/adapters/authentication_strategies/bearer_token_spec.rb b/modules/storages/spec/common/storages/adapters/authentication_strategies/bearer_token_spec.rb index 5dbea598a83..b7abbebd15c 100644 --- a/modules/storages/spec/common/storages/adapters/authentication_strategies/bearer_token_spec.rb +++ b/modules/storages/spec/common/storages/adapters/authentication_strategies/bearer_token_spec.rb @@ -38,13 +38,14 @@ module Storages let(:storage) { create(:nextcloud_storage) } let(:access_token) { "my_access_token" } - it "must yield with passed access token without" do + it "must yield with the passed access token" do strategy = Input::Strategy.build(key: :bearer_token, token: access_token) was_yielded = false Authentication[strategy].call(storage:) do |http| was_yielded = true - expect(http.instance_variable_get(:@options).headers["authorization"]).to eq("Bearer #{access_token}") + expect(http.instance_variable_get(:@options).auth_header_type).to eq("Bearer") + expect(http.instance_variable_get(:@options).auth_header_value).to eq(access_token) end expect(was_yielded).to be_truthy diff --git a/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_client_credentials_spec.rb b/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_client_credentials_spec.rb index 0ac4624a754..42d78317082 100644 --- a/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_client_credentials_spec.rb +++ b/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_client_credentials_spec.rb @@ -43,7 +43,7 @@ module Storages context "with valid oauth credentials", vcr: "auth/one_drive/client_credentials" do it "return success" do - result = Authentication[strategy_data].call(storage:, http_options: {}) { |http| make_request(http) } + result = Authentication[strategy_data].call(storage:) { make_request(it) } expect(result).to be_success expect(result.value!).to eq("EXPECTED_RESULT") @@ -51,9 +51,7 @@ module Storages it "caches the token if use_cache is true" do strategy_data = Input::Strategy.build(key: :oauth_client_credentials, use_cache: true) - Authentication[strategy_data].call(storage:, http_options: {}) do |http| - make_request(http) - end + Authentication[strategy_data].call(storage:) { make_request(it) } cache_key = described_class::TOKEN_CACHE_KEY % storage.id expect(Rails.cache.read(cache_key)).not_to be_nil @@ -62,7 +60,7 @@ module Storages context "with invalid client secret", vcr: "auth/one_drive/client_credentials_invalid_client_secret" do it "must return unauthorized" do - result = Authentication[strategy_data].call(storage:) { |http| make_request(http) } + result = Authentication[strategy_data].call(storage:) { make_request(it) } expect(result).to be_failure error = result.failure @@ -73,7 +71,7 @@ module Storages context "with invalid client id", vcr: "auth/one_drive/client_credentials_invalid_client_id" do it "must return unauthorized" do - result = Authentication[strategy_data].call(storage:) { |http| make_request(http) } + result = Authentication[strategy_data].call(storage:) { make_request(it) } expect(result).to be_failure error = result.failure @@ -84,7 +82,9 @@ module Storages private - def make_request(http) = handle_response(http.get(request_url)) + def make_request(http) + handle_response(http.get(request_url)) + end def handle_response(response) case response diff --git a/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_user_token_spec.rb b/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_user_token_spec.rb index 0742fa87b32..68ac775cfaa 100644 --- a/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_user_token_spec.rb +++ b/modules/storages/spec/common/storages/adapters/authentication_strategies/oauth_user_token_spec.rb @@ -98,7 +98,7 @@ module Storages allow(Rails.logger).to receive(:error) allow(strategy).to receive(:current_token).and_return(Success(token)) - allow(token).to receive(:destroy).and_raise(ActiveRecord::StaleObjectError).twice + allow(token).to receive(:destroy!).and_raise(ActiveRecord::StaleObjectError).twice expect do strategy.call(storage:, http_options:) { |http| make_request(http) } @@ -109,68 +109,16 @@ module Storages end context "with invalid oauth access token" do - it "must refresh token and return success" do - storage - token = OAuthClientToken.last - user_request_stub_1 = stub_request(:get, "https://nextcloud.local/ocs/v1.php/cloud/user") - .with( - headers: { - "Accept" => "*/*", - "Accept-Encoding" => "gzip, deflate", - "Authorization" => "Bearer #{token.access_token}", - "User-Agent" => /OpenProject \d+\.\d+\.\d+ HTTPX Client/ - } - ) - .to_return(status: 401, body: <<~XML, headers: {}) - - - - failure - 997 - Current user is not logged in - - - - - - XML - user_request_stub_2 = stub_request(:get, "https://nextcloud.local/ocs/v1.php/cloud/user") - .with( - headers: { - "Accept" => "*/*", - "Accept-Encoding" => "gzip, deflate", - "Authorization" => "Bearer NEW_ACCESS_TOKEN", - "User-Agent" => /OpenProject \d+\.\d+\.\d+ HTTPX Client/ - } - ) - .to_return(status: 200, body: <<~JSON, headers: { "Content-Type" => "application/json; charset=utf-8" }) - {"ocs":{"meta":{"status":"ok","statuscode":100,"message":"OK","totalitems":"","itemsperpage":""},"data":{"enabled":true,"storageLocation":"/var/www/html/data/admin","id":"admin","lastLogin":1709888213000,"backend":"Database","subadmin":[],"quota":{"free":962269761536,"used":1137306515,"total":963407068051,"relative":0.12,"quota":-3},"manager":"","avatarScope":"v2-federated","email":null,"emailScope":"v2-federated","additional_mail":[],"additional_mailScope":[],"displayname":"admin","display-name":"admin","displaynameScope":"v2-federated","phone":"","phoneScope":"v2-local","address":"","addressScope":"v2-local","website":"","websiteScope":"v2-local","twitter":"","twitterScope":"v2-local","fediverse":"","fediverseScope":"v2-local","organisation":"","organisationScope":"v2-local","role":"","roleScope":"v2-local","headline":"","headlineScope":"v2-local","biography":"","biographyScope":"v2-local","profile_enabled":"1","profile_enabledScope":"v2-local","groups":["admin"],"language":"en","locale":"","notify_email":null,"backendCapabilities":{"setDisplayName":true,"setPassword":true}}}} - JSON - token_request_stub = stub_request(:post, "https://nextcloud.local/index.php/apps/oauth2/api/v1/token") - .with( - body: { "client_id" => token.oauth_client.client_id, - "client_secret" => token.oauth_client.client_secret, - "grant_type" => "refresh_token", - "refresh_token" => token.refresh_token, - "scope" => "" }, - headers: { - "Accept" => "*/*", - "Accept-Encoding" => "gzip, deflate", - "Content-Type" => "application/x-www-form-urlencoded", - "User-Agent" => /OpenProject \d+\.\d+\.\d+ HTTPX Client/ - } - ) - .to_return(status: 200, body: <<~JSON, headers: { "Content-Type" => "application/json; charset=utf-8" }) - {"access_token":"NEW_ACCESS_TOKEN","token_type":"Bearer","expires_in":3600,"refresh_token":"NEW_REFRESH_TOKEN","user_id":"admin"} - JSON - result = Authentication[strategy_data].call(storage:) { |http| make_request(http) } + it "must refresh token and return success", vcr: "auth/nextcloud/refresh_token" do + token = OAuthClientToken.where(oauth_client_id: storage.oauth_client.id).last + original_access_token = token&.access_token + token&.update!(access_token: "NOT_A_VALID_TOKEN") - expect(user_request_stub_1).to have_been_made.once - expect(user_request_stub_2).to have_been_made.once - expect(token_request_stub).to have_been_made.once + result = Authentication[strategy_data].call(storage:) { |http| make_request(http) } expect(result).to be_success expect(result.value!).to eq("EXPECTED_RESULT") + expect(original_access_token).not_to eq(token&.reload&.access_token) end end diff --git a/modules/storages/spec/common/storages/adapters/providers/nextcloud/contracts/storage_contract_spec.rb b/modules/storages/spec/common/storages/adapters/providers/nextcloud/contracts/storage_contract_spec.rb index ae89c97cdf5..6a9cb9398bd 100644 --- a/modules/storages/spec/common/storages/adapters/providers/nextcloud/contracts/storage_contract_spec.rb +++ b/modules/storages/spec/common/storages/adapters/providers/nextcloud/contracts/storage_contract_spec.rb @@ -95,8 +95,7 @@ module Storages "Please verify that the connection is functioning properly." expect(subject.errors.to_hash).to eq({ password: [message] }) - # twice due to HTTPX retry plugin being enabled. - expect(credentials_request).to have_been_made.twice + expect(credentials_request).to have_been_made end end diff --git a/modules/storages/spec/common/storages/adapters/providers/nextcloud/queries/download_link_query_spec.rb b/modules/storages/spec/common/storages/adapters/providers/nextcloud/queries/download_link_query_spec.rb index ed9da57e56e..b5ab915fed1 100644 --- a/modules/storages/spec/common/storages/adapters/providers/nextcloud/queries/download_link_query_spec.rb +++ b/modules/storages/spec/common/storages/adapters/providers/nextcloud/queries/download_link_query_spec.rb @@ -82,14 +82,12 @@ module Storages end context "with outbound request returning 200 and an empty body" do - it "refreshes the token and returns success", vcr: "nextcloud/download_link_query_unauthorized" do + it "fails with code invalid_response", vcr: "nextcloud/download_link_query_unauthorized" do download_link = subject.call(auth_strategy:, input_data:) - expect(download_link).to be_success + expect(download_link).to be_failure - uri = download_link.value! - expect(uri.host).to eq("nextcloud.local") - expect(uri.path) - .to match(/index.php\/apps\/integration_openproject\/direct\/[0-9a-zA-Z]+\/#{file_link.origin_name}/) + error = download_link.failure + expect(error.code).to eq(:invalid_response) end end end diff --git a/modules/storages/spec/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator_spec.rb b/modules/storages/spec/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator_spec.rb index b74411e0d55..bb307442214 100644 --- a/modules/storages/spec/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator_spec.rb +++ b/modules/storages/spec/common/storages/adapters/providers/nextcloud/validators/ampf_configuration_validator_spec.rb @@ -45,9 +45,14 @@ module Storages let(:files_response) do Success(Results::StorageFileCollection.new( - files: [Results::StorageFile.new(id: project_folder_id, - name: project_storage.managed_project_folder_name)], - parent: Results::StorageFile.new(id: "root", name: "root"), + files: [ + Results::StorageFile.new( + id: project_folder_id, + name: project_storage.managed_project_folder_name, + mime_type: "application/x-op-directory" + ) + ], + parent: Results::StorageFile.new(id: "root", name: "root", mime_type: "application/x-op-directory"), ancestors: [] )) end @@ -87,7 +92,7 @@ module Storages allow(subject).to receive(:nextcloud_dependencies).and_return(absurd_version) results = validator.call - expect(results[:team_folder_app]).to be_a_failure + expect(results[:team_folder_app]).to be_a_warning expect(results[:team_folder_app].code).to eq(:nc_dependency_version_mismatch) expect(results[:team_folder_app].context[:dependency]).to eq("Team Folders") end @@ -108,7 +113,7 @@ module Storages results = validator.call states = results.tally - expect(states).to eq({ success: 2, failure: 1, skipped: 2 }) + expect(states).to eq({ success: 2, failure: 1, skipped: 4 }) expect(results[:userless_access]).to be_failure expect(results[:userless_access].code).to eq(:nc_userless_access_denied) end @@ -144,10 +149,14 @@ module Storages let(:files_response) do Success(Results::StorageFileCollection.new( files: [ - Results::StorageFile.new(id: project_folder_id, name: "I am your father"), + Results::StorageFile.new( + id: project_folder_id, + name: "I am your father", + mime_type: "application/x-op-directory" + ), Results::StorageFile.new(id: "noooooooooo", name: "testimony_of_luke_skywalker.md") ], - parent: Results::StorageFile.new(id: "root", name: "root"), + parent: Results::StorageFile.new(id: "root", name: "root", mime_type: "application/x-op-directory"), ancestors: [] )) end @@ -156,7 +165,43 @@ module Storages results = validator.call expect(results[:team_folder_contents]).to be_a_warning - expect(results[:team_folder_contents].code).to eq(:nc_unexpected_content) + expect(results[:team_folder_contents].code).to eq(:nc_unexpected_files) + end + end + + context "if the files request does not return a project folder" do + let(:files_response) do + Success(Results::StorageFileCollection.new( + files: [ + Results::StorageFile.new( + id: "13#{project_folder_id}37", + name: project_storage.managed_project_folder_name, # name matches, but ID is different + mime_type: "application/x-op-directory" + ) + ], + parent: Results::StorageFile.new(id: "root", name: "root", mime_type: "application/x-op-directory"), + ancestors: [] + )) + end + + it "shows a failure about a missing project folder" do + results = validator.call + + expect(results[:project_folders_exist]).to be_a_failure + expect(results[:project_folders_exist].code).to eq(:nc_project_folder_missing) + end + end + + context "when an AMPF project storage has no project folder yet" do + let!(:project_storage_two) do + create(:project_storage, :as_automatically_managed, project_folder_id: "", storage:, project: create(:project)) + end + + it "warns the user about a missing project folder link" do + results = validator.call + + expect(results[:project_folders_linked]).to be_a_warning + expect(results[:project_folders_linked].code).to eq(:nc_unlinked_project_folders) end end diff --git a/modules/storages/spec/components/storages/admin/side_panel/health_notifications_component_spec.rb b/modules/storages/spec/components/storages/admin/side_panel/health_notifications_component_spec.rb index 7cf678a7f50..c3dba9cb762 100644 --- a/modules/storages/spec/components/storages/admin/side_panel/health_notifications_component_spec.rb +++ b/modules/storages/spec/components/storages/admin/side_panel/health_notifications_component_spec.rb @@ -58,7 +58,7 @@ RSpec.describe Storages::Admin::SidePanel::HealthNotificationsComponent, type: : it "shows a healthy status" do expect(page).to have_test_selector("storage-health-status", text: "Healthy") - expect(page).to have_test_selector("storage-health-checked-at", text: "Last check: 11/28/2023 01:02 AM") + expect(page).to have_test_selector("storage-health-checked-at", text: "Last sync: 11/28/2023 01:02 AM") end end diff --git a/modules/storages/spec/contracts/storages/storages/shared_contract_examples.rb b/modules/storages/spec/contracts/storages/storages/shared_contract_examples.rb index b4479c92e5a..b6a08f63096 100644 --- a/modules/storages/spec/contracts/storages/storages/shared_contract_examples.rb +++ b/modules/storages/spec/contracts/storages/storages/shared_contract_examples.rb @@ -192,8 +192,7 @@ RSpec.shared_examples_for "nextcloud storage contract", :storage_server_helpers, it "retries failed request once" do contract.validate - # twice due to HTTPX retry plugin being enabled. - expect(stub_server_capabilities).to have_been_made.twice + expect(stub_server_capabilities).to have_been_made end end @@ -204,8 +203,7 @@ RSpec.shared_examples_for "nextcloud storage contract", :storage_server_helpers, it "retries failed request once" do contract.validate - # twice due to HTTPX retry plugin being enabled. - expect(stub_config_check).to have_been_made.twice + expect(stub_config_check).to have_been_made end end end diff --git a/modules/storages/spec/support/fixtures/vcr_cassettes/auth/nextcloud/refresh_token.yml b/modules/storages/spec/support/fixtures/vcr_cassettes/auth/nextcloud/refresh_token.yml new file mode 100644 index 00000000000..12f2c24ea33 --- /dev/null +++ b/modules/storages/spec/support/fixtures/vcr_cassettes/auth/nextcloud/refresh_token.yml @@ -0,0 +1,451 @@ +--- +http_interactions: +- request: + method: get + uri: https://nextcloud.local/ocs/v1.php/cloud/user + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - OpenProject 17.2.0 HTTPX Client + Accept: + - "*/*" + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer + response: + status: + code: 401 + message: Unauthorized + headers: + Cache-Control: + - no-cache, no-store, must-revalidate + Content-Security-Policy: + - default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none' + Content-Type: + - application/xml; charset=utf-8 + Date: + - Mon, 02 Mar 2026 16:08:46 GMT + Feature-Policy: + - autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone + 'none';payment 'none' + Referrer-Policy: + - no-referrer + Server: + - Apache/2.4.66 (Debian) + Set-Cookie: + - ocijlj3fsmm9=5b65855c97c459db69c61861f926501f; path=/; secure; HttpOnly; SameSite=Lax, + oc_sessionPassphrase=eeaqxrN0jZy61LxpAwJ7W1RwdhNnTJ4eE53GnTsym79KnC%2Fd%2Bs7VxPDGJ8sdlqgXt84b5inOVqF2cOYkCPIXrzCHtSBkF852ekuoKW9e0oBoEgxmnsccJbD5uSRwv9h%2B; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=5b65855c97c459db69c61861f926501f; + path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true; + path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax, + __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri, + 31-Dec-2100 23:59:59 GMT; SameSite=strict, ocijlj3fsmm9=5b65855c97c459db69c61861f926501f; + path=/; secure; HttpOnly; SameSite=Lax, nc_username=deleted; expires=Thu, + 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_token=deleted; expires=Thu, + 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_session_id=deleted; + expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_username=deleted; + expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly, + nc_token=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; + secure; HttpOnly, nc_session_id=deleted; expires=Thu, 01 Jan 1970 00:00:01 + GMT; Max-Age=0; path=/; secure; HttpOnly + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Powered-By: + - PHP/8.4.18 + X-Request-Id: + - mpcjYVvLLOeYrNKfHGA4 + X-Robots-Tag: + - noindex, nofollow + Content-Length: + - '230' + body: + encoding: UTF-8 + string: | + + + + failure + 997 + Current user is not logged in + + + + + + recorded_at: Mon, 02 Mar 2026 16:08:46 GMT +- request: + method: post + uri: https://nextcloud.local/index.php/apps/oauth2/api/v1/token + body: + encoding: ASCII-8BIT + string: grant_type=refresh_token&scope=&refresh_token= + headers: + User-Agent: + - OpenProject 17.2.0 HTTPX Client + Accept: + - "*/*" + Accept-Encoding: + - gzip, deflate + Authorization: + - Basic + Content-Type: + - application/x-www-form-urlencoded + Content-Length: + - '174' + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - no-cache, no-store, must-revalidate + Content-Encoding: + - gzip + Content-Security-Policy: + - default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none' + Content-Type: + - application/json; charset=utf-8 + Date: + - Mon, 02 Mar 2026 16:08:46 GMT + Feature-Policy: + - autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone + 'none';payment 'none' + Referrer-Policy: + - no-referrer + Server: + - Apache/2.4.66 (Debian) + Set-Cookie: + - ocijlj3fsmm9=b3b43751df79bb2be113552e0da3c8ef; path=/; secure; HttpOnly; SameSite=Lax, + oc_sessionPassphrase=jjRcONBYop3Z1ttvqVG8PoNd928I2MOEQ5wvxPYxmsvSM07Ow5FKvhg1JnCFLwnYSlfMCU5FaEQ35ZGSwULlYqJ8Fndsqn5nk48zjTTdXiedpPMlZDU%2F%2FkWaYG%2BChSAK; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=b3b43751df79bb2be113552e0da3c8ef; + path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true; + path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax, + __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri, + 31-Dec-2100 23:59:59 GMT; SameSite=strict, ocijlj3fsmm9=b3b43751df79bb2be113552e0da3c8ef; + path=/; secure; HttpOnly; SameSite=Lax + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Powered-By: + - PHP/8.4.18 + X-Request-Id: + - JewgU7FS0QDRXDmGLnWf + X-Robots-Tag: + - noindex, nofollow + Content-Length: + - '270' + body: + encoding: UTF-8 + string: '{"access_token":"","token_type":"Bearer","expires_in":3600,"refresh_token":"","user_id":"admin"}' + recorded_at: Mon, 02 Mar 2026 16:08:46 GMT +- request: + method: get + uri: https://nextcloud.local/ocs/v1.php/cloud/user + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - OpenProject 17.2.0 HTTPX Client + Accept: + - "*/*" + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - no-cache, no-store, must-revalidate + Content-Encoding: + - gzip + Content-Security-Policy: + - default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none' + Content-Type: + - application/xml; charset=utf-8 + Date: + - Mon, 02 Mar 2026 16:08:46 GMT + Feature-Policy: + - autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone + 'none';payment 'none' + Referrer-Policy: + - no-referrer + Server: + - Apache/2.4.66 (Debian) + Set-Cookie: + - ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; path=/; secure; HttpOnly; SameSite=Lax, + oc_sessionPassphrase=BzSroJ7oX15ip9xi5Y8Na48QPwG3mu7fpYl%2B2wlSxOFqh%2BUEIwflqvkgtdvr7LpA5zGPXrat0VvAckP95BkTm7ZcK8JJZEwKYK7shleHPn1ZaIzThTiJWuLuuRjnmxp%2F; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true; + path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax, + __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri, + 31-Dec-2100 23:59:59 GMT; SameSite=strict, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, nc_username=deleted; expires=Thu, + 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_token=deleted; expires=Thu, + 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_session_id=deleted; + expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_username=deleted; + expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly, + nc_token=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; + secure; HttpOnly, nc_session_id=deleted; expires=Thu, 01 Jan 1970 00:00:01 + GMT; Max-Age=0; path=/; secure; HttpOnly, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Powered-By: + - PHP/8.4.18 + X-Request-Id: + - 9KY3Gces4KNzsrkeOsWU + X-Robots-Tag: + - noindex, nofollow + X-User-Id: + - admin + Content-Length: + - '691' + body: + encoding: UTF-8 + string: | + + + + ok + 100 + OK + + + + + 1 + /var/www/html/data/admin + admin + 1772444624 + 1772467726 + 1772467726000 + Database + + + -3 + 60872728 + -3 + 0 + -3 + + + v2-federated + + v2-federated + + + admin + admin + v2-federated + + v2-local +
+ v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + 1 + v2-local + + v2-federated + + admin + + en + + Europe/Busingen + + + 1 + 1 + +
+
+ recorded_at: Mon, 02 Mar 2026 16:08:46 GMT +- request: + method: get + uri: https://nextcloud.local/ocs/v1.php/cloud/user + body: + encoding: US-ASCII + string: '' + headers: + User-Agent: + - OpenProject 17.2.0 HTTPX Client + Accept: + - "*/*" + Accept-Encoding: + - gzip, deflate + Authorization: + - Bearer + response: + status: + code: 200 + message: OK + headers: + Cache-Control: + - no-cache, no-store, must-revalidate + Content-Encoding: + - gzip + Content-Security-Policy: + - default-src 'none';base-uri 'none';manifest-src 'self';frame-ancestors 'none' + Content-Type: + - application/xml; charset=utf-8 + Date: + - Mon, 02 Mar 2026 16:08:46 GMT + Feature-Policy: + - autoplay 'none';camera 'none';fullscreen 'none';geolocation 'none';microphone + 'none';payment 'none' + Referrer-Policy: + - no-referrer + Server: + - Apache/2.4.66 (Debian) + Set-Cookie: + - ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; path=/; secure; HttpOnly; SameSite=Lax, + oc_sessionPassphrase=BzSroJ7oX15ip9xi5Y8Na48QPwG3mu7fpYl%2B2wlSxOFqh%2BUEIwflqvkgtdvr7LpA5zGPXrat0VvAckP95BkTm7ZcK8JJZEwKYK7shleHPn1ZaIzThTiJWuLuuRjnmxp%2F; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, __Host-nc_sameSiteCookielax=true; + path=/; httponly;secure; expires=Fri, 31-Dec-2100 23:59:59 GMT; SameSite=lax, + __Host-nc_sameSiteCookiestrict=true; path=/; httponly;secure; expires=Fri, + 31-Dec-2100 23:59:59 GMT; SameSite=strict, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, nc_username=deleted; expires=Thu, + 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_token=deleted; expires=Thu, + 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_session_id=deleted; + expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; secure; HttpOnly, nc_username=deleted; + expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; secure; HttpOnly, + nc_token=deleted; expires=Thu, 01 Jan 1970 00:00:01 GMT; Max-Age=0; path=/; + secure; HttpOnly, nc_session_id=deleted; expires=Thu, 01 Jan 1970 00:00:01 + GMT; Max-Age=0; path=/; secure; HttpOnly, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax, ocijlj3fsmm9=8c15d45120403fd0c95c1c8d23c250b1; + path=/; secure; HttpOnly; SameSite=Lax + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Permitted-Cross-Domain-Policies: + - none + X-Powered-By: + - PHP/8.4.18 + X-Request-Id: + - 9KY3Gces4KNzsrkeOsWU + X-Robots-Tag: + - noindex, nofollow + X-User-Id: + - admin + Content-Length: + - '691' + body: + encoding: UTF-8 + string: | + + + + ok + 100 + OK + + + + + 1 + /var/www/html/data/admin + admin + 1772444624 + 1772467726 + 1772467726000 + Database + + + -3 + 60872728 + -3 + 0 + -3 + + + v2-federated + + v2-federated + + + admin + admin + v2-federated + + v2-local +
+ v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + + v2-local + 1 + v2-local + + v2-federated + + admin + + en + + Europe/Busingen + + + 1 + 1 + +
+
+ recorded_at: Mon, 02 Mar 2026 16:08:46 GMT +recorded_with: VCR 6.4.0 diff --git a/modules/team_planner/config/locales/crowdin/js-fr.yml b/modules/team_planner/config/locales/crowdin/js-fr.yml index 4d8a0c78286..c96442a127a 100644 --- a/modules/team_planner/config/locales/crowdin/js-fr.yml +++ b/modules/team_planner/config/locales/crowdin/js-fr.yml @@ -19,7 +19,7 @@ fr: today: 'Aujourd''hui' drag_here_to_remove: 'Faites glisser ici pour supprimer le responsable et les dates de début et de fin.' cannot_drag_here: 'Impossible de déplacer le lot de travail en raison de restrictions d''autorisation ou d''édition.' - cannot_drag_to_non_working_day: 'Ce lot de travaux ne peut pas démarrer/terminer sur un jour non ouvré.' + cannot_drag_to_non_working_day: 'Ce lot de travail ne peut pas démarrer/terminer sur un jour non ouvré.' quick_add: empty_state: 'Utilisez le champ de recherche pour trouver des lots de travaux et faites-les glisser vers le planificateur pour l''assigner à quelqu''un et définir des dates de début et de fin.' search_placeholder: 'Rechercher...' diff --git a/modules/two_factor_authentication/config/locales/crowdin/ro.yml b/modules/two_factor_authentication/config/locales/crowdin/ro.yml index 6c8ef8748d3..7feb54c4620 100644 --- a/modules/two_factor_authentication/config/locales/crowdin/ro.yml +++ b/modules/two_factor_authentication/config/locales/crowdin/ro.yml @@ -178,7 +178,7 @@ ro: label_expiration_hint: "%{date} sau la deconectare" label_actions: "Acțiuni" label_confirmed: "Confirmat" - button_continue: "Continuă" + button_continue: "Continuaţi" button_make_default: "Marcați ca implicit" label_unverified_phone: "Telefonul mobil nu a fost încă verificat" notice_phone_number_format: "Te rog să introduci numărul în următorul format: +XX XXXXXXXX." diff --git a/modules/two_factor_authentication/config/locales/crowdin/ru.yml b/modules/two_factor_authentication/config/locales/crowdin/ru.yml index 80fa7dc0e25..bdba7c151d8 100644 --- a/modules/two_factor_authentication/config/locales/crowdin/ru.yml +++ b/modules/two_factor_authentication/config/locales/crowdin/ru.yml @@ -178,7 +178,7 @@ ru: label_expiration_hint: "%{date} или при выходе из системы" label_actions: "Действия" label_confirmed: "Подтвержден" - button_continue: "Продолжить" + button_continue: "Далее" button_make_default: "Задать по умолчанию" label_unverified_phone: "Сотовый телефон еще не подтвержден" notice_phone_number_format: "Введите номер в следующем формате: +XX XXXXXXXX." diff --git a/modules/two_factor_authentication/config/locales/crowdin/uk.yml b/modules/two_factor_authentication/config/locales/crowdin/uk.yml index 944c3bc181d..8649db79efe 100644 --- a/modules/two_factor_authentication/config/locales/crowdin/uk.yml +++ b/modules/two_factor_authentication/config/locales/crowdin/uk.yml @@ -119,7 +119,7 @@ uk: failed_to_delete: "Не вдалося видалити пристрій 2FA." is_default_cannot_delete: "Пристрій позначено як типовий і його не можна видалити через активну політику безпеки. Перед видаленням позначте інший пристрій як стандартний." not_existing: "Для вашого облікового запису не зареєстровано жодного пристрою 2FA." - 2fa_from_input: Введіть код, отриманий на пристрій %{device_name}, щоб підтвердити свою особу. + 2fa_from_input: Введіть код, що надійшов на пристрій %{device_name}, щоб підтвердити свою особу. 2fa_from_webauthn: Укажіть пристрій WebAuthn %{device_name}. Якщо це USB-пристрій, переконайтеся, що його підключено, і торкніться його. Потім натисніть кнопку входу. webauthn: title: "WebAuthn" diff --git a/modules/webhooks/spec/services/webhooks/outgoing/request_webhook_service_spec.rb b/modules/webhooks/spec/services/webhooks/outgoing/request_webhook_service_spec.rb index 3ad13e89f60..1d826303b63 100644 --- a/modules/webhooks/spec/services/webhooks/outgoing/request_webhook_service_spec.rb +++ b/modules/webhooks/spec/services/webhooks/outgoing/request_webhook_service_spec.rb @@ -43,7 +43,7 @@ RSpec.describe Webhooks::Outgoing::RequestWebhookService, :webmock, type: :model describe "#call!" do context "when the request is successful" do before do - stub_request(:post, ssrf_resolved_url(webhook.url)) + stub_request(:post, webhook.url) .with(body: "body", headers: { "X-Custom" => "header" }) .to_return(status: 200, body: "OK", headers: { "Content-Type" => "application/json" }) end @@ -52,7 +52,7 @@ RSpec.describe Webhooks::Outgoing::RequestWebhookService, :webmock, type: :model it "makes a POST request to the webhook URL with the given body and headers" do subject - expect(WebMock).to have_requested(:post, ssrf_resolved_url(webhook.url)) + expect(WebMock).to have_requested(:post, webhook.url) .with(body: "body", headers: { "X-Custom" => "header", "Host" => "example.net" }).once end @@ -67,11 +67,24 @@ RSpec.describe Webhooks::Outgoing::RequestWebhookService, :webmock, type: :model expect(log.response_body).to eq("OK") expect(log.url).to eq(webhook.url) end + + it "connects to the original hostname while routing through the resolved safe IP address" do + http_start_args = nil + allow(Net::HTTP).to receive(:start).and_wrap_original do |original, *args, **kwargs, &block| + http_start_args = { host: args[0], options: kwargs } + original.call(*args, **kwargs, &block) + end + + subject + + expect(http_start_args[:host]).to eq("example.net") + expect(http_start_args[:options]).to include(ipaddr: WithSsrfWebhookStubsMixin::SSRF_TEST_IP) + end end context "when the request times out" do before do - stub_request(:post, ssrf_resolved_url(webhook.url)).to_timeout + stub_request(:post, webhook.url).to_timeout end it "re-raises the timeout error while still creating a log entry" do @@ -83,7 +96,7 @@ RSpec.describe Webhooks::Outgoing::RequestWebhookService, :webmock, type: :model context "when request_url fails with SSL errors" do before do - stub_request(:post, ssrf_resolved_url(webhook.url)).to_raise(OpenSSL::SSL::SSLError) + stub_request(:post, webhook.url).to_raise(OpenSSL::SSL::SSLError) end it "still logs the exception" do @@ -135,7 +148,7 @@ RSpec.describe Webhooks::Outgoing::RequestWebhookService, :webmock, type: :model context "when an unexpected error occurs" do before do - stub_request(:post, ssrf_resolved_url(webhook.url)).to_raise(StandardError.new("something went wrong")) + stub_request(:post, webhook.url).to_raise(StandardError.new("something went wrong")) end it "creates a log entry" do diff --git a/modules/webhooks/spec/workers/attachment_webhook_job_spec.rb b/modules/webhooks/spec/workers/attachment_webhook_job_spec.rb index 9d29d127a3d..a0a5dce1ad2 100644 --- a/modules/webhooks/spec/workers/attachment_webhook_job_spec.rb +++ b/modules/webhooks/spec/workers/attachment_webhook_job_spec.rb @@ -52,7 +52,7 @@ RSpec.describe AttachmentWebhookJob, :webmock, type: :job do end let(:stub) do - stub_request(:post, ssrf_resolved_url(stubbed_url)) + stub_request(:post, stubbed_url) .with( body: hash_including( "action" => event, diff --git a/modules/webhooks/spec/workers/project_webhook_job_spec.rb b/modules/webhooks/spec/workers/project_webhook_job_spec.rb index 8369a8dc59d..6b89510bdc0 100644 --- a/modules/webhooks/spec/workers/project_webhook_job_spec.rb +++ b/modules/webhooks/spec/workers/project_webhook_job_spec.rb @@ -56,7 +56,7 @@ RSpec.describe ProjectWebhookJob, :webmock, type: :job do end let(:stub) do - stub_request(:post, ssrf_resolved_url(stubbed_url)) + stub_request(:post, stubbed_url) .with( body: hash_including( "action" => event, diff --git a/modules/webhooks/spec/workers/time_entry_webhook_job_spec.rb b/modules/webhooks/spec/workers/time_entry_webhook_job_spec.rb index 166a1c6e16a..17304244085 100644 --- a/modules/webhooks/spec/workers/time_entry_webhook_job_spec.rb +++ b/modules/webhooks/spec/workers/time_entry_webhook_job_spec.rb @@ -53,7 +53,7 @@ RSpec.describe TimeEntryWebhookJob, :webmock, type: :job do end let(:stub) do - stub_request(:post, ssrf_resolved_url(stubbed_url)) + stub_request(:post, stubbed_url) .with( body: hash_including( "action" => event, diff --git a/modules/webhooks/spec/workers/work_package_comment_webhook_job_spec.rb b/modules/webhooks/spec/workers/work_package_comment_webhook_job_spec.rb index 91bc64f5fed..8a97a093c87 100644 --- a/modules/webhooks/spec/workers/work_package_comment_webhook_job_spec.rb +++ b/modules/webhooks/spec/workers/work_package_comment_webhook_job_spec.rb @@ -53,7 +53,7 @@ RSpec.describe WorkPackageCommentWebhookJob, :webmock, type: :model do end let(:stub) do - stub_request(:post, ssrf_resolved_url(stubbed_url)).with( + stub_request(:post, stubbed_url).with( body: hash_including( "action" => event_name, "activity" => hash_including( diff --git a/modules/webhooks/spec/workers/work_package_webhook_job_spec.rb b/modules/webhooks/spec/workers/work_package_webhook_job_spec.rb index 8ed94899ef7..664d0515c9c 100644 --- a/modules/webhooks/spec/workers/work_package_webhook_job_spec.rb +++ b/modules/webhooks/spec/workers/work_package_webhook_job_spec.rb @@ -56,7 +56,7 @@ RSpec.describe WorkPackageWebhookJob, :webmock, type: :model do end let(:stub) do - stub_request(:post, ssrf_resolved_url(stubbed_url)) + stub_request(:post, stubbed_url) .with( body: hash_including( "action" => event, diff --git a/modules/wikis/README.md b/modules/wikis/README.md new file mode 100644 index 00000000000..2be37cd302b --- /dev/null +++ b/modules/wikis/README.md @@ -0,0 +1,7 @@ +# OpenProject Wikis Plugin + +FIXME Add description and check issue tracker link below + +## Issue Tracker + +https://community.openproject.org/projects/wikis/work_packages diff --git a/modules/wikis/config/locales/en.yml b/modules/wikis/config/locales/en.yml new file mode 100644 index 00000000000..efc34c0e46a --- /dev/null +++ b/modules/wikis/config/locales/en.yml @@ -0,0 +1,7 @@ +--- +en: + activerecord: + attributes: {} + errors: {} + models: {} + wikis: {} diff --git a/modules/wikis/lib/open_project/wikis.rb b/modules/wikis/lib/open_project/wikis.rb new file mode 100644 index 00000000000..d647a267736 --- /dev/null +++ b/modules/wikis/lib/open_project/wikis.rb @@ -0,0 +1,36 @@ +# 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 "open_project/wikis/engine" + +module OpenProject + module Wikis + end +end diff --git a/modules/wikis/lib/open_project/wikis/engine.rb b/modules/wikis/lib/open_project/wikis/engine.rb new file mode 100644 index 00000000000..c01586c0670 --- /dev/null +++ b/modules/wikis/lib/open_project/wikis/engine.rb @@ -0,0 +1,45 @@ +# 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. +#++ + +# Prevent load-order problems in case openproject-plugins is listed after a plugin in the Gemfile +# or not at all +require "open_project/plugins" + +module OpenProject::Wikis + class Engine < ::Rails::Engine + engine_name :openproject_wikis + + include OpenProject::Plugins::ActsAsOpEngine + + register "openproject-wikis", + author_url: "https://openproject.org", + requires_openproject: ">= 17.0.0" + end +end diff --git a/modules/wikis/lib/openproject-wikis.rb b/modules/wikis/lib/openproject-wikis.rb new file mode 100644 index 00000000000..adbe05a548f --- /dev/null +++ b/modules/wikis/lib/openproject-wikis.rb @@ -0,0 +1,31 @@ +# 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 "open_project/wikis" diff --git a/modules/wikis/openproject-wikis.gemspec b/modules/wikis/openproject-wikis.gemspec new file mode 100644 index 00000000000..f6ef7db7d07 --- /dev/null +++ b/modules/wikis/openproject-wikis.gemspec @@ -0,0 +1,44 @@ +# 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. +#++ + +# Describe your gem and declare its dependencies: +Gem::Specification.new do |s| + s.name = "openproject-wikis" + s.version = "1.0.0" + + s.authors = "OpenProject GmbH" + s.email = "info@openproject.org" + s.summary = "OpenProject Wikis" + s.description = "Allows linking work packages to pages in wikis, such as XWiki or the internal OpenProject wiki." + s.license = "GPLv3" + + s.files = Dir["{app,config,db,lib}/**/*"] + %w(CHANGELOG.md README.md) + s.metadata["rubygems_mfa_required"] = "true" +end diff --git a/modules/xls_export/config/locales/crowdin/zh-CN.yml b/modules/xls_export/config/locales/crowdin/zh-CN.yml index 59230e603ec..31c8d3cdd71 100644 --- a/modules/xls_export/config/locales/crowdin/zh-CN.yml +++ b/modules/xls_export/config/locales/crowdin/zh-CN.yml @@ -13,4 +13,4 @@ zh-CN: xls_with_relations: "带关系的 XLS" xls_export: child_of: 此项的子项 - parent_of: 此项的父级 + parent_of: 此项的父项 diff --git a/nix/gemset.nix b/nix/gemset.nix index db77431ee4e..9b80c3d84d8 100644 --- a/nix/gemset.nix +++ b/nix/gemset.nix @@ -527,15 +527,15 @@ version = "1.0.25"; }; carrierwave = { - dependencies = ["activemodel" "activesupport" "mime-types" "ssrf_filter"]; + dependencies = ["activemodel" "activesupport" "addressable" "image_processing" "marcel" "mini_mime" "ssrf_filter"]; groups = ["default"]; platforms = []; source = { remotes = ["https://rubygems.org"]; - sha256 = "055i3ybjv9n9hqaazxn3d9ibqhlwh93d4hdlwbpjjfy8qbrz6hiw"; + sha256 = "dy6364hzm3ijfzxgwdi3ny66sc24gyqj1wvsx60zj7xzmy3qsyh1"; type = "gem"; }; - version = "1.3.2"; + version = "2.2.6"; }; carrierwave_direct = { dependencies = ["carrierwave" "fog-aws"]; @@ -1695,10 +1695,10 @@ platforms = []; source = { remotes = ["https://rubygems.org"]; - sha256 = "1vhp6lifwvqs2b0a276lj61n86c1l7d1xiswjj2w23f54gl51mpk"; + sha256 = "dhmj4zvnbs3kirgs6s2d933hsds9pim6hf88rk807l1m8km2n091"; type = "gem"; }; - version = "1.0.0"; + version = "1.0.4"; }; messagebird-rest = { groups = ["default" "opf_plugins"]; @@ -3314,10 +3314,10 @@ platforms = []; source = { remotes = ["https://rubygems.org"]; - sha256 = "03f49f54837e407d43ee93ec733a8a94dc1bcf8185647ac61606e63aaedaa0db"; + sha256 = "632vjyxwh673082knymg3j82ml22f2fxw5z2m31h2816q3cm6mw1"; type = "gem"; }; - version = "1.0.8"; + version = "1.3.0"; }; stackprof = { groups = ["development" "test"]; diff --git a/package-lock.json b/package-lock.json index 5c9d870c25d..451db8f1fe3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1040,11 +1040,13 @@ "dev": true }, "node_modules/dompurify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", - "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.2.tgz", + "integrity": "sha512-6obghkliLdmKa56xdbLOpUZ43pAR6xFy1uOrxBaIDjT+yaRuuybLjGS9eVBoSR/UPU5fq3OXClEHLJNGvbxKpQ==", "dev": true, - "license": "(MPL-2.0 OR Apache-2.0)", + "engines": { + "node": ">=20" + }, "optionalDependencies": { "@types/trusted-types": "^2.0.7" } @@ -2426,11 +2428,10 @@ } }, "node_modules/undici": { - "version": "6.23.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", - "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.1.tgz", + "integrity": "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA==", "dev": true, - "license": "MIT", "engines": { "node": ">=18.17" } diff --git a/script/github_pr_errors b/script/github_pr_errors index 82f9abc693d..fcfb79f81bc 100755 --- a/script/github_pr_errors +++ b/script/github_pr_errors @@ -17,14 +17,14 @@ require "yaml" require "httpx" require "cgi" +require_relative "support/github_actions_failures" + GITHUB_API_OPENPROJECT_PREFIX = "https://api.github.com/repos/opf/openproject" GITHUB_HTML_OPENPROJECT_PREFIX = "https://github.com/opf/openproject" RAILS_ROOT = Pathname.new(__dir__).dirname EXCLUDED_JOB_NAMES = %w[eslint rubocop].freeze -if !ENV["GITHUB_USERNAME"] - raise "Missing GITHUB_USERNAME env" -elsif !ENV["GITHUB_TOKEN"] +if !ENV["GITHUB_TOKEN"] raise "Missing GITHUB_TOKEN env, go to https://github.com/settings/tokens and create one with 'repo' access" end @@ -179,8 +179,9 @@ end def http HTTPX .plugin(:follow_redirects) - .plugin(:basic_auth) - .basic_auth(ENV.fetch("GITHUB_USERNAME"), ENV.fetch("GITHUB_TOKEN")) + .with_headers( + "Authorization" => "Bearer #{ENV.fetch('GITHUB_TOKEN')}" + ) end def get_http(path) @@ -337,170 +338,6 @@ Report = Data.define( end end -class Error - attr_accessor :location, :page_html, :page_screenshot, :tests_group, :loading_error -end - -# rubocop:disable Layout/LineLength -# Looks like this in the job log: -# Process 28: TEST_ENV_NUMBER=28 RUBYOPT=-I/usr/local/bundle/bundler/gems/turbo_tests-3148ae6c3482/lib -r/usr/local/bundle/gems/bundler-2.5.23/lib/bundler/setup -W0 RSPEC_SILENCE_FILTER_ANNOUNCEMENTS=1 /usr/local/bundle/gems/bundler-2.5.23/exe/bundle exec rspec --seed 52674 --format TurboTests::JsonRowsFormatter --out tmp/test-pipes/subprocess-28 --format ParallelTests::RSpec::RuntimeLogger --out spec/support/turbo_runtime_features.log spec/features/api_docs/index_spec.rb spec/features/custom_fields/reorder_options_spec.rb spec/features/projects/projects_portfolio_spec.rb spec/features/projects/template_spec.rb spec/features/versions/edit_spec.rb spec/features/work_packages/details/markdown/description_editor_spec.rb spec/features/work_packages/table/hierarchy/hierarchy_parent_below_spec.rb spec/features/work_packages/table/inline_create/inline_create_refresh_spec.rb spec/features/work_packages/table/invalid_query_spec.rb spec/features/work_packages/tabs/activity_revisions_spec.rb -# rubocop:enable Layout/LineLength -class TestsGroup - attr_accessor :test_env_number, :seed, :files - - def initialize - @files = [] - end - - def include_error?(error) - return false if error.location.nil? - - files.any? { |file| error.location.include?(file) } - end - - def inspect - "#<#{self.class} @test_env_number=#{test_env_number} @seed=#{seed} (#{files.count} files)>" - end -end - -class JobErrorsFinder - SPEC_FAILURES_PATTERN = %r{^\S+ rspec (\S+) #.+$} - SPEC_LOADING_ERRORS_PATTERN = %r{^\S+ An error occurred while loading (\S+)\.\r?$} - SCREENSHOT_PATTERN = /\{"message":"Screenshot captured for failed feature test"[^\n]+$/ - TESTS_GROUP_PATTERN = /Process \d+: TEST_ENV_NUMBER=\d+ [^\n]+$/ - BRANCH_MERGE_PATTERN = /Merge \w{40} into (\w{40})$/ - - attr_reader :failures_explanation, :merge_branch_sha - - def self.scan_logs(report, logs) - finder = new - logs.each do |log| - finder.scan_log(log) - end - report.with( - errors: finder.errors, - failures_explanation: finder.failures_explanation, - merge_branch_sha: finder.merge_branch_sha - ) - end - - def scan_log(log) - find_failures(log) - find_failures_explanation(log) - find_loading_errors(log) - find_screenshots(log) - find_tests_groups(log) - find_merge_branch_info(log) - end - - def errors - @errors.values - end - - protected - - def initialize - @errors = {} - end - - def create_error(location) - return if location.nil? - - error = Error.new - error.location = location - @errors[location] ||= error - end - - def with_matching_error(location: nil, id: nil) - error = @errors[id] || @errors[location] - yield error if error && block_given? - error - end - - def find_failures(log) - log.scan(SPEC_FAILURES_PATTERN) - .flatten - .uniq - .sort - .each do |rerun_location| - create_error(rerun_location) - end - end - - def find_failures_explanation(log) - explanations = [] - log.split("\n").each do |line| - if line.end_with?("Failures:") .. line.end_with?("Failed examples:") - explanations << line - end - end - explanations.map! { it[29..] } # Remove leading timestamp (like "2024-02-05T08:37:54.5175930Z") - explanations.reject! do |line| - line == "Failures:" || - line == "Failed examples:" || - line.include?("gems/rspec-retry-") || - line.include?("gems/webmock-") - end - @failures_explanation = explanations.join("\n") - end - - def find_loading_errors(log) - log.scan(SPEC_LOADING_ERRORS_PATTERN) - .flatten - .uniq - .sort - .each do |location| - error = create_error(location) - error.loading_error = true - end - end - - def find_screenshots(log) - log.scan(SCREENSHOT_PATTERN) - .map { JSON.parse it } - .each do |screenshot_info| - id = screenshot_info["test_id"] - location = screenshot_info["test_location"] - with_matching_error(location:, id:) do |error| - error.page_html = screenshot_info["html"] - error.page_screenshot = screenshot_info["image"] - end - end - end - - def find_tests_groups(log) - tests_groups = log - .scan(TESTS_GROUP_PATTERN) - .flatten - .map { build_tests_group_from_command(it) } - - errors.each do |error| - error.tests_group = tests_groups.find { it.include_error?(error) } - end - end - - def find_merge_branch_info(log) - merge_branch_sha = log.scan(BRANCH_MERGE_PATTERN).flatten.first - @merge_branch_sha = merge_branch_sha if merge_branch_sha - end - - def build_tests_group_from_command(line) - tests_group = TestsGroup.new - parts = line.split - while parts.any? - case part = parts.shift - when /^TEST_ENV_NUMBER=/ - tests_group.test_env_number = part.delete_prefix("TEST_ENV_NUMBER=") - when "--seed" - tests_group.seed = parts.shift - when /_spec.rb$/ - tests_group.files << part - end - end - tests_group - end -end - class Formatter def initialize(compact: false) @compact = compact @@ -798,7 +635,12 @@ formatter = Formatter.new(compact: Options.compact) report = get_failed_jobs_logs(report, formatter) -report = JobErrorsFinder.scan_logs(report, report.failed_job_logs) +scan_result = GithubActionsFailures::JobErrorsFinder.scan_logs(report.failed_job_logs) +report = report.with( + errors: scan_result.errors, + failures_explanation: scan_result.failures_explanation, + merge_branch_sha: scan_result.merge_branch_sha +) case report.run_status when "completed" diff --git a/script/report_out_of_hours_ci_failures b/script/report_out_of_hours_ci_failures new file mode 100755 index 00000000000..afd2ff9bea2 --- /dev/null +++ b/script/report_out_of_hours_ci_failures @@ -0,0 +1,371 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true + +require "rubygems" +require "bundler" +Bundler.setup(:default, :development) + +require "json" +require "optparse" +require "pathname" +require "time" +require "yaml" +require "httpx" +require "active_support/all" + +require_relative "support/github_actions_failures" + +GITHUB_API_OPENPROJECT_PREFIX = "https://api.github.com/repos/opf/openproject" +RAILS_ROOT = Pathname.new(__dir__).dirname +TEST_WORKFLOW_FILE = "test-core.yml" +PRIMARY_BRANCH_PATTERN = /\A(?:dev|release\/.+)\z/ +TEST_STEP_NAMES = ["Unit tests", "Feature tests"].freeze +DEFAULT_TIMEZONE = "Europe/Berlin" +DEFAULT_LOOKBACK_DAYS = 30 + +module OutOfHoursCiFailures + RunSummary = Data.define(:run_id, :run_number, :html_url, :head_branch, :event, :created_at, :failed_steps, :errors) + SpecSummary = Data.define( + :location, + :test_types, + :out_of_hours_count, + :in_hours_count, + :branches, + :first_seen_at, + :last_seen_at, + :out_of_hours_runs, + :in_hours_runs, + :classification + ) + + class Options + DEFAULTS = { + days: DEFAULT_LOOKBACK_DAYS, + include_pr_runs: false, + json: false, + timezone: DEFAULT_TIMEZONE, + workflow: TEST_WORKFLOW_FILE + }.freeze + + class << self + # rubocop:disable Metrics/AbcSize + def parse!(argv) + options = DEFAULTS.dup + + OptionParser.new do |parser| + parser.banner = "Usage: script/report_out_of_hours_ci_failures [options]" + + parser.on("--days DAYS", Integer, "Look back this many days (default: #{DEFAULT_LOOKBACK_DAYS})") do |value| + options[:days] = value + end + + parser.on("--include-pr-runs", "Include pull_request runs in the report") do + options[:include_pr_runs] = true + end + + parser.on("--json", "Output JSON instead of a table") do + options[:json] = true + end + + parser.on( + "--timezone NAME", + "Timezone for the in-hours/out-of-hours classification (default: #{DEFAULT_TIMEZONE})" + ) do |value| + options[:timezone] = value + end + + parser.on("--workflow FILE", "Workflow file name to inspect (default: #{TEST_WORKFLOW_FILE})") do |value| + options[:workflow] = value + end + + parser.on("-h", "--help", "Print this help") do + puts parser + exit + end + end.parse!(argv) + + options + end + # rubocop:enable Metrics/AbcSize + end + end + + class GithubClient + def initialize(cache_dir:) + @cache_dir = cache_dir + end + + def workflow_runs(workflow_file:, page:) + get_json("actions/workflows/#{workflow_file}/runs?status=completed&per_page=100&page=#{page}") + end + + def jobs(run_id) + get_json("actions/runs/#{run_id}/jobs") + end + + def log(job_id) + cached("job_#{job_id}.log") do + get_http("actions/jobs/#{job_id}/logs") + end + end + + private + + def http + @http ||= HTTPX + .plugin(:follow_redirects) + .with_headers( + "Authorization" => "Bearer #{ENV.fetch('GITHUB_TOKEN')}" + ) + end + + def github_url(path) + path.start_with?("http") ? path : "#{GITHUB_API_OPENPROJECT_PREFIX}/#{path}" + end + + def get_http(path) + response = http.get(github_url(path)) + response.raise_for_status + response.to_s + end + + def get_json(path) + JSON.parse(get_http(path)) + rescue HTTPX::HTTPError => e + body = e.response.json + raise "#{body['message']} (see #{body['documentation_url']})" + end + + def cached(name) + path = @cache_dir.join(name) + return path.read if path.file? + + content = yield + path.dirname.mkpath + path.write(content) + content + end + end + + class ReportBuilder + def initialize(options:, client:) + @options = options + @client = client + @timezone = ActiveSupport::TimeZone[options[:timezone]] || raise("Unknown timezone #{options[:timezone]}") + @since = Time.current - options[:days].days + end + + # rubocop:disable Metrics/AbcSize + def build + spec_runs = Hash.new { |hash, key| hash[key] = [] } + + each_relevant_run do |workflow_run, failed_jobs| + logs = failed_jobs.map { |job| @client.log(job.fetch("id")) } + scan_result = GithubActionsFailures::JobErrorsFinder.scan_logs(logs) + next if scan_result.errors.empty? + + failed_steps = failed_jobs.flat_map { failing_test_steps(it) }.uniq.sort + summary = RunSummary.new( + run_id: workflow_run.fetch("id"), + run_number: workflow_run.fetch("run_number"), + html_url: workflow_run.fetch("html_url"), + head_branch: workflow_run.fetch("head_branch"), + event: workflow_run.fetch("event"), + created_at: parse_time(workflow_run.fetch("created_at")), + failed_steps:, + errors: scan_result.errors.map(&:location).sort + ) + + summary.errors.each do |location| + spec_runs[location] << summary + end + end + + spec_runs + .sort_by { |location, _| location } + .map { |location, runs| summarize(location, runs) } + .sort_by { |summary| [-summary.out_of_hours_count, -summary.in_hours_count, summary.location] } + end + # rubocop:enable Metrics/AbcSize + + private + + # rubocop:disable Metrics/AbcSize + def each_relevant_run + page = 1 + done = false + + loop do + response = @client.workflow_runs(workflow_file: @options[:workflow], page:) + runs = response.fetch("workflow_runs") + break if runs.empty? + + runs.each do |workflow_run| + created_at = parse_time(workflow_run.fetch("created_at")) + if created_at < @since + done = true + break + end + + next unless include_run?(workflow_run) + + jobs = @client.jobs(workflow_run.fetch("id")).fetch("jobs") + failed_jobs = jobs.select { test_job_failure?(it) } + next if failed_jobs.empty? + + yield workflow_run, failed_jobs + end + + page += 1 + break if done + end + end + # rubocop:enable Metrics/AbcSize + + def include_run?(workflow_run) + branch = workflow_run.fetch("head_branch") + return false unless branch.match?(PRIMARY_BRANCH_PATTERN) || @options[:include_pr_runs] + return false if workflow_run.fetch("event") == "pull_request" && !@options[:include_pr_runs] + + true + end + + def test_job_failure?(job) + job.fetch("conclusion") == "failure" && failing_test_steps(job).any? + end + + def failing_test_steps(job) + job + .fetch("steps") + .filter_map { |step| step["name"] if step["conclusion"] == "failure" && TEST_STEP_NAMES.include?(step["name"]) } + end + + # rubocop:disable Metrics/AbcSize + def summarize(location, runs) + out_of_hours_runs, in_hours_runs = runs.partition { out_of_hours?(it.created_at) } + branches = runs.map(&:head_branch).uniq.sort + test_types = runs.flat_map(&:failed_steps).uniq.sort + + SpecSummary.new( + location:, + test_types:, + out_of_hours_count: out_of_hours_runs.count, + in_hours_count: in_hours_runs.count, + branches:, + first_seen_at: runs.min_by(&:created_at).created_at, + last_seen_at: runs.max_by(&:created_at).created_at, + out_of_hours_runs: out_of_hours_runs.map(&:html_url), + in_hours_runs: in_hours_runs.map(&:html_url), + classification: classify(out_of_hours_runs:, in_hours_runs:, branches:) + ) + end + # rubocop:enable Metrics/AbcSize + + def classify(out_of_hours_runs:, in_hours_runs:, branches:) + total = out_of_hours_runs.count + in_hours_runs.count + return "needs manual review" if total < 2 + return "likely regression" if branches.one? + return "likely datetime-sensitive" if in_hours_runs.empty? + + ratio = out_of_hours_runs.count.to_f / total + return "likely datetime-sensitive" if ratio >= 0.75 && branches.many? + + "likely generic flaky" + end + + def out_of_hours?(time) + local = time.in_time_zone(@timezone) + local.saturday? || local.sunday? || local.hour < 9 || local.hour >= 18 + end + + def parse_time(value) + Time.iso8601(value) + end + end + + class Formatter + def initialize(timezone:) + @timezone = timezone + end + + def print(spec_summaries, json: false) + json ? print_json(spec_summaries) : print_table(spec_summaries) + end + + private + + # rubocop:disable Metrics/AbcSize + def print_json(spec_summaries) + puts JSON.pretty_generate( + spec_summaries.map do |summary| + { + location: summary.location, + test_types: summary.test_types, + out_of_hours_count: summary.out_of_hours_count, + in_hours_count: summary.in_hours_count, + branches: summary.branches, + first_seen_at: summary.first_seen_at.in_time_zone(@timezone).iso8601, + last_seen_at: summary.last_seen_at.in_time_zone(@timezone).iso8601, + classification: summary.classification, + out_of_hours_runs: summary.out_of_hours_runs, + in_hours_runs: summary.in_hours_runs + } + end + ) + end + # rubocop:enable Metrics/AbcSize + + # rubocop:disable Metrics/AbcSize + def print_table(spec_summaries) + puts [ + "Classification".ljust(26), + "OOH".rjust(3), + "IN".rjust(3), + "Type".ljust(16), + "Branches".ljust(18), + "First seen".ljust(17), + "Last seen".ljust(17), + "Spec" + ].join(" ") + + spec_summaries.each do |summary| + puts [ + summary.classification.ljust(26), + summary.out_of_hours_count.to_s.rjust(3), + summary.in_hours_count.to_s.rjust(3), + summary.test_types.join(",").ljust(16), + truncate(summary.branches.join(","), 18).ljust(18), + summary.first_seen_at.in_time_zone(@timezone).strftime("%F %H:%M"), + summary.last_seen_at.in_time_zone(@timezone).strftime("%F %H:%M"), + summary.location + ].join(" ") + end + end + # rubocop:enable Metrics/AbcSize + + def truncate(value, length) + return value if value.length <= length + + "#{value[0, length - 3]}..." + end + end +end + +if $PROGRAM_NAME == __FILE__ + if !ENV["GITHUB_TOKEN"] + raise "Missing GITHUB_TOKEN env, go to https://github.com/settings/tokens and create one with 'repo' access" + end + + # workaround an openssl 3.6.0 issue + # https://github.com/ruby/openssl/issues/949#issuecomment-3367944960 + s = OpenSSL::X509::Store.new.tap(&:set_default_paths) + OpenSSL::SSL::SSLContext.send(:remove_const, :DEFAULT_CERT_STORE) rescue nil # rubocop:disable Style/RescueModifier + OpenSSL::SSL::SSLContext.const_set(:DEFAULT_CERT_STORE, s.freeze) + + options = OutOfHoursCiFailures::Options.parse!(ARGV) + client = OutOfHoursCiFailures::GithubClient.new(cache_dir: RAILS_ROOT.join("tmp/report_out_of_hours_ci_failures")) + builder = OutOfHoursCiFailures::ReportBuilder.new(options:, client:) + formatter = OutOfHoursCiFailures::Formatter.new(timezone: options[:timezone]) + + formatter.print(builder.build, json: options[:json]) +end diff --git a/script/support/github_actions_failures.rb b/script/support/github_actions_failures.rb new file mode 100644 index 00000000000..f6407a3be6f --- /dev/null +++ b/script/support/github_actions_failures.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true + +require "json" + +module GithubActionsFailures + Result = Data.define(:errors, :failures_explanation, :merge_branch_sha) + + class Error + attr_accessor :location, :page_html, :page_screenshot, :tests_group, :loading_error + end + + class TestsGroup + attr_accessor :test_env_number, :seed, :files + + def initialize + @files = [] + end + + def include_error?(error) + return false if error.location.nil? + + files.any? { |file| error.location.include?(file) } + end + + def inspect + "#<#{self.class} @test_env_number=#{test_env_number} @seed=#{seed} (#{files.count} files)>" + end + end + + class JobErrorsFinder + SPEC_FAILURES_PATTERN = %r{^\S+ rspec (\S+) #.+$} + SPEC_LOADING_ERRORS_PATTERN = %r{^\S+ An error occurred while loading (\S+)\.\r?$} + SCREENSHOT_PATTERN = /\{"message":"Screenshot captured for failed feature test"[^\n]+$/ + # Looks like this in the job log: + # rubocop:disable Layout/LineLength + # Process 28: TEST_ENV_NUMBER=28 RUBYOPT=-I/usr/local/bundle/bundler/gems/turbo_tests-3148ae6c3482/lib -r/usr/local/bundle/gems/bundler-2.5.23/lib/bundler/setup -W0 RSPEC_SILENCE_FILTER_ANNOUNCEMENTS=1 /usr/local/bundle/gems/bundler-2.5.23/exe/bundle exec rspec --seed 52674 --format TurboTests::JsonRowsFormatter --out tmp/test-pipes/subprocess-28 --format ParallelTests::RSpec::RuntimeLogger --out spec/support/turbo_runtime_features.log spec/features/api_docs/index_spec.rb spec/features/custom_fields/reorder_options_spec.rb spec/features/projects/projects_portfolio_spec.rb spec/features/projects/template_spec.rb spec/features/versions/edit_spec.rb spec/features/work_packages/details/markdown/description_editor_spec.rb spec/features/work_packages/table/hierarchy/hierarchy_parent_below_spec.rb spec/features/work_packages/table/inline_create/inline_create_refresh_spec.rb spec/features/work_packages/table/invalid_query_spec.rb spec/features/work_packages/tabs/activity_revisions_spec.rb + # rubocop:enable Layout/LineLength + TESTS_GROUP_PATTERN = /Process \d+: TEST_ENV_NUMBER=\d+ [^\n]+$/ + BRANCH_MERGE_PATTERN = /Merge \w{40} into (\w{40})$/ + + def self.scan_logs(logs) + finder = new + logs.each do |log| + finder.scan_log(log) + end + + Result.new( + errors: finder.errors, + failures_explanation: finder.failures_explanation, + merge_branch_sha: finder.merge_branch_sha + ) + end + + attr_reader :failures_explanation, :merge_branch_sha + + def scan_log(log) + find_failures(log) + find_failures_explanation(log) + find_loading_errors(log) + find_screenshots(log) + find_tests_groups(log) + find_merge_branch_info(log) + end + + def errors + @errors.values + end + + private + + def initialize + @errors = {} + end + + def create_error(location) + return if location.nil? + + error = Error.new + error.location = location + @errors[location] ||= error + end + + def with_matching_error(location: nil, id: nil) + error = @errors[id] || @errors[location] + yield error if error && block_given? + error + end + + def find_failures(log) + log.scan(SPEC_FAILURES_PATTERN) + .flatten + .uniq + .sort + .each do |rerun_location| + create_error(rerun_location) + end + end + + def find_failures_explanation(log) + explanations = [] + log.split("\n").each do |line| + if line.end_with?("Failures:") .. line.end_with?("Failed examples:") + explanations << line + end + end + explanations.map! { it[29..] } # Remove leading GitHub Actions log timestamp (e.g. "2024-02-05T08:37:54.5175930Z ") + explanations.reject! do |line| + line == "Failures:" || + line == "Failed examples:" || + line.include?("gems/rspec-retry-") || + line.include?("gems/webmock-") + end + @failures_explanation = explanations.join("\n") + end + + def find_loading_errors(log) + log.scan(SPEC_LOADING_ERRORS_PATTERN) + .flatten + .uniq + .sort + .each do |location| + error = create_error(location) + error.loading_error = true + end + end + + def find_screenshots(log) + log.scan(SCREENSHOT_PATTERN) + .map { JSON.parse(it) } + .each do |screenshot_info| + id = screenshot_info["test_id"] + location = screenshot_info["test_location"] + with_matching_error(location:, id:) do |error| + error.page_html = screenshot_info["html"] + error.page_screenshot = screenshot_info["image"] + end + end + end + + def find_tests_groups(log) + tests_groups = log + .scan(TESTS_GROUP_PATTERN) + .flatten + .map { build_tests_group_from_command(it) } + + errors.each do |error| + error.tests_group = tests_groups.find { it.include_error?(error) } + end + end + + def find_merge_branch_info(log) + merge_branch_sha = log.scan(BRANCH_MERGE_PATTERN).flatten.first + @merge_branch_sha = merge_branch_sha if merge_branch_sha + end + + def build_tests_group_from_command(line) + tests_group = TestsGroup.new + parts = line.split + while parts.any? + case part = parts.shift + when /^TEST_ENV_NUMBER=/ + tests_group.test_env_number = part.delete_prefix("TEST_ENV_NUMBER=") + when "--seed" + tests_group.seed = parts.shift + when /_spec.rb$/ + tests_group.files << part + end + end + tests_group + end + end +end diff --git a/spec/components/portfolios/details_component_spec.rb b/spec/components/portfolios/details_component_spec.rb index e8a0a74dfd9..31fea1f7d66 100644 --- a/spec/components/portfolios/details_component_spec.rb +++ b/spec/components/portfolios/details_component_spec.rb @@ -38,6 +38,7 @@ RSpec.describe Portfolios::DetailsComponent, type: :component do render_inline(described_class.new(...)) end + let(:reference_time) { Time.zone.local(2025, 2, 1, 12, 0, 0) } let(:user) { create(:admin) } let(:status_code_a) { "on_track" } let(:status_code_b) { "at_risk" } @@ -51,6 +52,8 @@ RSpec.describe Portfolios::DetailsComponent, type: :component do end before do + travel_to(reference_time) + create(:program, parent: portfolio, status_code: status_code_a).tap do |program_a| create(:project, parent: program_a, status_code: status_code_a).tap do |project_a| create(:project, parent: project_a, status_code: status_code_b) @@ -69,6 +72,10 @@ RSpec.describe Portfolios::DetailsComponent, type: :component do def portfolio.favorited?; false; end end + after do + travel_back + end + shared_examples "having a description and last update time" do it { expect(subject).to have_text(portfolio.description) } diff --git a/spec/contracts/work_packages/update_contract_spec.rb b/spec/contracts/work_packages/update_contract_spec.rb index bab6d8a4784..8d888aca538 100644 --- a/spec/contracts/work_packages/update_contract_spec.rb +++ b/spec/contracts/work_packages/update_contract_spec.rb @@ -384,9 +384,21 @@ RSpec.describe WorkPackages::UpdateContract do context "for a user having only the assign_versions permission" do let(:permissions) { %i[assign_versions] } - it "includes all attributes except version_id" do + it "includes version_id only" do expect(subject) - .to include("version_id", "version") + .to include("version_id", "version", "lock_version_id", "lock_version") + + expect(subject) + .not_to include("subject", "start_date", "description") + end + end + + context "for a user having only the manage_sprint_items permission" do + let(:permissions) { %i[manage_sprint_items] } + + it "includes version_id only" do + expect(subject) + .to include("version_id", "version", "lock_version_id", "lock_version") expect(subject) .not_to include("subject", "start_date", "description") diff --git a/spec/controllers/projects_settings_menu_controller_spec.rb b/spec/controllers/projects_settings_menu_controller_spec.rb index 4154892b372..c6a7bbde3ca 100644 --- a/spec/controllers/projects_settings_menu_controller_spec.rb +++ b/spec/controllers/projects_settings_menu_controller_spec.rb @@ -105,8 +105,6 @@ RSpec.describe Projects::Settings::ModulesController, "menu" do it_behaves_like "renders the modules show page" it_behaves_like "has selector", "#main-menu a.wiki-example-menu-item" - - it_behaves_like "has selector", "#main-menu a.wiki-sub-menu-item" end end diff --git a/spec/features/custom_styles/tabs_navigation_spec.rb b/spec/features/custom_styles/tabs_navigation_spec.rb index 2ab45898a2e..d2304dd356c 100644 --- a/spec/features/custom_styles/tabs_navigation_spec.rb +++ b/spec/features/custom_styles/tabs_navigation_spec.rb @@ -84,7 +84,7 @@ RSpec.describe "Tabs navigation and content switching on the admin/design page" expect(page).to have_current_path custom_style_path(tab: "branding") # remove the logo and redirect to the branding tab - custom_style.send :remove_logo + custom_style.send :remove_logo! expect(File.exist?(file_path)).to be false expect(page).to have_current_path custom_style_path(tab: "branding") end diff --git a/spec/features/menu_items/wiki_menu_item_spec.rb b/spec/features/menu_items/wiki_menu_item_spec.rb index be9d357814a..dad3558d3ff 100644 --- a/spec/features/menu_items/wiki_menu_item_spec.rb +++ b/spec/features/menu_items/wiki_menu_item_spec.rb @@ -64,11 +64,11 @@ RSpec.describe "Wiki menu items", # Create two items with identical slugs (one with space, which is removed) let(:item1) do MenuItems::WikiMenuItem.new(navigatable_id: wiki.id, - parent: parent_menu, title: "Item 1", name: "slug") + parent: nil, title: "Item 1", name: "slug ") end let(:item2) do MenuItems::WikiMenuItem.new(navigatable_id: wiki.id, - parent: parent_menu, title: "Item 2", name: "slug ") + parent: nil, title: "Item 2", name: "slug") end it "one is invalid and deleted during visit" do @@ -114,38 +114,27 @@ RSpec.describe "Wiki menu items", expect(page) .to have_current_path(project_wiki_path(project, wiki_page)) - # modifying the menu item to a different name and to be a subpage + # modifying the menu item to a different name page.find_test_selector("wiki-more-dropdown-menu").click page.find_test_selector("wiki-configure-menu-action-menu-item").click wait_for_network_idle fill_in "Name of menu item", with: "Custom page name" - choose "Show as submenu item of" - - select other_wiki_page.slug, from: "parent_wiki_menu_item" click_link_or_button "Save" wait_for_network_idle - # the other page is now the main heading + # the custom name is used as the main heading expect(page) - .to have_css(".main-menu--children-menu-header", text: other_wiki_page.title) + .to have_css(".main-menu--children-menu-header", text: "Custom page name") - expect(page) - .to have_css(".wiki-menu--sub-item", text: "Custom page name") - - find(".wiki-menu--sub-item", text: "Custom page name").click - wait_for_network_idle - - expect(page) - .to have_current_path(project_wiki_path(project, wiki_page)) - - # the submenu item is not visible on top level + # the item is visible on top level + visit(project_wiki_path(project, wiki_page)) find(".main-menu--arrow-left-to-project").click expect(page) - .to have_no_css(".main-item-wrapper", text: "Custom page name") + .to have_css(".main-item-wrapper", text: "Custom page name") # deleting the page will remove the menu item visit project_wiki_path(project, wiki_page) diff --git a/spec/features/notifications/notification_center/notification_center_date_alert_mention_spec.rb b/spec/features/notifications/notification_center/notification_center_date_alert_mention_spec.rb index 496eebf61b7..94dac8faead 100644 --- a/spec/features/notifications/notification_center/notification_center_date_alert_mention_spec.rb +++ b/spec/features/notifications/notification_center/notification_center_date_alert_mention_spec.rb @@ -12,9 +12,9 @@ RSpec.describe "Notification center date alert and mention", create(:user, member_with_permissions: { project => %w[view_work_packages] }) end - shared_let(:work_package) { create(:work_package, project:, due_date: 1.day.ago) } - - shared_let(:notification_mention) do + let(:reference_time) { Time.zone.local(2025, 1, 8, 12, 0, 0) } + let(:work_package) { create(:work_package, project:, due_date: 1.day.ago.to_date) } + let!(:notification_mention) do create(:notification, reason: :mentioned, recipient: user, @@ -22,7 +22,7 @@ RSpec.describe "Notification center date alert and mention", actor:) end - shared_let(:notification_date_alert) do + let!(:notification_date_alert) do create(:notification, reason: :date_alert_due_date, recipient: user, @@ -32,11 +32,16 @@ RSpec.describe "Notification center date alert and mention", let(:center) { Pages::Notifications::Center.new } before do + travel_to(reference_time) login_as user visit notifications_center_path wait_for_reload end + after do + travel_back + end + context "with date alerts ee", with_ee: %i[date_alerts] do it "shows only the date alert time, not the mentioned author" do center.within_item(notification_date_alert) do diff --git a/spec/features/notifications/notification_center/notification_center_reminder_spec.rb b/spec/features/notifications/notification_center/notification_center_reminder_spec.rb index f2d9ddb7abd..f7b0d877061 100644 --- a/spec/features/notifications/notification_center/notification_center_reminder_spec.rb +++ b/spec/features/notifications/notification_center/notification_center_reminder_spec.rb @@ -13,10 +13,11 @@ RSpec.describe "Notification center reminder, mention and date alert", create(:user, member_with_permissions: { project => %w[view_work_packages] }) end - shared_let(:work_package) { create(:work_package, project:, due_date: 1.day.ago) } - shared_let(:work_package2) { create(:work_package, project:) } + let(:reference_time) { Time.zone.local(2025, 1, 8, 12, 0, 0) } + let(:work_package) { create(:work_package, project:, due_date: 1.day.ago.to_date) } + let(:work_package2) { create(:work_package, project:) } - shared_let(:notification_mentions) do + let!(:notification_mentions) do [create(:notification, reason: :mentioned, recipient: user, @@ -29,29 +30,34 @@ RSpec.describe "Notification center reminder, mention and date alert", actor: actor)] end - shared_let(:notification_date_alert) do + let!(:notification_date_alert) do create(:notification, reason: :date_alert_due_date, recipient: user, resource: work_package) end - shared_let(:notification_reminder) do + let!(:notification_reminder) do create_reminder_notification_for(work_package: work_package, user: user) end - shared_let(:notification_reminder2) do + let!(:notification_reminder2) do create_reminder_notification_for(work_package: work_package2, user: user) end let(:center) { Pages::Notifications::Center.new } before do + travel_to(reference_time) login_as user visit notifications_center_path wait_for_reload end + after do + travel_back + end + context "with a reminder, mention and date alert" do it "shows the reminder alert within aggregation with date alert + reminder note" do center.within_item(notification_reminder) do diff --git a/spec/features/work_packages/edit_on_assign_version_permission_spec.rb b/spec/features/work_packages/edit_on_assign_version_permission_spec.rb index bf1f68515ce..ab0d2e05907 100644 --- a/spec/features/work_packages/edit_on_assign_version_permission_spec.rb +++ b/spec/features/work_packages/edit_on_assign_version_permission_spec.rb @@ -10,7 +10,7 @@ RSpec.describe "edit work package", :js do lastname: "Guy", member_with_permissions: { project => permissions }) end - let(:permissions) { %i[view_work_packages assign_versions] } + let(:permissions) { %i[view_work_packages manage_sprint_items assign_versions] } let(:cf_all) do create(:work_package_custom_field, is_for_all: true, field_format: "text") @@ -43,7 +43,21 @@ RSpec.describe "edit work package", :js do context "as a user having only the assign_versions permission" do it "can only change the version" do - wp_page.update_attributes version: version.name + wp_page.fill_in_attributes version: version.name + + wp_page.expect_toast(message: "Successful update") + wp_page.expect_attributes version: version.name + + subject_field = wp_page.work_package_field("subject") + subject_field.expect_read_only + end + end + + context "as a user having only the manage_sprint_items permission" do + let(:permissions) { %i[view_work_packages manage_sprint_items] } + + it "can only change the version" do + wp_page.fill_in_attributes version: version.name wp_page.expect_toast(message: "Successful update") wp_page.expect_attributes version: version.name diff --git a/spec/features/work_packages/reminders_spec.rb b/spec/features/work_packages/reminders_spec.rb index 9fd51cab04c..ad0b68b9eb1 100644 --- a/spec/features/work_packages/reminders_spec.rb +++ b/spec/features/work_packages/reminders_spec.rb @@ -32,6 +32,7 @@ require "spec_helper" RSpec.describe "Work package reminder modal", :js do + let(:reference_time) { Time.find_zone!("Europe/Berlin").local(2025, 1, 8, 12, 0, 0) } let!(:project) { create(:project) } let!(:work_package) { create(:work_package, project:) } let!(:role_that_allows_managing_own_reminders) do @@ -54,6 +55,14 @@ RSpec.describe "Work package reminder modal", let(:work_package_page) { Pages::FullWorkPackage.new(work_package) } let(:center) { Pages::Notifications::Center.new } + before do + travel_to(reference_time) + end + + after do + travel_back + end + context "with permissions to manage own reminders" do current_user { user_with_permissions } diff --git a/spec/features/work_packages/table/queries/filter_spec.rb b/spec/features/work_packages/table/queries/filter_spec.rb index 5e70bdda107..0e4ab78b8fb 100644 --- a/spec/features/work_packages/table/queries/filter_spec.rb +++ b/spec/features/work_packages/table/queries/filter_spec.rb @@ -556,26 +556,36 @@ RSpec.describe "filter work packages", :js do end describe "datetime filters" do + shared_let(:business_day_at_noon) { Time.find_zone!("Europe/Kyiv").local(2025, 1, 8, 12, 0, 0) } + + before do + travel_to(business_day_at_noon) + end + + after do + travel_back + end + shared_let(:wp_updated_today) do create(:work_package, subject: "Created today", project:, - created_at: Time.current.change(hour: 12), - updated_at: Time.current.change(hour: 12)) + created_at: business_day_at_noon, + updated_at: business_day_at_noon) end shared_let(:wp_updated_3d_ago) do create(:work_package, subject: "Created 3d ago", project:, - created_at: 3.days.ago, - updated_at: 3.days.ago) + created_at: business_day_at_noon - 3.days, + updated_at: business_day_at_noon - 3.days) end shared_let(:wp_updated_5d_ago) do create(:work_package, subject: "Created 5d ago", project:, - created_at: 5.days.ago, - updated_at: 5.days.ago) + created_at: business_day_at_noon - 5.days, + updated_at: business_day_at_noon - 5.days) end it "filters on date by created_at (Regression #28459)" do diff --git a/spec/features/work_packages/table/queries/mobile_date_filter_spec.rb b/spec/features/work_packages/table/queries/mobile_date_filter_spec.rb index 9f09f6e18c2..7cf0cdabc96 100644 --- a/spec/features/work_packages/table/queries/mobile_date_filter_spec.rb +++ b/spec/features/work_packages/table/queries/mobile_date_filter_spec.rb @@ -36,17 +36,23 @@ RSpec.describe "mobile date filter work packages", :js do shared_let(:wp_table) { Pages::WorkPackagesTable.new(project) } shared_let(:wp_cards) { Pages::WorkPackageCards.new(project) } shared_let(:filters) { Components::WorkPackages::Filters.new } - shared_let(:work_package_with_due_date) { create(:work_package, project:, due_date: Date.current) } - shared_let(:work_package_without_due_date) { create(:work_package, project:, due_date: 5.days.from_now) } + shared_let(:business_day_at_noon) { Time.zone.local(2025, 1, 8, 12, 0, 0) } + shared_let(:work_package_with_due_date) { create(:work_package, project:, due_date: business_day_at_noon.to_date) } + shared_let(:work_package_without_due_date) { create(:work_package, project:, due_date: business_day_at_noon.to_date + 5.days) } current_user { user } include_context "with mobile screen size" before do + travel_to(business_day_at_noon) wp_table.visit! end + after do + travel_back + end + context "when filtering between finish date" do it "allows filtering, saving and retrieving and altering the saved filter" do filters.open @@ -59,9 +65,9 @@ RSpec.describe "mobile date filter work packages", :js do clear_input_field_contents(start_field) clear_input_field_contents(end_field) - start_field.set 1.day.ago.to_date + start_field.set business_day_at_noon.to_date - 1.day start_field.send_keys :tab - end_field.set Date.current + end_field.set business_day_at_noon.to_date end_field.send_keys :tab wait_for_reload @@ -76,7 +82,7 @@ RSpec.describe "mobile date filter work packages", :js do last_query = Query.last date_filter = last_query.filters.last - expect(date_filter.values).to eq [1.day.ago.to_date.iso8601, Date.current.iso8601] + expect(date_filter.values).to eq [(business_day_at_noon.to_date - 1.day).iso8601, business_day_at_noon.to_date.iso8601] end end @@ -90,7 +96,7 @@ RSpec.describe "mobile date filter work packages", :js do expect(date_field["type"]).to eq "date" clear_input_field_contents(date_field) - date_field.set Date.current + date_field.set business_day_at_noon.to_date wait_for_reload loading_indicator_saveguard @@ -104,7 +110,7 @@ RSpec.describe "mobile date filter work packages", :js do last_query = Query.last date_filter = last_query.filters.last - expect(date_filter.values).to eq [Date.current.iso8601] + expect(date_filter.values).to eq [business_day_at_noon.to_date.iso8601] end end end diff --git a/spec/fixtures/import/jira/issue.json b/spec/fixtures/import/jira/issue.json new file mode 100644 index 00000000000..3068393a935 --- /dev/null +++ b/spec/fixtures/import/jira/issue.json @@ -0,0 +1,92 @@ +{ + "id": "10100", + "key": "DYX-1", + "self": "https://jira-software.local/rest/api/2/issue/10100", + "fields": { + "summary": "Kanban cards represent work items", + "description": "*About Kanban*\n\nKanban is part of the Toyota Lean Manufacturing methodology.", + "created": "2026-02-04T09:51:03.000+0000", + "updated": "2026-03-10T12:00:59.000+0000", + "creator": { + "key": "JIRAUSER10000", + "name": "p.balashou", + "displayName": "Pavel Balashou", + "emailAddress": "p.balashou@openproject.com", + "active": true + }, + "assignee": { + "key": "JIRAUSER10000", + "name": "p.balashou", + "displayName": "Pavel Balashou", + "emailAddress": "p.balashou@openproject.com", + "active": true + }, + "issuetype": { + "id": "10100", + "name": "Task", + "description": "A task that needs to be done.", + "subtask": false + }, + "status": { + "id": "3", + "name": "In Progress", + "description": "This issue is being actively worked on." + }, + "priority": { + "id": "1", + "name": "Highest" + }, + "comment": { + "total": 1, + "comments": [ + { + "id": "10100", + "body": "Created 2 hours 36 minutes ago", + "author": { + "key": "JIRAUSER10000", + "name": "p.balashou", + "displayName": "Pavel Balashou", + "emailAddress": "p.balashou@openproject.com" + }, + "created": "2026-02-04T09:51:03.503+0000", + "updated": "2026-02-04T09:51:03.503+0000" + } + ] + }, + "attachment": [ + { + "id": "10000", + "filename": "solid-color-image.png", + "content": "https://jira-software.local/secure/attachment/10000/solid-color-image.png", + "mimeType": "image/png", + "size": 38697, + "author": { + "key": "JIRAUSER10000", + "name": "p.balashou", + "displayName": "Pavel Balashou", + "emailAddress": "p.balashou@openproject.com" + }, + "created": "2026-02-05T12:29:19.045+0000" + } + ] + }, + "changelog": { + "total": 1, + "histories": [ + { + "id": "10133", + "items": [ + { + "field": "Attachment", + "toString": "solid-color-image.png" + } + ], + "author": { + "key": "JIRAUSER10000", + "name": "p.balashou" + }, + "created": "2026-02-05T12:29:19.257+0000" + } + ] + } +} diff --git a/spec/fixtures/import/jira/project.json b/spec/fixtures/import/jira/project.json new file mode 100644 index 00000000000..bee84e91bd1 --- /dev/null +++ b/spec/fixtures/import/jira/project.json @@ -0,0 +1,23 @@ +{ + "id": "10242", + "key": "DYX", + "name": "Zombie Engine", + "self": "https://jira-software.local/rest/api/2/project/10242", + "expand": "description,lead,createdAt,createdBy,lastUpdatedAt,lastUpdatedBy,url,projectKeys", + "archived": false, + "avatarUrls": { + "16x16": "https://jira-software.local/secure/projectavatar?size=xsmall&avatarId=10324", + "24x24": "https://jira-software.local/secure/projectavatar?size=small&avatarId=10324", + "32x32": "https://jira-software.local/secure/projectavatar?size=medium&avatarId=10324", + "48x48": "https://jira-software.local/secure/projectavatar?avatarId=10324" + }, + "description": "Test project created by jira_projects.rb script at 2026-02-13 09:32:25 UTC", + "projectKeys": ["DYX"], + "projectTypeKey": "software", + "projectCategory": { + "id": "10008", + "name": "Legacy", + "self": "https://jira-software.local/rest/api/2/projectCategory/10008", + "description": "Maintenance and migration projects" + } +} diff --git a/spec/fixtures/import/jira/user.json b/spec/fixtures/import/jira/user.json new file mode 100644 index 00000000000..6d025e3505d --- /dev/null +++ b/spec/fixtures/import/jira/user.json @@ -0,0 +1,10 @@ +{ + "key": "JIRAUSER10000", + "name": "p.balashou", + "displayName": "Pavel Balashou", + "emailAddress": "p.balashou@openproject.com", + "active": true, + "groups": { + "items": [] + } +} diff --git a/spec/lib/api/v3/work_packages/work_package_representer_spec.rb b/spec/lib/api/v3/work_packages/work_package_representer_spec.rb index 5de3c09d359..e8abbfe723d 100644 --- a/spec/lib/api/v3/work_packages/work_package_representer_spec.rb +++ b/spec/lib/api/v3/work_packages/work_package_representer_spec.rb @@ -490,6 +490,20 @@ RSpec.describe API::V3::WorkPackages::WorkPackageRepresenter do end end + context "when user lacks edit permission but has manage_sprint_items" do + let(:permissions) { all_permissions - [:edit_work_packages] + [:manage_sprint_items] } + + it_behaves_like "has an untitled link" do + let(:link) { "update" } + let(:href) { api_v3_paths.work_package_form(work_package.id) } + end + + it_behaves_like "has an untitled link" do + let(:link) { "updateImmediately" } + let(:href) { api_v3_paths.work_package(work_package.id) } + end + end + context "when user lacks edit permission but has change_work_package_status" do let(:permissions) { all_permissions - [:edit_work_packages] + [:change_work_package_status] } diff --git a/spec/migrations/fix_sprint_role_dependencies_spec.rb b/spec/migrations/fix_sprint_role_dependencies_spec.rb new file mode 100644 index 00000000000..68211e2ee51 --- /dev/null +++ b/spec/migrations/fix_sprint_role_dependencies_spec.rb @@ -0,0 +1,143 @@ +# 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 "spec_helper" + +require Rails.root.join("db/migrate/20260304160505_fix_sprint_role_dependencies") + +RSpec.describe FixSprintRoleDependencies, type: :model do + let!(:role) { create(:project_role, permissions:, add_public_permissions: false) } + + subject(:migrate) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } } + + describe "completing depencencies for view_sprints permission" do + context "when view_work_packages is missing" do + let(:permissions) { [:view_sprints] } + + it "adds view_work_packages" do + expect { migrate } + .to change { role.reload.permissions } + .from(match_array(%i[view_sprints])) + .to(match_array(%i[view_sprints view_work_packages])) + end + end + + context "when view_work_packages is present" do + let(:permissions) { %i[view_sprints view_work_packages] } + + it "does not duplicate view_work_packages" do + expect { migrate }.not_to change { role.reload.permissions } + end + end + end + + describe "completing dependencies for create_sprints permission" do + context "when view_sprints and view_work_packages are missing" do + let(:permissions) { [:create_sprints] } + + it "adds view_sprints and view_work_packages" do + expect { migrate } + .to change { role.reload.permissions } + .from(match_array(%i[create_sprints])) + .to(match_array(%i[create_sprints view_sprints view_work_packages])) + end + end + + context "when view_work_packages is present" do + let(:permissions) { %i[create_sprints view_work_packages] } + + it "adds view_sprints" do + expect { migrate } + .to change { role.reload.permissions } + .from(match_array(%i[create_sprints view_work_packages])) + .to(match_array(%i[create_sprints view_sprints view_work_packages])) + end + end + + context "when view_sprints and view_work_packages are present" do + let(:permissions) { %i[create_sprints view_sprints view_work_packages] } + + it "does not change the permissions" do + expect { migrate }.not_to change { role.reload.permissions } + end + end + end + + describe "completing dependencies for manage_sprint_items permission" do + context "when view_sprints and view_work_packages are missing" do + let(:permissions) { [:manage_sprint_items] } + + it "adds view_sprints and view_work_packages" do + expect { migrate } + .to change { role.reload.permissions } + .from(match_array(%i[manage_sprint_items])) + .to(match_array(%i[manage_sprint_items view_sprints view_work_packages])) + end + end + + context "when view_work_packages is present" do + let(:permissions) { %i[manage_sprint_items view_work_packages] } + + it "adds view_sprints" do + expect { migrate } + .to change { role.reload.permissions } + .from(match_array(%i[manage_sprint_items view_work_packages])) + .to(match_array(%i[manage_sprint_items view_sprints view_work_packages])) + end + end + + context "when view_sprints and view_work_packages are present" do + let(:permissions) { %i[manage_sprint_items view_sprints view_work_packages] } + + it "does not change the permissions" do + expect { migrate }.not_to change { role.reload.permissions } + end + end + end + + describe "completing combined dependencies with view_work_packages missing" do + let(:permissions) { %i[view_sprints create_sprints manage_sprint_items] } + + it "adds view_work_packages without duplicating view_sprints" do + expect { migrate } + .to change { role.reload.permissions } + .from(match_array(%i[view_sprints create_sprints manage_sprint_items])) + .to(match_array(%i[view_sprints create_sprints manage_sprint_items view_work_packages])) + end + end + + describe "not copmpleting dependencies for other roles" do + let(:permissions) { [:edit_work_packages] } + + it "does not change the permissions" do + expect { migrate }.not_to change { role.reload.permissions } + end + end +end diff --git a/spec/migrations/migrate_backlogs_permissions_spec.rb b/spec/migrations/migrate_backlogs_permissions_spec.rb new file mode 100644 index 00000000000..67703ea716a --- /dev/null +++ b/spec/migrations/migrate_backlogs_permissions_spec.rb @@ -0,0 +1,127 @@ +# 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 "spec_helper" +require Rails.root.join("db/migrate/20260212145213_migrate_backlogs_permissions") + +RSpec.describe MigrateBacklogsPermissions, type: :model do + subject(:migrate) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } } + + # source permission => expected permissions after up migration. + up_mapping = { + view_master_backlog: %i[view_sprints], + view_taskboards: %i[view_sprints], + update_sprints: %i[create_sprints], + manage_versions: %i[manage_versions create_sprints], + assign_versions: %i[assign_versions manage_sprint_items] + } + + # source permission => expected permissions after rollback. + # Backlogs-specific permissions (view_master_backlog, view_taskboards, update_sprints) + # are lost because the down migration cannot determine the original source. + down_mapping = { + view_master_backlog: [], + view_taskboards: [], + update_sprints: [], + manage_versions: %i[manage_versions], + assign_versions: %i[assign_versions] + } + + let(:all_source_permissions) do + %i(view_master_backlog + view_taskboards + update_sprints + manage_versions + assign_versions) + end + + let!(:role) { create(:project_role, permissions:, add_public_permissions: false) } + + describe "migrating up" do + context "with a role having no backlogs permissions" do + let(:permissions) { [] } + + it "does not change permissions" do + expect { migrate }.not_to change { role.reload.permissions } + end + end + + up_mapping.each do |source_permission, expected_permissions| + context "with a role having only :#{source_permission} permission" do + let(:permissions) { [source_permission] } + + it "results in #{expected_permissions}" do + expect { migrate } + .to change { role.reload.permissions } + .from(contain_exactly(source_permission)) + .to(match_array(expected_permissions)) + end + end + end + + context "with a role combining all source permissions" do + let(:permissions) { all_source_permissions } + + it "migrates to all new permissions plus retained core permissions" do + expect { migrate } + .to change { role.reload.permissions } + .to(match_array(%i[view_sprints create_sprints manage_sprint_items manage_versions assign_versions])) + end + end + end + + describe "migrating down" do + subject(:rollback) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:down) } } + + before { migrate } + + down_mapping.each do |source_permission, expected_permissions| + context "with a role originally having only :#{source_permission} permission" do + let(:permissions) { [source_permission] } + + it "retains #{expected_permissions.empty? ? 'no' : expected_permissions} permissions" do + expect { rollback } + .to change { role.reload.permissions } + .to(match_array(expected_permissions)) + end + end + end + + context "with a role combining all source permissions" do + let(:permissions) { all_source_permissions } + + it "retains only core permissions that were not deleted during up" do + expect { rollback } + .to change { role.reload.permissions } + .to(match_array(%i[manage_versions assign_versions])) + end + end + end +end diff --git a/spec/migrations/rename_comment_permissions_spec.rb b/spec/migrations/rename_comment_permissions_spec.rb new file mode 100644 index 00000000000..e64fcb36094 --- /dev/null +++ b/spec/migrations/rename_comment_permissions_spec.rb @@ -0,0 +1,95 @@ +# 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 "spec_helper" +require Rails.root.join("db/migrate/20250422072119_rename_comment_permissions") + +RSpec.describe RenameCommentPermissions, type: :model do + subject(:migrate) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:up) } } + + let(:old_permissions) do + %i[add_work_package_notes edit_own_work_package_notes edit_work_package_notes + view_comments_with_restricted_visibility add_comments_with_restricted_visibility + edit_own_comments_with_restricted_visibility edit_others_comments_with_restricted_visibility] + end + let(:new_permissions) do + %i[add_work_package_comments edit_own_work_package_comments edit_work_package_comments + view_internal_comments add_internal_comments + edit_own_internal_comments edit_others_internal_comments] + end + + let!(:empty_role) { create(:project_role) } + let!(:role_with_all) do + create(:project_role, permissions: old_permissions, add_public_permissions: false) + end + let!(:role_with_notes_only) do + create(:project_role, permissions: %i[add_work_package_notes edit_own_work_package_notes], add_public_permissions: false) + end + + describe "migrating up" do + it "does not add permissions to a role without comment permissions" do + expect { migrate }.not_to change { empty_role.reload.permissions } + end + + it "renames all comment permissions" do + expect { migrate } + .to change { role_with_all.reload.permissions } + .from(match_array(old_permissions)) + .to(match_array(new_permissions)) + end + + it "renames only the matching permissions" do + expect { migrate } + .to change { role_with_notes_only.reload.permissions } + .from(match_array(%i[add_work_package_notes edit_own_work_package_notes])) + .to(match_array(%i[add_work_package_comments edit_own_work_package_comments])) + end + end + + describe "migrating down" do + subject(:rollback) { ActiveRecord::Migration.suppress_messages { described_class.migrate(:down) } } + + before { migrate } + + it "reverts all comment permissions" do + expect { rollback } + .to change { role_with_all.reload.permissions } + .from(match_array(new_permissions)) + .to(match_array(old_permissions)) + end + + it "reverts only the matching permissions" do + expect { rollback } + .to change { role_with_notes_only.reload.permissions } + .from(match_array(%i[add_work_package_comments edit_own_work_package_comments])) + .to(match_array(%i[add_work_package_notes edit_own_work_package_notes])) + end + end +end diff --git a/spec/models/custom_style_spec.rb b/spec/models/custom_style_spec.rb index 2101b05d698..b484d0be4f6 100644 --- a/spec/models/custom_style_spec.rb +++ b/spec/models/custom_style_spec.rb @@ -35,17 +35,24 @@ RSpec.describe CustomStyle do let!(:file_path) { custom_style.send(image).file.path } - before do - custom_style.send :"remove_#{image}" - end + subject { custom_style.send :"remove_#{image}!" } it "deletes the file" do + subject + expect(File.exist?(file_path)).to be false end it "clears the file mount column" do + subject + expect(custom_style.reload.send(image).file).to be_nil end + + it "updates the model" do + expect { subject } + .to change(custom_style, :updated_at) + end end describe "#remove_favicon" do diff --git a/spec/models/enabled_module_spec.rb b/spec/models/enabled_module_spec.rb index 7b1edf59f8e..773f876f722 100644 --- a/spec/models/enabled_module_spec.rb +++ b/spec/models/enabled_module_spec.rb @@ -34,6 +34,37 @@ RSpec.describe EnabledModule do # Force reload, as association is not always(?) showing let(:project) { create(:project, enabled_module_names: modules).reload } + describe "MODULE_DISABLED event" do + let(:modules) { %w[wiki] } + + it "fires the event when a module is removed from the collection" do + disabled_module = project.enabled_modules.find_by(name: "wiki") + + allow(OpenProject::Notifications) + .to receive(:send) + .with(OpenProject::Events::MODULE_DISABLED, disabled_module:) + + project.enabled_module_names = [] + + expect(OpenProject::Notifications) + .to have_received(:send) + .with(OpenProject::Events::MODULE_DISABLED, disabled_module:) + .once + end + + it "does not fire the event when creating a module" do + project.enabled_module_names = [] + + allow(OpenProject::Notifications) + .to receive(:send) + .with(OpenProject::Events::MODULE_DISABLED, disabled_module: anything) + + project.enabled_module_names = ["wiki"] + + expect(OpenProject::Notifications).not_to have_received(:send) + end + end + describe "#wiki" do let(:modules) { %w[wiki] } @@ -48,11 +79,11 @@ RSpec.describe EnabledModule do expect do project.enabled_module_names = [] project.reload - end.not_to change { Wiki.count } + end.not_to change(Wiki, :count) expect do project.enabled_module_names = ["wiki"] - end.not_to change { Wiki.count } + end.not_to change(Wiki, :count) expect(project.wiki).not_to be_nil end @@ -91,11 +122,11 @@ RSpec.describe EnabledModule do expect do project.enabled_module_names = [] project.reload - end.not_to change { Repository.count } + end.not_to change(Repository, :count) expect do project.enabled_module_names = ["repository"] - end.not_to change { Repository.count } + end.not_to change(Repository, :count) expect(project.repository).not_to be_nil end diff --git a/spec/models/permitted_params_spec.rb b/spec/models/permitted_params_spec.rb index afdd270a5d5..a7fa32414c1 100644 --- a/spec/models/permitted_params_spec.rb +++ b/spec/models/permitted_params_spec.rb @@ -549,6 +549,12 @@ RSpec.describe PermittedParams do it_behaves_like "allows params" end + describe "sprint_id" do + let(:hash) { { "sprint_id" => "1" } } + + it_behaves_like "allows params" + end + describe "notes" do let(:hash) { { "journal_notes" => "blubs" } } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9370a5db8d9..322c4d7cc1d 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -540,6 +540,23 @@ RSpec.describe Project do end end + describe "#custom_values_for_custom_field" do + let(:custom_field) { create(:list_project_custom_field, multi_value: true) } + # intentionally out of order + let!(:cv2) { create(:custom_value, id: 1002, customized: project, custom_field:) } + let!(:cv1) { create(:custom_value, id: 1001, customized: project, custom_field:) } + let!(:cv3) { create(:custom_value, id: 1003, customized: project, custom_field:) } + + before do + allow(project).to receive(:available_custom_fields) { ProjectCustomField.all } + end + + it "returns values ordered by id" do + values = project.custom_values_for_custom_field(custom_field) + expect(values).to eq([cv1, cv2, cv3]) + end + end + describe "url identifier" do let(:reserved) do Rails.application.routes.routes diff --git a/spec/requests/api/v3/work_packages/show_resource_spec.rb b/spec/requests/api/v3/work_packages/show_resource_spec.rb index d4f4b971168..5eba4712539 100644 --- a/spec/requests/api/v3/work_packages/show_resource_spec.rb +++ b/spec/requests/api/v3/work_packages/show_resource_spec.rb @@ -412,8 +412,17 @@ RSpec.describe "API v3 Work package resource", end context "when the timestamps are relative date keywords" do + let(:business_day_at_noon) { Time.zone.parse("2025-01-08T12:00:00Z") } let(:timestamps) { [Timestamp.new("oneWeekAgo@12:00+00:00"), Timestamp.now] } + before do + travel_to(business_day_at_noon) + end + + after do + travel_back + end + it "has an embedded link to the baseline work package" do expect(subject) .to be_json_eql(api_v3_paths.work_package(work_package.id, timestamps: timestamps.first).to_json) @@ -421,11 +430,9 @@ RSpec.describe "API v3 Work package resource", end it "has the absolute timestamps within the self link" do - Timecop.freeze do - expect(subject) - .to be_json_eql(api_v3_paths.work_package(work_package.id, timestamps: timestamps.map(&:absolute)).to_json) - .at_path("_links/self/href") - end + expect(subject) + .to be_json_eql(api_v3_paths.work_package(work_package.id, timestamps: timestamps.map(&:absolute)).to_json) + .at_path("_links/self/href") end describe "attributesByTimestamp" do @@ -456,7 +463,7 @@ RSpec.describe "API v3 Work package resource", # Travel 1 day to test the href not being cached, because the # relative date keyword has a fixed hour part, which means the timestamp # will change its value only in 1 day units - Timecop.travel 1.day do + travel 1.day do get get_path end end.to change { @@ -468,7 +475,7 @@ RSpec.describe "API v3 Work package resource", it "does not cache the attributes" do get get_path expect do - Timecop.travel 2.days do + travel 2.days do get get_path end end.to change { @@ -486,7 +493,7 @@ RSpec.describe "API v3 Work package resource", it "is not cached" do get get_path expect do - Timecop.travel 2.days do + travel 2.days do get get_path end end.to change { diff --git a/spec/scripts/out_of_hours_ci_failures/report_builder_spec.rb b/spec/scripts/out_of_hours_ci_failures/report_builder_spec.rb new file mode 100644 index 00000000000..6c08091558c --- /dev/null +++ b/spec/scripts/out_of_hours_ci_failures/report_builder_spec.rb @@ -0,0 +1,183 @@ +# frozen_string_literal: true + +require "spec_helper" + +module OutOfHoursCiFailures +end + +load Rails.root.join("script/report_out_of_hours_ci_failures") + +RSpec.describe OutOfHoursCiFailures::ReportBuilder do + let(:options) do + { + days: 30, + include_pr_runs: false, + json: false, + timezone: "Europe/Berlin", + workflow: "test-core.yml" + } + end + + let(:client) { instance_spy(OutOfHoursCiFailures::GithubClient) } + let(:builder) { described_class.new(options:, client:) } + let(:run_id) { 1001 } + + before do + allow(Time).to receive(:current).and_return(Time.zone.parse("2026-03-08 12:00:00 UTC")) + end + + it "classifies boundary times using the configured timezone" do + stub_runs( + workflow(run_id:, created_at: "2026-03-03T07:59:00Z") + ) + stub_jobs(run_id => failed_jobs("Feature tests")) + stub_logs("spec/features/example_spec.rb:10") + + summary = builder.build.first + + expect(summary.out_of_hours_count).to eq(1) + expect(summary.in_hours_count).to eq(0) + end + + it "ignores build failures and keeps only unit and feature test failures" do + stub_runs( + workflow(run_id:, created_at: "2026-03-03T08:00:00Z"), + workflow(run_id: 1002, created_at: "2026-03-03T09:00:00Z") + ) + stub_jobs( + run_id => build_failure_jobs, + 1002 => failed_jobs("Unit tests") + ) + stub_logs("spec/models/example_spec.rb:12") + + summaries = builder.build + + expect(summaries.map(&:location)).to eq(["spec/models/example_spec.rb:12"]) + end + + it "classifies repeated out-of-hours failures on multiple branches as likely datetime-sensitive" do + stub_runs( + workflow(run_id:, branch: "dev", created_at: "2026-03-03T07:59:00Z"), + workflow( + run_id: 1002, + branch: "release/17.2", + created_at: "2026-03-04T18:00:00Z" + ) + ) + stub_jobs( + run_id => failed_jobs("Feature tests"), + 1002 => failed_jobs("Feature tests") + ) + stub_logs("spec/features/example_spec.rb:10") + + summary = builder.build.first + + expect(summary.classification).to eq("likely datetime-sensitive") + end + + it "classifies failures seen in and out of hours as likely generic flaky" do + stub_runs( + workflow(run_id:, branch: "dev", created_at: "2026-03-03T07:59:00Z"), + workflow( + run_id: 1002, + branch: "release/17.2", + created_at: "2026-03-04T10:00:00Z" + ) + ) + stub_jobs( + run_id => failed_jobs("Feature tests"), + 1002 => failed_jobs("Feature tests") + ) + stub_logs("spec/features/example_spec.rb:10") + + summary = builder.build.first + + expect(summary.classification).to eq("likely generic flaky") + end + + it "classifies repeated failures on a single branch as likely regression" do + stub_runs( + workflow( + run_id:, + created_at: "2026-03-03T07:59:00Z", + branch: "feature/foo", + event: "push" + ), + workflow( + run_id: 1002, + created_at: "2026-03-04T08:30:00Z", + branch: "feature/foo", + event: "push" + ) + ) + stub_jobs( + run_id => failed_jobs("Unit tests"), + 1002 => failed_jobs("Unit tests") + ) + stub_logs("spec/models/example_spec.rb:12") + + summaries = described_class.new(options: options.merge(include_pr_runs: true), client:).build + + expect(summaries.first.classification).to eq("likely regression") + end + + def stub_runs(*runs) + allow(client).to receive(:workflow_runs).and_return( + { "workflow_runs" => runs }, + { "workflow_runs" => [] } + ) + end + + def stub_jobs(jobs_by_run_id) + allow(client).to receive(:jobs) do |id| + jobs_by_run_id.fetch(id) + end + end + + def stub_logs(location) + allow(client).to receive(:log).and_return(job_log(location)) + end + + def workflow(run_id:, created_at:, branch: "dev", event: "push") + { + "id" => run_id, + "run_number" => run_id, + "html_url" => "https://github.com/opf/openproject/actions/runs/#{run_id}", + "head_branch" => branch, + "event" => event, + "created_at" => created_at + } + end + + def failed_jobs(step_name) + { + "jobs" => [ + { + "id" => 9001, + "conclusion" => "failure", + "steps" => [ + { "name" => step_name, "conclusion" => "failure" } + ] + } + ] + } + end + + def build_failure_jobs + { + "jobs" => [ + { + "id" => 9002, + "conclusion" => "failure", + "steps" => [ + { "name" => "Build", "conclusion" => "failure" } + ] + } + ] + } + end + + def job_log(location) + "2026-03-03T08:00:00.0000000Z rspec #{location} # example failure\n" + end +end diff --git a/spec/seeders/root_seeder_standard_edition_spec.rb b/spec/seeders/root_seeder_standard_edition_spec.rb index 326491fc278..c0ab9890c87 100644 --- a/spec/seeders/root_seeder_standard_edition_spec.rb +++ b/spec/seeders/root_seeder_standard_edition_spec.rb @@ -131,7 +131,7 @@ RSpec.describe RootSeeder, member_role = root_seeder.seed_data.find_reference(:default_role_member) expect(member_role.permissions).to include( :view_work_packages, # from common basic data - :view_taskboards, # from backlogs module + :view_sprints, # from backlogs module :show_board_views, # from board module :view_documents, # from documents module :view_budgets, # from costs module diff --git a/spec/services/project_custom_fields/load_service_spec.rb b/spec/services/project_custom_fields/load_service_spec.rb new file mode 100644 index 00000000000..8a5d2516d0b --- /dev/null +++ b/spec/services/project_custom_fields/load_service_spec.rb @@ -0,0 +1,87 @@ +# 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 "spec_helper" + +RSpec.describe ProjectCustomFields::LoadService do + let(:project) { create(:project) } + let(:custom_field) { create(:list_project_custom_field, multi_value: true) } + let(:project_custom_fields) { ProjectCustomField.where(id: project_custom_field_ids) } + let(:project_custom_field_ids) { custom_field.id } + + subject(:service) { described_class.new(project:, project_custom_fields:) } + + describe "#get_eager_loaded_project_custom_field_values_for" do + context "when the custom field has no values" do + it "returns an empty array" do + expect(service.get_eager_loaded_project_custom_field_values_for(custom_field.id)).to eq([]) + end + end + + context "when the custom field has values" do + # intentionally out of order + let!(:cv2) { create(:custom_value, id: 1002, customized: project, custom_field:) } + let!(:cv1) { create(:custom_value, id: 1001, customized: project, custom_field:) } + let!(:cv3) { create(:custom_value, id: 1003, customized: project, custom_field:) } + + it "returns the values ordered by id" do + expect(service.get_eager_loaded_project_custom_field_values_for(custom_field.id)) + .to eq([cv1, cv2, cv3]) + end + + it "returns empty array for other custom field" do + expect(service.get_eager_loaded_project_custom_field_values_for(0)) + .to eq([]) + end + + context "when another custom field has values" do + let(:other_custom_field) { create(:list_project_custom_field, multi_value: true) } + let!(:other_cv) { create(:custom_value, customized: project, custom_field: other_custom_field) } + let(:project_custom_field_ids) { [custom_field.id, other_custom_field.id] } + + it "returns values only for the requested custom field" do + expect(service.get_eager_loaded_project_custom_field_values_for(custom_field.id)) + .to eq([cv1, cv2, cv3]) + expect(service.get_eager_loaded_project_custom_field_values_for(other_custom_field.id)) + .to eq([other_cv]) + end + end + + context "when another project has values for the same custom field" do + let!(:other_project_cv) { create(:custom_value, customized: create(:project), custom_field:) } + + it "returns only values for the given project" do + expect(service.get_eager_loaded_project_custom_field_values_for(custom_field.id)) + .to eq([cv1, cv2, cv3]) + end + end + end + end +end diff --git a/spec/services/projects/creation_wizard/create_artifact_work_package_service_spec.rb b/spec/services/projects/creation_wizard/create_artifact_work_package_service_spec.rb index 6a1f79b977b..c8d5646dc96 100644 --- a/spec/services/projects/creation_wizard/create_artifact_work_package_service_spec.rb +++ b/spec/services/projects/creation_wizard/create_artifact_work_package_service_spec.rb @@ -315,69 +315,6 @@ RSpec.describe Projects::CreationWizard::CreateArtifactWorkPackageService do end end - context "when an artifact work package already exists" do - shared_let(:already_existing_artifact_work_package) do - create(:work_package, project:, subject: "Fake project initiation request") - end - - before do - project.update(project_creation_wizard_artifact_work_package_id: already_existing_artifact_work_package.id) - end - - it "does not create a new artifact work package" do - result = instance.call - expect(result).to be_success - expect(result.errors.full_messages).to be_empty - - project = result.result - expect(project.project_creation_wizard_artifact_work_package_id) - .to eq(already_existing_artifact_work_package.id) - expect(project.work_packages.count).to eq(1) - end - - it "does not try to validate the contract" do - instance.call - expect(mocked_contract).not_to have_received(:validate) - end - - context "when artifact storage is project storage" do - before do - # setup storage to ensure it's not called - storage = create(:nextcloud_storage_with_local_connection) - project_storage = create(:project_storage, project:, storage:, project_folder_id: "/project_folder") - project.update( - project_creation_wizard_artifact_export_type: "file_link", - project_creation_wizard_artifact_export_storage: project_storage.id - ) - - allow(Storages::UploadFileService) - .to receive(:call) - .and_return(ServiceResult.success) - end - - it "does not try to create another artifact pdf and upload it" do - result = instance.call - expect(result).to be_success - expect(result.errors.full_messages).to be_empty - expect(Storages::UploadFileService).not_to have_received(:call) - end - end - - context "when the already existing artifact work package gets deleted " \ - "(dangling artifact work package id in project settings)" do - before do - already_existing_artifact_work_package.destroy - end - - it "creates a new artifact work package" do - result = instance.call - expect(result).to be_success - expect(result.errors.full_messages).to be_empty - expect(project.project_creation_wizard_artifact_work_package_id).not_to eq(already_existing_artifact_work_package.id) - end - end - end - context "with required work package custom fields" do shared_let(:required_wp_custom_field) do create(:work_package_custom_field, diff --git a/spec/services/projects/creation_wizard/submit_artifact_service_spec.rb b/spec/services/projects/creation_wizard/submit_artifact_service_spec.rb new file mode 100644 index 00000000000..0ead45f0332 --- /dev/null +++ b/spec/services/projects/creation_wizard/submit_artifact_service_spec.rb @@ -0,0 +1,165 @@ +# 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 "spec_helper" + +RSpec.describe Projects::CreationWizard::SubmitArtifactService do + shared_let(:status_new) { create(:status, name: "New") } + shared_let(:type) { create(:type, name: "Project initiation") } + shared_let(:current_user) { create(:user, lastname: "current_user") } + shared_let(:role) do + create(:project_role, permissions: %i[ + add_work_packages + view_project_attributes + ]) + end + shared_let(:default_priority) { create(:default_priority) } + shared_let(:project) do + create( + :project, + name: "Important Project", + types: [type], + project_creation_wizard_artifact_name: "project_mandate", + project_creation_wizard_enabled: true, + project_creation_wizard_work_package_type_id: type.id, + project_creation_wizard_status_when_submitted_id: status_new.id, + project_creation_wizard_artifact_export_type: "attachment" + ).tap do |p| + p.members << create(:member, principal: current_user, project: p, roles: [role]) + end + end + + let(:instance) do + described_class.new(user: current_user, project:) + end + + before do + login_as current_user + end + + context "when no artifact work package exists" do + let(:create_service_result) { ServiceResult.success(result: project) } + let(:create_service_instance) { instance_double(Projects::CreationWizard::CreateArtifactWorkPackageService, call: create_service_result) } + + before do + project.update!(project_creation_wizard_artifact_work_package_id: nil) + + allow(Projects::CreationWizard::CreateArtifactWorkPackageService) + .to receive(:new) + .with(user: current_user, model: project) + .and_return(create_service_instance) + end + + it "delegates to CreateArtifactWorkPackageService" do + result = instance.call + + expect(result).to be_success + expect(Projects::CreationWizard::CreateArtifactWorkPackageService).to have_received(:new) + expect(create_service_instance).to have_received(:call) + end + + it "returns the result from CreateArtifactWorkPackageService" do + result = instance.call + + expect(result).to eq(create_service_result) + end + end + + context "when the artifact work package already exists" do + shared_let(:work_package) do + create(:work_package, project:, type:, status: status_new, subject: "Existing artifact") + end + + let(:upload_result) { ServiceResult.success(result: nil) } + let(:upload_service_instance) { instance_double(Projects::CreationWizard::UploadArtifactService, call: upload_result) } + + before do + project.update!(project_creation_wizard_artifact_work_package_id: work_package.id) + + allow(Projects::CreationWizard::UploadArtifactService) + .to receive(:new) + .with(user: current_user, project:, work_package:) + .and_return(upload_service_instance) + end + + it "delegates to UploadArtifactService" do + result = instance.call + + expect(result).to be_success + expect(result.result).to eq(project) + expect(Projects::CreationWizard::UploadArtifactService).to have_received(:new) + expect(upload_service_instance).to have_received(:call) + end + + it "does not call CreateArtifactWorkPackageService" do + allow(Projects::CreationWizard::CreateArtifactWorkPackageService).to receive(:new) + + instance.call + + expect(Projects::CreationWizard::CreateArtifactWorkPackageService).not_to have_received(:new) + end + + context "when upload fails" do + let(:upload_result) do + ServiceResult.failure(result: nil).tap do |result| + result.errors.add(:base, "Upload failed!") + end + end + + it "returns a successful result with errors from the upload" do + result = instance.call + + expect(result).to be_success + expect(result.errors[:base]).to include("Upload failed!") + end + end + end + + context "when the artifact work package id is dangling (work package was deleted)" do + let(:create_service_result) { ServiceResult.success(result: project) } + let(:create_service_instance) { instance_double(Projects::CreationWizard::CreateArtifactWorkPackageService, call: create_service_result) } + + before do + project.update!(project_creation_wizard_artifact_work_package_id: 99999) + + allow(Projects::CreationWizard::CreateArtifactWorkPackageService) + .to receive(:new) + .with(user: current_user, model: project) + .and_return(create_service_instance) + end + + it "delegates to CreateArtifactWorkPackageService" do + result = instance.call + + expect(result).to be_success + expect(Projects::CreationWizard::CreateArtifactWorkPackageService).to have_received(:new) + end + end +end diff --git a/spec/services/projects/creation_wizard/upload_artifact_service_spec.rb b/spec/services/projects/creation_wizard/upload_artifact_service_spec.rb new file mode 100644 index 00000000000..7571cac1239 --- /dev/null +++ b/spec/services/projects/creation_wizard/upload_artifact_service_spec.rb @@ -0,0 +1,201 @@ +# 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 "spec_helper" + +RSpec.describe Projects::CreationWizard::UploadArtifactService do + shared_let(:status_new) { create(:status, name: "New") } + shared_let(:type) { create(:type, name: "Project initiation") } + shared_let(:current_user) { create(:user, lastname: "current_user") } + shared_let(:role) do + create(:project_role, permissions: %i[ + add_work_packages + view_project_attributes + ]) + end + shared_let(:default_priority) { create(:default_priority) } + shared_let(:project) do + create( + :project, + name: "Important Project", + types: [type], + project_creation_wizard_artifact_name: "project_mandate", + project_creation_wizard_enabled: true, + project_creation_wizard_work_package_type_id: type.id, + project_creation_wizard_status_when_submitted_id: status_new.id + ).tap do |p| + p.members << create(:member, principal: current_user, project: p, roles: [role]) + end + end + + shared_let(:work_package) do + create( + :work_package, + project:, + type:, + status: status_new, + subject: "Artifact Work Package" + ) + end + + let(:instance) do + described_class.new(user: current_user, project:, work_package:) + end + + before do + login_as current_user + end + + context "when artifact storage is internal (attachment)" do + before do + project.update(project_creation_wizard_artifact_export_type: "attachment") + end + + it "adds the PDF as an attachment to the work package" do + initial_attachment_count = work_package.attachments.count + + result = instance.call + + expect(result).to be_success + work_package.reload + expect(work_package.attachments.count).to eq(initial_attachment_count + 1) + + attachment = work_package.attachments.last + date = Date.current.iso8601 + expect(attachment.content_type).to eq "application/pdf" + expect(attachment.filename).to match(/.*_Project_mandate_#{status_new.name}_#{date}_\d+-\d+.pdf/) + expect(attachment.author).to eq(current_user) + end + + context "when work package already has an attachment" do + before do + work_package.attachments.create( + author: current_user, + file: OpenProject::Files.create_uploaded_file( + name: "existing_file.pdf", + content_type: "application/pdf", + content: "old content", + binary: true + ) + ) + end + + it "adds a new attachment without removing the existing one" do + initial_count = work_package.attachments.count + + result = instance.call + + expect(result).to be_success + work_package.reload + expect(work_package.attachments.count).to eq(initial_count + 1) + end + end + end + + context "when artifact storage is project storage (file link)" do + let(:storage) { create(:nextcloud_storage_with_local_connection) } + let(:project_storage) { create(:project_storage, project:, storage:, project_folder_id: "/project_folder") } + let(:service_result) { ServiceResult.success(result: nil) } + + before do + project.update!( + project_creation_wizard_artifact_export_type: "file_link", + project_creation_wizard_artifact_export_storage: project_storage.id + ) + + allow(Storages::UploadFileService) + .to receive(:call) + .and_return(service_result) + end + + it "uploads the artifact to the project storage" do + result = instance.call + + expect(result).to be_success + work_package.reload + expect(work_package.attachments.count).to eq(0) + + date = Date.current.iso8601 + expect(Storages::UploadFileService) + .to have_received(:call) + .with(container: work_package, + project_storage:, + file_path: "Project mandate", + filename: /.*_Project_mandate_#{status_new.name}_#{date}_\d+-\d+.pdf/, + file_data: instance_of(StringIO)) + end + + context "when storage upload fails" do + let(:service_result) do + ServiceResult.failure(result: nil).tap do |result| + result.errors.add(:base, "Storage upload failed!") + end + end + + it "returns a failure result" do + result = instance.call + + expect(result).to be_failure + expect(Storages::UploadFileService).to have_received(:call) + end + end + + context "when project storage is not configured" do + before do + project.update(project_creation_wizard_artifact_export_storage: nil) + end + + it "returns a failure result with storage error" do + result = instance.call + + expect(result).to be_failure + expect(result.message).to eq(I18n.t("projects.wizard.create_artifact_storage_error")) + end + + it "does not call the storage upload service" do + instance.call + + expect(Storages::UploadFileService).not_to have_received(:call) + end + end + + context "when project storage does not exist (invalid id)" do + before do + project.update(project_creation_wizard_artifact_export_storage: 99999) + end + + it "returns a failure result" do + result = instance.call + + expect(result).to be_failure + end + end + end +end diff --git a/spec/services/reminders/set_attributes_service_spec.rb b/spec/services/reminders/set_attributes_service_spec.rb index 5804ee862d4..b550d9679d7 100644 --- a/spec/services/reminders/set_attributes_service_spec.rb +++ b/spec/services/reminders/set_attributes_service_spec.rb @@ -31,6 +31,7 @@ require "spec_helper" RSpec.describe Reminders::SetAttributesService do + let(:business_day_at_noon) { Time.zone.local(2025, 1, 8, 12, 0, 0) } let(:user) { build_stubbed(:user) } let(:model_instance) { Reminder.new } let(:remindable) { build_stubbed(:work_package) } @@ -44,6 +45,14 @@ RSpec.describe Reminders::SetAttributesService do contract_class:) end + before do + travel_to(business_day_at_noon) + end + + after do + travel_back + end + describe "building remind_at timestamp" do it "sets the remind_at attribute from date and time params" do params = { diff --git a/spec/services/reminders/update_service_spec.rb b/spec/services/reminders/update_service_spec.rb index acd00a363d2..5de4c90e384 100644 --- a/spec/services/reminders/update_service_spec.rb +++ b/spec/services/reminders/update_service_spec.rb @@ -39,18 +39,24 @@ RSpec.describe Reminders::UpdateService do describe "remind_at changed" do subject { described_class.new(user:, model: model_instance).call(call_attributes) } + let(:business_day_at_noon) { Time.zone.local(2025, 1, 8, 12, 0, 0) } let(:model_instance) { create(:reminder, :scheduled, :with_unread_notifications, creator: user) } let(:user) { create(:admin) } - let(:remind_at) { 2.days.from_now.change(hour: 12, min: 0) } + let(:remind_at) { business_day_at_noon + 2.days } let(:call_attributes) { { remind_at_date: remind_at.to_date, remind_at_time: remind_at.strftime("%H:%M") } } before do - model_instance.update(job_id: 1) + travel_to(business_day_at_noon) + model_instance.update!(job_id: 1) allow(Reminders::ScheduleReminderJob).to receive(:schedule) .with(model_instance) .and_return(instance_double(Reminders::ScheduleReminderJob, job_id: 2)) end + after do + travel_back + end + context "with an existing unfinished scheduled job" do let(:job) { instance_double(GoodJob::Job, finished?: false, destroy: true) } @@ -101,7 +107,7 @@ RSpec.describe Reminders::UpdateService do end context "with remind_at attribute in non-utc timezone" do - let(:call_attributes) { { remind_at: 2.days.from_now.in_time_zone("Africa/Nairobi") } } + let(:call_attributes) { { remind_at: remind_at.in_time_zone("Africa/Nairobi") } } it "reschedules the reminder" do expect { subject }.to change(model_instance, :job_id).from("1").to("2") diff --git a/spec/support/components/common/filters.rb b/spec/support/components/common/filters.rb index 805ed1081f5..93a946a0cf9 100644 --- a/spec/support/components/common/filters.rb +++ b/spec/support/components/common/filters.rb @@ -198,6 +198,8 @@ module Components end def open_filters + return if filters_expanded? + retry_block do toggle_filters_section expect(page).to have_css(".op-filters-form.-expanded") @@ -213,6 +215,13 @@ module Components filters_toggle.click end + def filters_expanded? + # wait for widgets to be loaded (filters button should be visible) + filters_toggle + + page.has_css?(".op-filters-form.-expanded", wait: 0) + end + def autocomplete_filter?(filter) filter.has_css?('[data-filter-autocomplete="true"]', wait: 0) end diff --git a/spec/support/shared/with_direct_uploads.rb b/spec/support/shared/with_direct_uploads.rb index 302dd0b5677..144da94d473 100644 --- a/spec/support/shared/with_direct_uploads.rb +++ b/spec/support/shared/with_direct_uploads.rb @@ -172,14 +172,19 @@ RSpec.configure do |config| WithDirectUploads.new(self).before example - class FogAttachment < Attachment - # Remounting the uploader overrides the original file setter taking care of setting, - # among other things, the content type. So we have to restore that original - # method this way. - # We do this in a new, separate class, as to not interfere with any other specs. - alias_method :set_file, :file= - mount_uploader :file, FogFileUploader - alias_method :file=, :set_file + # Only define FogAttachment once. In CW 2.x, mount_uploader uses prepend, + # so re-opening the class and re-mounting would stack prepend modules and + # cause infinite recursion with the alias_method trick below. + unless defined?(FogAttachment) + class FogAttachment < Attachment + # Remounting the uploader overrides the original file setter taking care of setting, + # among other things, the content type. So we have to restore that original + # method this way. + # We do this in a new, separate class, as to not interfere with any other specs. + alias_method :set_file, :file= + mount_uploader :file, FogFileUploader + alias_method :file=, :set_file + end end end diff --git a/spec/support/shared/with_ssrf_webhook_stubs.rb b/spec/support/shared/with_ssrf_webhook_stubs.rb index 2114f66bddf..23bb7590060 100644 --- a/spec/support/shared/with_ssrf_webhook_stubs.rb +++ b/spec/support/shared/with_ssrf_webhook_stubs.rb @@ -29,23 +29,11 @@ module WithSsrfWebhookStubsMixin ## # A safe public IP returned by the stubbed resolver for any hostname. - # It is not in SsrfFilter's private-address blocklist, so SSRF validation passes, - # and WebMock stubs using this IP will match the actual Net::HTTP request. + # It is not in SsrfFilter's private-address blocklist, so SSRF validation passes. + # ssrf_filter 1.3+ makes requests to the original hostname URL (not the resolved IP), + # passing the resolved IP via the `ipaddr:` option to Net::HTTP.start instead. SSRF_TEST_IP = "93.184.216.34" - ## - # Translates a webhook URL containing a hostname to the IP-based URL that - # SsrfFilter will use when making the actual HTTP request. Use this when - # setting up WebMock stubs so that they match the resolved request. - # - # URLs that already contain an IP address are returned unchanged. - def ssrf_resolved_url(url) - uri = URI.parse(url) - return url if ip_address?(uri.host) - - url.sub(uri.host, SSRF_TEST_IP) - end - def ip_address?(host) [Resolv::IPv4::Regex, Resolv::IPv6::Regex].any? { host.match? it } end diff --git a/spec/support_spec/table_helpers/column_type/schedule_spec.rb b/spec/support_spec/table_helpers/column_type/schedule_spec.rb index f377b7c6947..0d849151808 100644 --- a/spec/support_spec/table_helpers/column_type/schedule_spec.rb +++ b/spec/support_spec/table_helpers/column_type/schedule_spec.rb @@ -50,6 +50,10 @@ module TableHelpers::ColumnType travel_to(fake_today) end + after do + travel_back + end + describe "origin day" do it "is identified by the 'M' in 'MTWTFSS' in the header and corresponds to the next monday" do expect(parsed_attributes(<<~TABLE)) diff --git a/spec/support_spec/table_helpers/example_methods_spec.rb b/spec/support_spec/table_helpers/example_methods_spec.rb index 18f44454e40..b212d8aca97 100644 --- a/spec/support_spec/table_helpers/example_methods_spec.rb +++ b/spec/support_spec/table_helpers/example_methods_spec.rb @@ -45,6 +45,10 @@ module TableHelpers travel_to(fake_today) end + after do + travel_back + end + it "applies attribute changes to a group of work packages from a visual table representation" do main = build_stubbed(:work_package, subject: "main") second = build_stubbed(:work_package, subject: "second") diff --git a/spec/workers/import/jira_import_projects_job_spec.rb b/spec/workers/import/jira_import_projects_job_spec.rb new file mode 100644 index 00000000000..ab04b5ce409 --- /dev/null +++ b/spec/workers/import/jira_import_projects_job_spec.rb @@ -0,0 +1,180 @@ +# 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 "spec_helper" + +RSpec.describe Import::JiraImportProjectsJob, :webmock do + let(:jira) { create(:jira) } + let(:author) { create(:user) } + let(:jira_import) do + create(:jira_import, jira:, author:, projects: [{ "id" => "10242", "key" => "DYX", "name" => "Zombie Engine" }]) + end + + let(:jira_project_payload) { JSON.parse(Rails.root.join("spec/fixtures/import/jira/project.json").read) } + let(:jira_issue_payload) { JSON.parse(Rails.root.join("spec/fixtures/import/jira/issue.json").read) } + let(:jira_user_payload) { JSON.parse(Rails.root.join("spec/fixtures/import/jira/user.json").read) } + + let!(:jira_project) do + create(:jira_project, + jira:, + jira_import:, + jira_project_id: "10242", + payload: jira_project_payload) + end + + describe "#perform" do + context "when a project with the same identifier already exists" do + let!(:existing_project) { create(:project, identifier: "dyx", name: "Existing Project") } + + it "raises an error with the taken identifier and existing project info" do + expect { described_class.new.perform(jira_import.id) } + .to raise_error("You are trying to import a project with already used " \ + "identifier: dyx. Existing project: Existing Project.") + end + end + + context "when importing a full project with issues, comments, and attachments" do + let!(:jira_issue) do + create(:jira_issue, + jira:, + jira_import:, + jira_issue_id: "10100", + jira_project_id: jira_project.id, + payload: jira_issue_payload) + end + + let!(:jira_issue_type) do + create(:jira_issue_type, + jira:, + jira_import:, + jira_issue_type_id: "10100", + payload: { "id" => "10100", "name" => "Task" }) + end + + let!(:jira_status) do + create(:jira_status, + jira:, + jira_import:, + jira_status_id: "3", + payload: { "id" => "3", "name" => "In Progress" }) + end + + let!(:jira_priority) do + create(:jira_priority, + jira:, + jira_import:, + jira_priority_id: "1", + payload: { "id" => "1", "name" => "Highest" }) + end + + let!(:jira_user) do + create(:jira_user, + jira:, + jira_import:, + jira_user_key: "JIRAUSER10000", + payload: jira_user_payload) + end + + let!(:op_user) { create(:user, login: "p.balashou", mail: "p.balashou@openproject.com") } + + let!(:jira_user_reference) do + create(:jira_open_project_reference, + jira:, + jira_import:, + jira_entity_class: "Import::JiraUser", + jira_entity_id: jira_user.id.to_s, + op_entity_class: "User", + op_entity_id: op_user.id.to_s) + end + + let(:attachment_content) { Rails.root.join("spec/fixtures/files/image.png").binread } + + before do + stub_request(:get, "https://jira-software.local/secure/attachment/10000/solid-color-image.png") + .to_return(status: 200, body: attachment_content, headers: { "Content-Type" => "image/png" }) + end + + it "creates the project in OpenProject" do + expect { described_class.new.perform(jira_import.id) } + .to change(Project, :count).by(1) + + project = Project.find_by(identifier: "dyx") + expect(project).to be_present + expect(project.name).to eq("Zombie Engine") + end + + it "creates the work package with correct attributes" do + described_class.new.perform(jira_import.id) + + work_package = WorkPackage.find_by(subject: "Kanban cards represent work items") + expect(work_package).to be_present + expect(work_package.type.name).to eq("Task") + expect(work_package.status.name).to eq("In Progress") + expect(work_package.priority.name).to eq("Highest") + expect(work_package.assigned_to).to eq(op_user) + end + + it "creates a comment on the work package" do + described_class.new.perform(jira_import.id) + + work_package = WorkPackage.find_by(subject: "Kanban cards represent work items") + expect(work_package.journals.where(notes: "Created 2 hours 36 minutes ago").count).to be 1 + end + + it "creates an attachment on the work package" do + described_class.new.perform(jira_import.id) + + work_package = WorkPackage.find_by(subject: "Kanban cards represent work items") + expect(work_package.attachments.count).to eq(1) + expect(work_package.attachments.first.filename).to eq("solid-color-image.png") + end + + it "creates references for imported entities" do + expect { described_class.new.perform(jira_import.id) } + .to change(Import::JiraOpenProjectReference, :count).by_at_least(4) + end + end + + context "when project creation fails with a general error" do + before do + # rubocop:disable RSpec/AnyInstance + allow_any_instance_of(Projects::CreateService).to receive(:call).and_return( + ServiceResult.failure(message: "Something went wrong during project creation") + ) + # rubocop:enable RSpec/AnyInstance + end + + it "raises the error message" do + expect { described_class.new.perform(jira_import.id) } + .to raise_error("Something went wrong during project creation") + end + end + end +end