diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index bfb56a7c6d3..761aeeb067b 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -21,7 +21,10 @@ jobs: if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' uses: contributor-assistant/github-action@v2.6.1 env: + # https://github.com/contributor-assistant/github-action?tab=readme-ov-file#environmental-variables + # Built-in GitHub token to make the API calls for interacting with GitHub. Does not need to be specified the secrets store. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Access token for repository where the signatures are stored (see below for remote-repository-name) PERSONAL_ACCESS_TOKEN: ${{ secrets.OPENPROJECTCI_GH_LEGAL_TOKEN }} with: path-to-signatures: "contributor-license-agreement/signatures/version1.json" diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 861d79fd3dc..03f3c5ae822 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -76,12 +76,14 @@ jobs: crowdin_branch_name: ${{ steps.vars.outputs.crowdin_branch }} # Dont create a PR for the updated translations push_translations: false + user: auto env: OPENPROJECT_CROWDIN_PROJECT: ${{ secrets.OPENPROJECT_CROWDINV2_PROJECT }} OPENPROJECT_CROWDIN_API_KEY: ${{ secrets.OPENPROJECT_CROWDINV2_API_KEY }} - name: "Fix root key in Portuguese crowdin translation files" - run: | - script/i18n/fix_crowdin_pt_language_root_key + run: script/i18n/fix_crowdin_pt_language_root_key + - name: "Rewrite crowdin translation files using ruby yaml library" + run: script/i18n/rewrite_crowdin_yml_files - name: "Commit translations" env: BRANCH: ${{ matrix.branch }} diff --git a/.rubocop.yml b/.rubocop.yml index 1815a2c11d1..1bd67f59709 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -169,6 +169,10 @@ Rails/ContentTag: # dynamic finders cop clashes with capybara ID cop Rails/DynamicFindBy: Enabled: true + AllowedMethods: + - find_by_id_or_identifier + - find_by_id_or_identifier! + - find_by_semantic_identifier Exclude: - "spec/features/**/*.rb" - "spec/support/**/*.rb" diff --git a/.ruby-version b/.ruby-version index 1454f6ed4b7..4d54daddb61 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -4.0.1 +4.0.2 diff --git a/Gemfile b/Gemfile index 548a87c64e5..afa3fb1128d 100644 --- a/Gemfile +++ b/Gemfile @@ -41,10 +41,10 @@ gem "activemodel-serializers-xml", "~> 1.0.1" gem "activerecord-import", "~> 2.2.0" gem "activerecord-session_store", "~> 2.2.0" gem "ox" -gem "rails", "~> 8.1.2" +gem "rails", "~> 8.1.3" gem "responders", "~> 3.2" -gem "ffi", "~> 1.15" +gem "ffi", "~> 1.17" gem "connection_pool", "~> 3.0.2" @@ -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.8.0" +gem "mcp", "~> 0.9.2" gem "meta-tags", "~> 2.23.0" @@ -204,9 +204,9 @@ gem "carrierwave_direct", "~> 3.0.0" gem "ssrf_filter", "~> 1.3" gem "fog-aws" -gem "aws-sdk-core", "~> 3.241" +gem "aws-sdk-core", "~> 3.244" # File upload via fog + screenshots on travis -gem "aws-sdk-s3", "~> 1.213" +gem "aws-sdk-s3", "~> 1.217" gem "openproject-token", "~> 8.8.2" @@ -254,7 +254,7 @@ gem "factory_bot_rails", "~> 6.5.0", require: false gem "turbo_power", "~> 0.7.0" gem "turbo-rails", "~> 2.0.20" -gem "httpx", "~> 1.7.4" +gem "httpx", "~> 1.7.5" # Brings actual deep-freezing to most ruby objects gem "ice_nine" @@ -266,7 +266,7 @@ group :test do # Test prof provides factories from code # and other niceties - gem "test-prof", "~> 1.5.0" + gem "test-prof", "~> 1.6.0" gem "turbo_tests", github: "opf/turbo_tests", ref: "with-patches" gem "rack_session_access" diff --git a/Gemfile.lock b/Gemfile.lock index ec987c85987..c6ef1b70a8f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -203,7 +203,7 @@ PATH remote: modules/two_factor_authentication specs: openproject-two_factor_authentication (1.0.0) - aws-sdk-sns (>= 1.101, < 1.113) + aws-sdk-sns (>= 1.101, < 1.114) messagebird-rest (>= 1.4.2, < 5.1.0) rotp (~> 6.1) webauthn (~> 3.0) @@ -228,31 +228,31 @@ GEM remote: https://rubygems.org/ specs: Ascii85 (2.0.1) - action_text-trix (2.1.17) + action_text-trix (2.1.18) railties - actioncable (8.1.2.1) - actionpack (= 8.1.2.1) - activesupport (= 8.1.2.1) + actioncable (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (8.1.2.1) - actionpack (= 8.1.2.1) - activejob (= 8.1.2.1) - activerecord (= 8.1.2.1) - activestorage (= 8.1.2.1) - activesupport (= 8.1.2.1) + actionmailbox (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) - actionmailer (8.1.2.1) - actionpack (= 8.1.2.1) - actionview (= 8.1.2.1) - activejob (= 8.1.2.1) - activesupport (= 8.1.2.1) + actionmailer (8.1.3) + actionpack (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activesupport (= 8.1.3) mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (8.1.2.1) - actionview (= 8.1.2.1) - activesupport (= 8.1.2.1) + actionpack (8.1.3) + actionview (= 8.1.3) + activesupport (= 8.1.3) nokogiri (>= 1.8.5) rack (>= 2.2.4) rack-session (>= 1.0.1) @@ -263,34 +263,34 @@ GEM actionpack-xml_parser (2.0.1) actionpack (>= 5.0) railties (>= 5.0) - actiontext (8.1.2.1) + actiontext (8.1.3) action_text-trix (~> 2.1.15) - actionpack (= 8.1.2.1) - activerecord (= 8.1.2.1) - activestorage (= 8.1.2.1) - activesupport (= 8.1.2.1) + actionpack (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (8.1.2.1) - activesupport (= 8.1.2.1) + actionview (8.1.3) + activesupport (= 8.1.3) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) active_record_doctor (2.0.1) activerecord (>= 7.0.0) - activejob (8.1.2.1) - activesupport (= 8.1.2.1) + activejob (8.1.3) + activesupport (= 8.1.3) globalid (>= 0.3.6) - activemodel (8.1.2.1) - activesupport (= 8.1.2.1) + activemodel (8.1.3) + activesupport (= 8.1.3) activemodel-serializers-xml (1.0.3) activemodel (>= 5.0.0.a) activesupport (>= 5.0.0.a) builder (~> 3.1) - activerecord (8.1.2.1) - activemodel (= 8.1.2.1) - activesupport (= 8.1.2.1) + activerecord (8.1.3) + activemodel (= 8.1.3) + activesupport (= 8.1.3) timeout (>= 0.4.0) activerecord-import (2.2.0) activerecord (>= 4.2) @@ -302,13 +302,13 @@ GEM cgi (>= 0.3.6) rack (>= 2.0.8, < 4) railties (>= 7.0) - activestorage (8.1.2.1) - actionpack (= 8.1.2.1) - activejob (= 8.1.2.1) - activerecord (= 8.1.2.1) - activesupport (= 8.1.2.1) + activestorage (8.1.3) + actionpack (= 8.1.3) + activejob (= 8.1.3) + activerecord (= 8.1.3) + activesupport (= 8.1.3) marcel (~> 1.0) - activesupport (8.1.2.1) + activesupport (8.1.3) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -347,8 +347,8 @@ GEM awesome_nested_set (3.9.0) activerecord (>= 4.0.0, < 8.2) aws-eventstream (1.4.0) - aws-partitions (1.1227.0) - aws-sdk-core (3.243.0) + aws-partitions (1.1234.0) + aws-sdk-core (3.244.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) @@ -356,15 +356,15 @@ GEM bigdecimal jmespath (~> 1, >= 1.6.1) logger - aws-sdk-kms (1.122.0) - aws-sdk-core (~> 3, >= 3.241.4) + aws-sdk-kms (1.123.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.216.0) - aws-sdk-core (~> 3, >= 3.243.0) + aws-sdk-s3 (1.217.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) - aws-sdk-sns (1.112.0) - aws-sdk-core (~> 3, >= 3.241.4) + aws-sdk-sns (1.113.0) + aws-sdk-core (~> 3, >= 3.244.0) aws-sigv4 (~> 1.5) aws-sigv4 (1.12.1) aws-eventstream (~> 1, >= 1.0.2) @@ -391,7 +391,7 @@ GEM erubi (~> 1.4) parser (>= 2.4) smart_properties - bigdecimal (4.0.1) + bigdecimal (4.1.1) bindata (2.5.1) bootsnap (1.23.0) msgpack (~> 1.2) @@ -586,14 +586,14 @@ GEM concurrent-ruby (~> 1.1) webrick (~> 1.7) websocket-driver (~> 0.7) - ffi (1.17.3-aarch64-linux-gnu) - ffi (1.17.3-aarch64-linux-musl) - ffi (1.17.3-arm-linux-gnu) - ffi (1.17.3-arm-linux-musl) - ffi (1.17.3-arm64-darwin) - ffi (1.17.3-x86_64-darwin) - ffi (1.17.3-x86_64-linux-gnu) - ffi (1.17.3-x86_64-linux-musl) + ffi (1.17.4-aarch64-linux-gnu) + ffi (1.17.4-aarch64-linux-musl) + ffi (1.17.4-arm-linux-gnu) + ffi (1.17.4-arm-linux-musl) + ffi (1.17.4-arm64-darwin) + ffi (1.17.4-x86_64-darwin) + ffi (1.17.4-x86_64-linux-gnu) + ffi (1.17.4-x86_64-linux-musl) flamegraph (0.9.5) fog-aws (3.33.1) base64 (>= 0.2, < 0.4) @@ -703,7 +703,7 @@ GEM htmlentities (4.3.4) http-2 (1.1.3) http_parser.rb (0.8.1) - httpx (1.7.4) + httpx (1.7.5) http-2 (>= 1.1.3) i18n (1.14.8) concurrent-ruby (~> 1.0) @@ -744,9 +744,9 @@ GEM reline (>= 0.4.2) iso8601 (0.13.0) jmespath (1.6.2) - job-iteration (1.12.0) - activejob (>= 6.1) - json (2.19.2) + job-iteration (1.13.0) + activejob (>= 7.0) + json (2.19.3) json-jwt (1.17.0) activesupport (>= 4.2) aes_key_wrap @@ -821,7 +821,7 @@ GEM marcel (1.0.4) markly (0.15.2) matrix (0.4.3) - mcp (0.8.0) + mcp (0.9.2) json-schema (>= 4.1) messagebird-rest (5.0.0) jwt (< 4) @@ -835,7 +835,7 @@ GEM mini_magick (5.3.1) logger mini_mime (1.1.5) - minitest (6.0.2) + minitest (6.0.3) drb (~> 2.0) prism (~> 1.5) msgpack (1.8.0) @@ -1091,17 +1091,17 @@ GEM ostruct (0.6.3) ox (2.14.23) bigdecimal (>= 3.0) - pagy (43.4.1) + pagy (43.4.4) json uri yaml paper_trail (17.0.0) activerecord (>= 7.1) request_store (~> 1.4) - parallel (1.27.0) + parallel (1.28.0) parallel_tests (4.10.1) parallel - parser (3.3.10.2) + parser (3.3.11.1) ast (~> 2.4.1) racc pdf-core (0.9.0) @@ -1199,7 +1199,7 @@ GEM puma (>= 5.0, < 8) raabro (1.4.0) racc (1.8.1) - rack (2.2.22) + rack (2.2.23) rack-attack (6.8.0) rack (>= 1.0, < 4) rack-cors (2.0.2) @@ -1227,20 +1227,20 @@ GEM rackup (1.0.1) rack (< 3) webrick - rails (8.1.2.1) - actioncable (= 8.1.2.1) - actionmailbox (= 8.1.2.1) - actionmailer (= 8.1.2.1) - actionpack (= 8.1.2.1) - actiontext (= 8.1.2.1) - actionview (= 8.1.2.1) - activejob (= 8.1.2.1) - activemodel (= 8.1.2.1) - activerecord (= 8.1.2.1) - activestorage (= 8.1.2.1) - activesupport (= 8.1.2.1) + rails (8.1.3) + actioncable (= 8.1.3) + actionmailbox (= 8.1.3) + actionmailer (= 8.1.3) + actionpack (= 8.1.3) + actiontext (= 8.1.3) + actionview (= 8.1.3) + activejob (= 8.1.3) + activemodel (= 8.1.3) + activerecord (= 8.1.3) + activestorage (= 8.1.3) + activesupport (= 8.1.3) bundler (>= 1.15.0) - railties (= 8.1.2.1) + railties (= 8.1.3) rails-controller-testing (1.0.5) actionpack (>= 5.0.1.rc1) actionview (>= 5.0.1.rc1) @@ -1255,9 +1255,9 @@ GEM rails-i18n (8.1.0) i18n (>= 0.7, < 2) railties (>= 8.0.0, < 9) - railties (8.1.2.1) - actionpack (= 8.1.2.1) - activesupport (= 8.1.2.1) + railties (8.1.3) + actionpack (= 8.1.3) + activesupport (= 8.1.3) irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) @@ -1287,7 +1287,7 @@ GEM redis-client (>= 0.22.0) redis-client (0.28.0) connection_pool - regexp_parser (2.11.3) + regexp_parser (2.12.0) reline (0.6.3) io-console (~> 0.5) representable (3.2.0) @@ -1331,11 +1331,10 @@ GEM rspec-support (3.13.7) rspec-wait (1.0.2) rspec (>= 3.4) - rubocop (1.85.1) + rubocop (1.86.0) 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) @@ -1455,7 +1454,7 @@ GEM table_print (1.5.7) terminal-table (4.0.0) unicode-display_width (>= 1.1.1, < 4) - test-prof (1.5.2) + test-prof (1.6.0) text-hyphen (1.5.0) thor (1.5.0) thread_safe (0.3.6) @@ -1581,8 +1580,8 @@ DEPENDENCIES appsignal (~> 4.7) auto_strip_attributes (~> 2.5) awesome_nested_set (~> 3.9.0) - aws-sdk-core (~> 3.241) - aws-sdk-s3 (~> 1.213) + aws-sdk-core (~> 3.244) + aws-sdk-s3 (~> 1.217) axe-core-rspec bcrypt (~> 3.1.22) bootsnap (~> 1.23.0) @@ -1623,7 +1622,7 @@ DEPENDENCIES escape_utils (~> 1.3) factory_bot (~> 6.5.6) factory_bot_rails (~> 6.5.0) - ffi (~> 1.15) + ffi (~> 1.17) flamegraph fog-aws friendly_id (~> 5.6.0) @@ -1637,7 +1636,7 @@ DEPENDENCIES grids! html-pipeline (~> 2.14.0) htmldiff - httpx (~> 1.7.4) + httpx (~> 1.7.5) i18n-js (~> 4.2.4) i18n-tasks (~> 1.1.0) ice_cube (~> 0.17.0) @@ -1656,7 +1655,7 @@ DEPENDENCIES mail (= 2.9.0) markly (~> 0.15) matrix (~> 0.4.3) - mcp (~> 0.8.0) + mcp (~> 0.9.2) md_to_pdf! meta-tags (~> 2.23.0) mini_magick (~> 5.3.0) @@ -1722,7 +1721,7 @@ DEPENDENCIES rack-test (~> 2.2.0) rack-timeout (~> 0.7.0) rack_session_access - rails (~> 8.1.2) + rails (~> 8.1.3) rails-controller-testing (~> 1.0.2) rails-i18n (~> 8.1.0) rbtrace @@ -1771,7 +1770,7 @@ DEPENDENCIES svg-graph (~> 2.2.0) sys-filesystem (~> 1.5.0) table_print (~> 1.5.6) - test-prof (~> 1.5.0) + test-prof (~> 1.6.0) timecop (~> 0.9.0) ttfunk (~> 1.7.0) turbo-rails (~> 2.0.20) @@ -1794,24 +1793,24 @@ DEPENDENCIES CHECKSUMS Ascii85 (2.0.1) sha256=15cb5d941808543cbb9e7e6aea3c8ec3877f154c3461e8b3673e97f7ecedbe5a - action_text-trix (2.1.17) sha256=b44691639d77e67169dc054ceacd1edc04d44dc3e4c6a427aa155a2beb4cc951 - actioncable (8.1.2.1) sha256=a2f88cecce148b3fcb63d2e517d7694e119830a85baa7d6cf59e5453dcf32e8d - actionmailbox (8.1.2.1) sha256=c2e45c0c1e5687e35e050838c94a8aed0d954c56a32ea411d54cd848c338c54e - actionmailer (8.1.2.1) sha256=d7d62fbc2197f1a7006bb5af4c665edf999adf79ab6c10337c088d27e6622071 - actionpack (8.1.2.1) sha256=a6b69cd10ec4c8d978c8eee51206e34152b1c1be017e534236dbc89a3d00ffb8 + action_text-trix (2.1.18) sha256=3fdb83f8bff4145d098be283cdd47ac41caf5110bfa6df4695ed7127d7fb3642 + actioncable (8.1.3) sha256=e5bc7f75e44e6a22de29c4f43176927c3a9ce4824464b74ed18d8226e75a80f0 + actionmailbox (8.1.3) sha256=df7da474eaa0e70df4ed5a6fef66eb3b3b0f2dbf7f14518deee8d77f1b4aae59 + actionmailer (8.1.3) sha256=831f724891bb70d0aaa4d76581a6321124b6a752cb655c9346aae5479318448d + actionpack (8.1.3) sha256=af998cae4d47c5d581a2cc363b5c77eb718b7c4b45748d81b1887b25621c29a3 actionpack-xml_parser (2.0.1) sha256=40cb461ee99445314ab580a783fb7413580deb8b28113c9e70ecd7c1b334d5e6 - actiontext (8.1.2.1) sha256=1e503ce600a6ab2e12a46f999959a7d8e2fdaff910ca01dcf3b968934b55d957 - actionview (8.1.2.1) sha256=38daa7b87bca427e2967f139e5b7f0d1081271bdafd0e015d8ef97a006f570a6 + actiontext (8.1.3) sha256=d291019c00e1ea9e6463011fa214f6081a56d7b9a1d224e7d3f6384c1dafc7d2 + actionview (8.1.3) sha256=1347c88c7f3edb38100c5ce0e9fb5e62d7755f3edc1b61cce2eb0b2c6ea2fd5d active_record_doctor (2.0.1) sha256=7af0ac02195385c8f2f67d0e4ebe72b1fc79d65eaaf329e0db07f4d12a84069a - activejob (8.1.2.1) sha256=c89c311d07fd358b76c581ed8fee87c5b4351fb44994f3389385c014d22182fe - activemodel (8.1.2.1) sha256=8f31a6f9c12fecb8e5a0fce8a8950cfd94f0d75829322935f99e8217a3e5f3c6 + activejob (8.1.3) sha256=a149b1766aa8204c3c3da7309e4becd40fcd5529c348cffbf6c9b16b565fe8d3 + activemodel (8.1.3) sha256=90c05cbe4cef3649b8f79f13016191ea94c4525ce4a5c0fb7ef909c4b91c8219 activemodel-serializers-xml (1.0.3) sha256=fa1b16305e7254cc58a59c68833e3c0a593a59c8ab95d3be5aaea7cd9416c397 - activerecord (8.1.2.1) sha256=3f79140318ff6d23376f5d9b1b5b5e2c7d3cc8979dd71367e9a8394378ca630a + activerecord (8.1.3) sha256=8003be7b2466ba0a2a670e603eeb0a61dd66058fccecfc49901e775260ac70ab activerecord-import (2.2.0) sha256=f8ca99b196e50775723d1f1d192c379f656378dc9f5628240992a0d78807fa4b activerecord-nulldb-adapter (1.2.2) sha256=01e0b2e49af11ad56a92e274a3d8c9fb3c50a12a5460218c4c4b45355d9ef968 activerecord-session_store (2.2.0) sha256=65918054573683bf4f87af89e765e1fece14c9d71cfac1f11abe4687c96e2743 - activestorage (8.1.2.1) sha256=36794c9b8853ac9276b0386cb1f8973374d8e71e8a9666bb02e70f5b7c9c5391 - activesupport (8.1.2.1) sha256=beec20ced12ad569194554399449a6372fdab03061b8f48a9ed6ef9b7dc251b2 + activestorage (8.1.3) sha256=0564ce9309143951a67615e1bb4e090ee54b8befed417133cae614479b46384d + activesupport (8.1.3) sha256=21a5e0dfbd4c3ddd9e1317ec6a4d782fa226e7867dc70b0743acda81a1dca20e acts_as_list (1.2.6) sha256=8345380900b7bee620c07ad00991ccee59af3d8c9e8574f426e321da2865fdc8 acts_as_tree (2.9.1) sha256=b869eb10a8de38616b64ffcf9e882d3d99c8e06909c4057078a76c3b89a9a2f3 addressable (2.8.9) sha256=cc154fcbe689711808a43601dee7b980238ce54368d23e127421753e46895485 @@ -1827,11 +1826,11 @@ CHECKSUMS auto_strip_attributes (2.6.0) sha256=a7e2e0cf744de2bcd947fd68014220702bcc88c81274c1cd9ce6f7316aae39b0 awesome_nested_set (3.9.0) sha256=3ce99e816550f97f4de118e621630070aacf24928b920fe4a68846578a8daaed aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b - aws-partitions (1.1227.0) sha256=122dd20fe108cb38d38cccbc1f2592408bc1b30ca6e0d05797a7af2501567e29 - aws-sdk-core (3.243.0) sha256=a014eef785124b71d28325783fa422a1512f8421ec9b6e3931c8b0ca3fbb0f1c - aws-sdk-kms (1.122.0) sha256=47ce3f51b26bd7d76f1270cfdfca17b40073ecd3219c8c9400788712abfb4eb8 - aws-sdk-s3 (1.216.0) sha256=a3bf6191e6f7a3dfb04b7cc73409f059394be559e4aff92d2a764341e4d90af4 - aws-sdk-sns (1.112.0) sha256=aff1b1b5bbcb4229599221c558a41790c1cd1a1fed47ac3d27d27512ad24b254 + aws-partitions (1.1234.0) sha256=8f74aa6ca2e945fa50d83a23fc10f5406cb0becd32ac9f8940001ba8b04dcf8a + aws-sdk-core (3.244.0) sha256=3e458c078b0c5bdee95bc370c3a483374b3224cf730c1f9f0faf849a5d9a18ea + aws-sdk-kms (1.123.0) sha256=d405f37e82f8fa32045ca8980be266c0b45b37aaf2012afe0254321a1e811f20 + aws-sdk-s3 (1.217.0) sha256=6ea709272c666888b14e9c62345abd9a6a967759ae13667c28f01fde6823c24b + aws-sdk-sns (1.113.0) sha256=15fe37d010e86f4c28b4c2f2133c463ce5c14189ec3673a1f43c30dfee511b0f aws-sigv4 (1.12.1) sha256=6973ff95cb0fd0dc58ba26e90e9510a2219525d07620c8babeb70ef831826c00 axe-core-api (4.11.1) sha256=a6460506449a692030620a0574fee7afa6cd38cfbbf6620d20bf4d53d33a80cc axe-core-rspec (4.11.1) sha256=dc6c0e166405cd3a28c4a0937f6521ee5b511c12c0ca1627144a1ee7d5014aec @@ -1840,7 +1839,7 @@ CHECKSUMS bcrypt (3.1.22) sha256=1f0072e88c2d705d94aff7f2c5cb02eb3f1ec4b8368671e19112527489f29032 benchmark (0.5.0) sha256=465df122341aedcb81a2a24b4d3bd19b6c67c1530713fd533f3ff034e419236c better_html (2.2.0) sha256=e68ab66ab09696b708333bbf35e8aa3c107500ba7892f528e2111624bdd8cf76 - bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7 + bigdecimal (4.1.1) sha256=1c09efab961da45203c8316b0cdaec0ff391dfadb952dd459584b63ebf8054ca bindata (2.5.1) sha256=53186a1ec2da943d4cb413583d680644eb810aacbf8902497aac8f191fad9e58 bootsnap (1.23.0) sha256=c1254f458d58558b58be0f8eb8f6eec2821456785b7cdd1e16248e2020d3f214 brakeman (8.0.4) sha256=7bf921fa9638544835df9aa7b3e720a9a72c0267f34f92135955edd80d4dcf6f @@ -1927,14 +1926,14 @@ CHECKSUMS faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c 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 - ffi (1.17.3-arm-linux-gnu) sha256=5bd4cea83b68b5ec0037f99c57d5ce2dd5aa438f35decc5ef68a7d085c785668 - ffi (1.17.3-arm-linux-musl) sha256=0d7626bb96265f9af78afa33e267d71cfef9d9a8eb8f5525344f8da6c7d76053 - ffi (1.17.3-arm64-darwin) sha256=0c690555d4cee17a7f07c04d59df39b2fba74ec440b19da1f685c6579bb0717f - ffi (1.17.3-x86_64-darwin) sha256=1f211811eb5cfaa25998322cdd92ab104bfbd26d1c4c08471599c511f2c00bb5 - ffi (1.17.3-x86_64-linux-gnu) sha256=3746b01f677aae7b16dc1acb7cb3cc17b3e35bdae7676a3f568153fb0e2c887f - ffi (1.17.3-x86_64-linux-musl) sha256=086b221c3a68320b7564066f46fed23449a44f7a1935f1fe5a245bd89d9aea56 + ffi (1.17.4-aarch64-linux-gnu) sha256=b208f06f91ffd8f5e1193da3cae3d2ccfc27fc36fba577baf698d26d91c080df + ffi (1.17.4-aarch64-linux-musl) sha256=9286b7a615f2676245283aef0a0a3b475ae3aae2bb5448baace630bb77b91f39 + ffi (1.17.4-arm-linux-gnu) sha256=d6dbddf7cb77bf955411af5f187a65b8cd378cb003c15c05697f5feee1cb1564 + ffi (1.17.4-arm-linux-musl) sha256=9d4838ded0465bef6e2426935f6bcc93134b6616785a84ffd2a3d82bc3cf6f95 + ffi (1.17.4-arm64-darwin) sha256=19071aaf1419251b0a46852abf960e77330a3b334d13a4ab51d58b31a937001b + ffi (1.17.4-x86_64-darwin) sha256=aa70390523cf3235096cf64962b709b4cfbd5c082a2cb2ae714eb0fe2ccda496 + ffi (1.17.4-x86_64-linux-gnu) sha256=9d3db14c2eae074b382fa9c083fe95aec6e0a1451da249eab096c34002bc752d + ffi (1.17.4-x86_64-linux-musl) sha256=3fdf9888483de005f8ef8d1cf2d3b20d86626af206cbf780f6a6a12439a9c49e flamegraph (0.9.5) sha256=a683020637ffa0e14a72640fa41babf14d926bfeaed87e31907cfd06ab2de8dc fog-aws (3.33.1) sha256=20c7336ed978be6cbf2765844c53f30676288af98f1cb49945aa7b7b45a799a5 fog-core (2.6.0) sha256=3fe08aa83a23cddce42f4ba412040c08f890d7ff04c175c0ee59119371245be6 @@ -1976,7 +1975,7 @@ CHECKSUMS htmlentities (4.3.4) sha256=125a73c6c9f2d1b62100b7c3c401e3624441b663762afa7fe428476435a673da http-2 (1.1.3) sha256=1b2f379d35a11dbae94f8a1a52c053d8c161eb4a0c98b5d1605ff1b2bf171c9c http_parser.rb (0.8.1) sha256=9ae8df145b39aa5398b2f90090d651c67bd8e2ebfe4507c966579f641e11097a - httpx (1.7.4) sha256=91fb3e0f7325966a5da4d463a1dd7240e8550d8b0de79e346cc5dc1df1eacd2b + httpx (1.7.5) sha256=f002b7728ad108b721ab9c4e02b17b5c780581d490cd13245fc3d5c5b7127b6e i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 i18n-js (4.2.4) sha256=61390d372f8fa68c495c5907d577657e8cc3a7031f4945db1e91f935e1391355 i18n-tasks (1.1.2) sha256=4dcfba49e52a623f30661cb316cb80d84fbba5cb8c6d88ef5e02545fffa3637a @@ -1990,8 +1989,8 @@ CHECKSUMS irb (1.17.0) sha256=168c4ddb93d8a361a045c41d92b2952c7a118fa73f23fe14e55609eb7a863aae iso8601 (0.13.0) sha256=298c2b15b7be5fa95a1372813d36a2257656cd8e906dfbc1f5cb409851425aa2 jmespath (1.6.2) sha256=238d774a58723d6c090494c8879b5e9918c19485f7e840f2c1c7532cf84ebcb1 - job-iteration (1.12.0) sha256=0164057417750f6e9c3ed548f029f1136b18eb53975fa438b09304a525d6c6c0 - json (2.19.2) sha256=e7e1bd318b2c37c4ceee2444841c86539bc462e81f40d134cf97826cb14e83cf + job-iteration (1.13.0) sha256=3300844e81309fbd06fd2310d6aa8e1f43bf30fe03a3fc5067580b62f456b7e1 + json (2.19.3) sha256=289b0bb53052a1fa8c34ab33cc750b659ba14a5c45f3fcf4b18762dc67c78646 json-jwt (1.17.0) sha256=6ff99026b4c54281a9431179f76ceb81faa14772d710ef6169785199caadc4cc json-schema (6.2.0) sha256=e8bff46ed845a22c1ab2bd0d7eccf831c01fe23bb3920caa4c74db4306813666 json_schemer (2.5.0) sha256=2f01fb4cce721a4e08dd068fc2030cffd0702a7f333f1ea2be6e8991f00ae396 @@ -2014,7 +2013,7 @@ CHECKSUMS marcel (1.0.4) sha256=0d5649feb64b8f19f3d3468b96c680bae9746335d02194270287868a661516a4 markly (0.15.2) sha256=65dae965d4dd4ecd997fba43b93acc0fe7dadfec6f07a748640c7a9299a8551e matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b - mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb + mcp (0.9.2) sha256=d413926f4f9dc1274f462196c2a9fbf9cb261ecd3c66a60c82eb71ee62bd2932 md_to_pdf (0.2.6) messagebird-rest (5.0.0) sha256=da4cc1efba3d5e4aa021fad07426c2cb6b326ce5670da5104bb8f6056a39d59c meta-tags (2.23.0) sha256=ffe78b5bee398de4ff5ac3316f5a786049538a651643b8476def06c3acc762c1 @@ -2023,7 +2022,7 @@ CHECKSUMS mime-types-data (3.2026.0317) sha256=77f078a4d8631d52b842ba77099734b06eddb7ad339d792e746d2272b67e511b mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef - minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d + minitest (6.0.3) sha256=88ac8a1de36c00692420e7cb3cc11a0773bbcb126aee1c249f320160a7d11411 msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732 multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7 mustermann (3.0.4) sha256=85fadcb6b3c6493a8b511b42426f904b7f27b282835502233dd154daab13aa22 @@ -2142,11 +2141,11 @@ CHECKSUMS ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 overviews (1.0.0) ox (2.14.23) sha256=4a9aedb4d6c78c5ebac1d7287dc7cc6808e14a8831d7adb727438f6a1b461b66 - pagy (43.4.1) sha256=a211fc64b2c396f84db08f8de7bf919e73732de9e9e170a33eec7960bbdd3822 + pagy (43.4.4) sha256=b41a57328a0aabfd222266a89e9de3dc3a735c17bd57f8113829c95fece5bef6 paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85 - parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + parallel (1.28.0) sha256=33e6de1484baf2524792d178b0913fc8eb94c628d6cfe45599ad4458c638c970 parallel_tests (4.10.1) sha256=df05458c691462b210f7a41fc2651d4e4e8a881e8190e6d1e122c92c07735d70 - parser (3.3.10.2) sha256=6f60c84aa4bdcedb6d1a2434b738fe8a8136807b6adc8f7f53b97da9bc4e9357 + parser (3.3.11.1) sha256=d17ace7aabe3e72c3cc94043714be27cc6f852f104d81aa284c2281aecc65d54 pdf-core (0.9.0) sha256=4f368b2f12b57ec979872d4bf4bd1a67e8648e0c81ab89801431d2fc89f4e0bb pdf-inspector (1.3.0) sha256=fc107579d6f29b636e2da3d6743479b2624d9e390bf2d84beef8fd4ebe1a05bd pdf-reader (2.15.1) sha256=18c6a986a84a3117fa49f4279fc2de51f5d2399b71833df5d2bccd595c7068ce @@ -2181,7 +2180,7 @@ CHECKSUMS puma-plugin-statsd (2.7.0) sha256=04f243a7233f4d06ec0e26f1a3522bce18a5910ae711763fabff22681bdad08b raabro (1.4.0) sha256=d4fa9ff5172391edb92b242eed8be802d1934b1464061ae5e70d80962c5da882 racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f - rack (2.2.22) sha256=c5cf0b7f872559966d974abe3101a57d51caf12504ee76290b98720004f64542 + rack (2.2.23) sha256=a8fe9d7e07064770b8ec123663fded8a59ef7e2b6db5cda7173d45a5718ab69c rack-attack (6.8.0) sha256=f2499fdebf85bcc05573a22dff57d24305ac14ec2e4156cd3c28d47cafeeecf2 rack-cors (2.0.2) sha256=415d4e1599891760c5dc9ef0349c7fecdf94f7c6a03e75b2e7c2b54b82adda1b rack-mini-profiler (4.0.1) sha256=485810c23211f908196c896ea10cad72ed68780ee2998bec1f1dfd7558263d78 @@ -2192,12 +2191,12 @@ CHECKSUMS rack-timeout (0.7.0) sha256=757337e9793cca999bb73a61fe2a7d4280aa9eefbaf787ce3b98d860749c87d9 rack_session_access (0.2.0) sha256=03eb98f2027429ccbbeb18556006dfb6d928b0557ad3770783b8e2f368198d6b rackup (1.0.1) sha256=ba86604a28989fe1043bff20d819b360944ca08156406812dca6742b24b3c249 - rails (8.1.2.1) sha256=93ebf1efc792c9bc47e9795259c920312d3920008dad3ae634b7a0457ffe0af8 + rails (8.1.3) sha256=6d017ba5348c98fc909753a8169b21d44de14d2a0b92d140d1a966834c3c9cd3 rails-controller-testing (1.0.5) sha256=741448db59366073e86fc965ba403f881c636b79a2c39a48d0486f2607182e94 rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d rails-html-sanitizer (1.7.0) sha256=28b145cceaf9cc214a9874feaa183c3acba036c9592b19886e0e45efc62b1e89 rails-i18n (8.1.0) sha256=52d5fd6c0abef28d84223cc05647f6ae0fd552637a1ede92deee9545755b6cf3 - railties (8.1.2.1) sha256=f4d902869541af4e5b5552d726062fa59ec0fd9078f7ab87720dbd93f22c43ee + railties (8.1.3) sha256=913eb0e0cb520aac687ffd74916bd726d48fa21f47833c6292576ef6a286de22 rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c rake-compiler-dock (1.11.0) sha256=eab51f2cd533eb35cea6b624a75281f047123e70a64c58b607471bb49428f8c2 @@ -2211,7 +2210,7 @@ CHECKSUMS redcarpet (3.6.1) sha256=d444910e6aa55480c6bcdc0cdb057626e8a32c054c29e793fa642ba2f155f445 redis (5.4.1) sha256=b5e675b57ad22b15c9bcc765d5ac26f60b675408af916d31527af9bd5a81faae redis-client (0.28.0) sha256=888892f9cd8787a41c0ece00bdf5f556dfff7770326ce40bb2bc11f1bfec824b - regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4 + regexp_parser (2.12.0) sha256=35a916a1d63190ab5c9009457136ae5f3c0c7512d60291d0d1378ba18ce08ebb reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835 representable (3.2.0) sha256=cc29bf7eebc31653586849371a43ffe36c60b54b0a6365b5f7d95ec34d1ebace request_store (1.7.0) sha256=e1b75d5346a315f452242a68c937ef8e48b215b9453a77a6c0acdca2934c88cb @@ -2230,7 +2229,7 @@ CHECKSUMS rspec-retry (0.6.2) sha256=6101ba23a38809811ae3484acde4ab481c54d846ac66d5037ccb40131a60d858 rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c rspec-wait (1.0.2) sha256=865f921239325d3d26fc10ded4bdd485d8b58bcaaad1a28dd85ed15266b5a912 - rubocop (1.85.1) sha256=3dbcf9e961baa4c376eeeb2a03913dca5e3987033b04d38fa538aa1e7406cc77 + rubocop (1.86.0) sha256=4ff1186fe16ebe9baff5e7aad66bb0ad4cabf5cdcd419f773146dbba2565d186 rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c rubocop-factory_bot (2.28.0) sha256=4b17fc02124444173317e131759d195b0d762844a71a29fe8139c1105d92f0cb @@ -2280,7 +2279,7 @@ CHECKSUMS sys-filesystem (1.5.5) sha256=6f995890a734b9f0aa55df5e09d99adeb9fd1c288f2c4097269a1f8c95e15033 table_print (1.5.7) sha256=436664281f93387b882335795e16cfeeb839ad0c785ff7f9110fc0f17c68b5cb terminal-table (4.0.0) sha256=f504793203f8251b2ea7c7068333053f0beeea26093ec9962e62ea79f94301d2 - test-prof (1.5.2) sha256=185839fb7d3745b3770ec48e3e5718eff9e28c327a50a1e18a3a9ef1060f8576 + test-prof (1.6.0) sha256=b04cee7b9684b9b31e23e258f835ba67ee5b4fd5f6cc7204d076ef695805c335 text-hyphen (1.5.0) sha256=c44a4533b8a554e7ff7c955e131bcccc78a0b4c56ce1d73f2c8c11f43b075a06 thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 thread_safe (0.3.6) sha256=9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a @@ -2328,7 +2327,7 @@ CHECKSUMS zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd RUBY VERSION - ruby 4.0.1 + ruby 4.0.2 BUNDLED WITH - 4.0.6 + 4.0.9 diff --git a/app/components/admin/import/jira/import_runs/wizard_step_review_component.rb b/app/components/admin/import/jira/import_runs/wizard_step_review_component.rb index 2f337b3ccd5..566318ce4e8 100644 --- a/app/components/admin/import/jira/import_runs/wizard_step_review_component.rb +++ b/app/components/admin/import/jira/import_runs/wizard_step_review_component.rb @@ -50,7 +50,11 @@ module Admin::Import::Jira::ImportRuns return nil if imported_projects.none? ids = imported_projects.pluck(:op_entity_id).map(&:to_s) - helpers.projects_path(filters: [{ id: { operator: "=", values: ids } }].to_json) + if ids.length == 1 + helpers.project_path(id: ids[0]) + else + helpers.projects_path(filters: [{ id: { operator: "=", values: ids } }].to_json) + end end def imported_work_packages @@ -61,8 +65,13 @@ module Admin::Import::Jira::ImportRuns def imported_work_packages_url return nil if imported_work_packages.none? - project_ids = imported_projects.pluck(:op_entity_id).map(&:to_s) - helpers.work_packages_path(query_props: { f: [{ n: "project", o: "=", v: project_ids }] }.to_json) + wp_ids = imported_work_packages.pluck(:op_entity_id).map(&:to_s) + if wp_ids.length == 1 + helpers.work_package_path(id: wp_ids[0]) + else + project_ids = imported_projects.pluck(:op_entity_id).map(&:to_s) + helpers.work_packages_path(query_props: { f: [{ n: "project", o: "=", v: project_ids }] }.to_json) + end end def imported_users diff --git a/app/components/individual_principal_base_filter_component.rb b/app/components/individual_principal_base_filter_component.rb index 7e1291b6cfe..362c30f683e 100644 --- a/app/components/individual_principal_base_filter_component.rb +++ b/app/components/individual_principal_base_filter_component.rb @@ -75,7 +75,7 @@ class IndividualPrincipalBaseFilterComponent < ApplicationComponent end def base_query - raise NotImplementedError + raise SubclassResponsibilityError end protected @@ -93,7 +93,7 @@ class IndividualPrincipalBaseFilterComponent < ApplicationComponent # INSTANCE METHODS: def filter_path - raise NotImplementedError + raise SubclassResponsibilityError end def initially_visible? diff --git a/app/components/my/notifications/project_settings_dialog_component.html.erb b/app/components/my/notifications/project_settings_dialog_component.html.erb new file mode 100644 index 00000000000..d92281d49d4 --- /dev/null +++ b/app/components/my/notifications/project_settings_dialog_component.html.erb @@ -0,0 +1,62 @@ +<%#-- 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( + id: DIALOG_ID, + title: t("my_account.notifications.project_specific_settings.dialog_title"), + position: :right, + test_selector: "project-specific-settings-form" + ) + ) do |d| + d.with_body do + primer_form_with( + model: notification_setting, + scope: :notification_setting, + url: @form_url, + method: edit_mode? ? :patch : :post, + html: { id: FORM_ID }, + data: { + turbo: false, + controller: "show-when-checked", + show_when_checked_visibility_class: "d-none" + } + ) do |form| + forms = [My::Notifications::ProjectAutocompleterForm.new(form, readonly: edit_mode?, user: @user)] + forms << My::Notifications::ParticipatingForm.new(form, show_submit: false) + forms << My::Notifications::DateAlertsForm.new(form, show_submit: false) if date_alerts_available? + forms << My::Notifications::NonParticipatingForm.new(form, show_submit: false) + render(Primer::Forms::FormList.new(*forms)) + end + end + d.with_footer(show_divider: true) do + concat(render(Primer::Beta::Button.new(data: { "close-dialog-id": DIALOG_ID })) { t("button_cancel") }) + concat(render(Primer::Beta::Button.new(scheme: :primary, type: :submit, form: FORM_ID)) { t("button_save") }) + end + end %> diff --git a/app/components/my/notifications/project_settings_dialog_component.rb b/app/components/my/notifications/project_settings_dialog_component.rb new file mode 100644 index 00000000000..1b2d0daaa87 --- /dev/null +++ b/app/components/my/notifications/project_settings_dialog_component.rb @@ -0,0 +1,62 @@ +# 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 My + module Notifications + class ProjectSettingsDialogComponent < ApplicationComponent + include OpTurbo::Streamable + include OpPrimer::FormHelpers + + DIALOG_ID = "project-notification-settings-dialog" + FORM_ID = "project-notification-settings-form" + + def initialize(user:, form_url:, notification_setting: nil) + super + @user = user + @form_url = form_url + @provided_setting = notification_setting + end + + private + + def notification_setting + @notification_setting ||= @provided_setting || @user.notification_settings.build + end + + def edit_mode? + notification_setting.persisted? + end + + def date_alerts_available? + EnterpriseToken.allows_to?(:date_alerts) + end + end + end +end diff --git a/app/components/my/notifications/show_page_component.html.erb b/app/components/my/notifications/show_page_component.html.erb new file mode 100644 index 00000000000..2bb538428ce --- /dev/null +++ b/app/components/my/notifications/show_page_component.html.erb @@ -0,0 +1,135 @@ +<%#-- 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. + +++#%> + +<%= + settings_primer_form_with( + model: global_notification_setting, + scope: :notification_setting, + url: update_participating_url, + method: :patch, + data: { turbo: false, test_selector: "participating-form" } + ) do |form| + render(My::Notifications::ParticipatingForm.new(form)) + end +%> + +<% if date_alerts_available? %> + <%= + settings_primer_form_with( + model: global_notification_setting, + scope: :notification_setting, + url: update_date_alerts_url, + method: :patch, + data: { + turbo: false, + controller: "show-when-checked", + show_when_checked_visibility_class: "d-none", + test_selector: "date-alerts-form" + } + ) do |form| + render(My::Notifications::DateAlertsForm.new(form)) + end + %> +<% else %> + <%= render(Primer::Beta::Subhead.new(mt: 3)) do |component| + component.with_heading(size: :medium) { t("my_account.notifications.date_alerts.title") } + end %> + <%= render(EnterpriseEdition::BannerComponent.new(:date_alerts, variant: :inline)) %> +<% end %> + +<%= + settings_primer_form_with( + model: global_notification_setting, + scope: :notification_setting, + url: update_non_participating_url, + method: :patch, + data: { turbo: false, test_selector: "non-participating-form" } + ) do |form| + render(My::Notifications::NonParticipatingForm.new(form)) + end +%> + +<%= render(Primer::BaseComponent.new(tag: :div, classes: "op-admin-settings-form-wrapper")) do %> + <%= render(Primer::Beta::Subhead.new(mt: 3)) do |component| + component.with_heading(size: :medium) { t("my_account.notifications.project_specific_settings.title") } + end %> + + <% if project_notification_settings.any? %> + <%= render(Primer::Beta::BorderBox.new(mb: 3)) do |box| %> + <% box.with_header { t("my_account.notifications.project_specific_settings.list_header") } %> + <% project_notification_settings.each do |setting| %> + <% box.with_row(test_selector: "project-specific-settings-list") do %> + <%= flex_layout(justify_content: :space_between, align_items: :center, width: :full) do |flex| %> + <%= flex.with_column do %> + <%= setting.project.name %> + <% end %> + <%= flex.with_column do %> + <%= render(Primer::Alpha::ActionMenu.new(test_selector: "project-specific-settings-list--action-menu")) do |menu| + menu.with_show_button( + scheme: :invisible, + size: :small, + icon: :"kebab-horizontal", + "aria-label": t(:label_open_menu), + tooltip_direction: :w + ) + menu.with_item( + label: t("button_edit"), + href: edit_project_settings_url(setting.project_id), + content_arguments: { data: { controller: "async-dialog" } } + ) do |item| + item.with_leading_visual_icon(icon: :pencil) + end + menu.with_item( + label: t("button_delete"), + scheme: :danger, + href: project_setting_url(setting.project_id), + content_arguments: { data: { turbo_method: :delete, turbo_confirm: t("text_are_you_sure") } } + ) do |item| + item.with_leading_visual_icon(icon: :trash) + end + end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + + <%= render( + Primer::Beta::Button.new( + tag: :a, + href: new_project_settings_url, + data: { controller: "async-dialog" }, + mb: 3 + ) + ) do |b| + b.with_leading_visual_icon(icon: :plus) + helpers.t("my_account.notifications.project_specific_settings.add_button") + end %> +<% end %> diff --git a/app/components/my/notifications/show_page_component.rb b/app/components/my/notifications/show_page_component.rb new file mode 100644 index 00000000000..d86e1c2d462 --- /dev/null +++ b/app/components/my/notifications/show_page_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 My + module Notifications + class ShowPageComponent < ApplicationComponent + include OpPrimer::FormHelpers + include OpPrimer::ComponentHelpers + + attr_reader :global_notification_setting, + :update_participating_url, + :update_non_participating_url, + :update_date_alerts_url, + :new_project_settings_url, + :project_notification_settings + + def initialize(user:, + global_notification_setting:, + update_participating_url:, + update_non_participating_url:, + update_date_alerts_url:, + new_project_settings_url:, + edit_project_settings_url:, + project_setting_url:) + super + + @user = user + @global_notification_setting = global_notification_setting + @update_participating_url = update_participating_url + @update_non_participating_url = update_non_participating_url + @update_date_alerts_url = update_date_alerts_url + @new_project_settings_url = new_project_settings_url + @edit_project_settings_url_builder = edit_project_settings_url + @project_setting_url_builder = project_setting_url + @project_notification_settings = user.notification_settings.where.not(project: nil).includes(:project) + end + + def edit_project_settings_url(project_id) + @edit_project_settings_url_builder.call(project_id) + end + + def project_setting_url(project_id) + @project_setting_url_builder.call(project_id) + end + + def date_alerts_available? + EnterpriseToken.allows_to?(:date_alerts) + end + end + end +end diff --git a/app/components/my/notifications/show_page_header_component.rb b/app/components/my/notifications/show_page_header_component.rb new file mode 100644 index 00000000000..20db77a9211 --- /dev/null +++ b/app/components/my/notifications/show_page_header_component.rb @@ -0,0 +1,64 @@ +# 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 My + module Notifications + class ShowPageHeaderComponent < ApplicationComponent + def call + render(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { t("my_account.notifications_and_email.title") } + header.with_breadcrumbs( + [{ href: helpers.my_account_path, text: t(:label_my_account) }, + t("my_account.notifications_and_email.title")] + ) + + helpers.render_tab_header_nav(header, tabs) + end + end + + def tabs + [ + { + name: "notifications", + path: helpers.my_notifications_path(tab: "notifications"), + label: t("my_account.notifications_and_email.tabs.notifications"), + data: { turbo: false } + }, + { + name: "reminders", + path: helpers.my_notifications_path(tab: "reminders"), + label: t("my_account.notifications_and_email.tabs.email_reminders"), + data: { turbo: false } + } + ] + end + end + end +end diff --git a/app/components/my/reminders/daily_times_component.html.erb b/app/components/my/reminders/daily_times_component.html.erb new file mode 100644 index 00000000000..bb8c350caae --- /dev/null +++ b/app/components/my/reminders/daily_times_component.html.erb @@ -0,0 +1,111 @@ +<%#-- 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. + +++#%> + +
+
+ <% @times.each do |time| %> + <%= + flex_layout(mb: 2, data: { "my--daily-reminders-target": "row" }) do |flex| + flex.with_column do + render( + Primer::Alpha::Select.new( + name: field_name, + label: t("my_account.email_reminders.daily_reminders.time_slot_label"), + visually_hide_label: true, + data: { test_selector: "settings-daily-time" } + ) + ) do |select| + time_options.each do |label, value| + select.option(label:, value:, selected: selected_value_for(time) == value) + end + end + end + + flex.with_column do + render( + Primer::Beta::IconButton.new( + icon: :x, + scheme: :invisible, + type: :button, + hidden: @times.size <= 1, + aria: { label: t("my_account.email_reminders.daily_reminders.remove_time") }, + data: { action: "my--daily-reminders#removeTime", test_selector: "settings-daily-time--remove" } + ) + ) + end + end + %> + <% end %> +
+ + + + <%= render( + Primer::Beta::Button.new( + scheme: :invisible, + type: :button, + data: { action: "my--daily-reminders#addTime" } + ) + ) do |btn| + btn.with_leading_visual_icon(icon: :plus) + t("my_account.email_reminders.daily_reminders.add_time") + end %> +
diff --git a/app/components/my/reminders/daily_times_component.rb b/app/components/my/reminders/daily_times_component.rb new file mode 100644 index 00000000000..f83c5a2e6ea --- /dev/null +++ b/app/components/my/reminders/daily_times_component.rb @@ -0,0 +1,59 @@ +# 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 My + module Reminders + class DailyTimesComponent < ApplicationComponent + include OpPrimer::ComponentHelpers + + def initialize(times:, scope:) + super + + @times = Array(times) + @scope = scope + end + + def field_name + "#{@scope}[times][]" + end + + def time_options + (0..23).map do |hour| + time = Time.utc(2000, 1, 1, hour) + [I18n.l(time, format: :time), time.strftime("%H:00:00+00:00")] + end + end + + def selected_value_for(time_str) + Time.zone.parse(time_str.to_s).strftime("%H:00:00+00:00") + end + end + end +end diff --git a/app/components/my/reminders/show_page_component.html.erb b/app/components/my/reminders/show_page_component.html.erb new file mode 100644 index 00000000000..b881f631bb4 --- /dev/null +++ b/app/components/my/reminders/show_page_component.html.erb @@ -0,0 +1,98 @@ +<%#-- 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. + +++#%> + +<%= + settings_primer_form_with( + model: @user.pref, + scope: "pref[immediate_reminders]", + url: update_url, + method: :patch, + data: { turbo: false, test_selector: "immediate-reminders-form" } + ) do |form| + render(My::Reminders::ImmediateRemindersForm.new(form)) + end +%> + +<%= + settings_primer_form_with( + model: daily_reminders_form_model, + scope: "pref[daily_reminders]", + url: update_url, + method: :patch, + data: { + turbo: false, + controller: "show-when-checked", + show_when_checked_visibility_class: "d-none", + test_selector: "daily-reminders-form" + } + ) do |form| + render(My::Reminders::DailyRemindersForm.new(form)) + end +%> + +<%= + settings_primer_form_with( + model: @user.pref, + scope: :pref, + url: update_workdays_url, + method: :patch, + data: { turbo: false, test_selector: "workdays-form" } + ) do |form| + render(My::Reminders::WorkdaysForm.new(form)) + end +%> + +<%= + settings_primer_form_with( + model: pause_reminders_form_model, + scope: "pref[pause_reminders]", + url: update_url, + method: :patch, + data: { + turbo: false, + controller: "show-when-checked", + show_when_checked_visibility_class: "d-none", + test_selector: "pause-reminders-form" + } + ) do |form| + render(My::Reminders::PauseRemindersForm.new(form)) + end +%> + +<%= + settings_primer_form_with( + model: global_notification_setting, + scope: :notification_setting, + url: update_email_alerts_url, + method: :patch, + data: { turbo: false } + ) do |form| + render(My::Reminders::EmailAlertsForm.new(form)) + end +%> diff --git a/app/components/my/reminders/show_page_component.rb b/app/components/my/reminders/show_page_component.rb new file mode 100644 index 00000000000..2826d704416 --- /dev/null +++ b/app/components/my/reminders/show_page_component.rb @@ -0,0 +1,66 @@ +# 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 My + module Reminders + class ShowPageComponent < ApplicationComponent + include OpPrimer::FormHelpers + + attr_reader :global_notification_setting, :update_url, :update_workdays_url, :update_email_alerts_url + + def initialize(user:, global_notification_setting:, update_url:, update_workdays_url:, update_email_alerts_url:) + super + + @user = user + @global_notification_setting = global_notification_setting + @update_url = update_url + @update_workdays_url = update_workdays_url + @update_email_alerts_url = update_email_alerts_url + end + + def daily_reminders_form_model + daily_reminders = @user.pref.daily_reminders + My::Reminders::DailyRemindersForm::DailyRemindersFormModel.new( + enabled: daily_reminders[:enabled], + times: daily_reminders[:times] + ) + end + + def pause_reminders_form_model + pause_reminders = @user.pref.pause_reminders + My::Reminders::PauseRemindersForm::PauseRemindersFormModel.new( + enabled: pause_reminders[:enabled], + first_day: pause_reminders[:first_day], + last_day: pause_reminders[:last_day] + ) + end + end + end +end diff --git a/app/components/op_primer/email_updates_mode_selector_component.rb b/app/components/op_primer/email_updates_mode_selector_component.rb index ba9ef59f64a..b4ce6d4f656 100644 --- a/app/components/op_primer/email_updates_mode_selector_component.rb +++ b/app/components/op_primer/email_updates_mode_selector_component.rb @@ -38,7 +38,7 @@ module OpPrimer super() if !show_button && alt_text.blank? - raise NotImplementedError, "alt_text must be provided when the button is shown conditionally" + raise ArgumentError, "alt_text must be provided when the button is shown conditionally" end diff --git a/app/components/op_primer/status_button_component.rb b/app/components/op_primer/status_button_component.rb index 74260921ff1..f98b7fc5f40 100644 --- a/app/components/op_primer/status_button_component.rb +++ b/app/components/op_primer/status_button_component.rb @@ -50,7 +50,7 @@ module OpPrimer end def default_button_title - raise NotImplementedError + raise SubclassResponsibilityError end def disabled? diff --git a/app/components/portfolios/details_component.html.erb b/app/components/portfolios/details_component.html.erb index bb5223eb2f4..3acf1a9345b 100644 --- a/app/components/portfolios/details_component.html.erb +++ b/app/components/portfolios/details_component.html.erb @@ -40,7 +40,7 @@ size: :large, data: { hover_card_trigger_target: "trigger", - hover_card_popover_id: sub_status_hover_card_id, + hover_card_popover_template_id: sub_status_hover_card_id, test_selector: "op-portfolios--sub-status-bar" } ) @@ -166,9 +166,8 @@ <%= # Card that appears when hovering over the progress bar if render_sub_status_bar? - content_tag(:div, class: "op-hover-card--hidden-container") do + content_tag(:template, id: sub_status_hover_card_id) do flex_layout( - id: sub_status_hover_card_id, classes: "op-portfolios--popover", data: { test_selector: "op-portfolios--hover-card-#{portfolio.id}" diff --git a/app/components/portfolios/details_component.sass b/app/components/portfolios/details_component.sass index 43fda50c700..1a20a5fd433 100644 --- a/app/components/portfolios/details_component.sass +++ b/app/components/portfolios/details_component.sass @@ -51,3 +51,6 @@ $status_not-set: var(--progressBar-track-bgColor) // invisible background color @media screen and (max-width: $breakpoint-sm) margin-top: var(--base-size-16, 1rem) margin-bottom: var(--base-size-16, 1rem) !important + +.op-hover-card:has(> .op-portfolios--popover) + width: fit-content diff --git a/app/components/projects/concerns/identifier_suggestion.rb b/app/components/projects/concerns/identifier_suggestion.rb index 2c970953180..2fbfb502fea 100644 --- a/app/components/projects/concerns/identifier_suggestion.rb +++ b/app/components/projects/concerns/identifier_suggestion.rb @@ -32,7 +32,7 @@ module Projects module Concerns module IdentifierSuggestion def identifier_suggestion_data - suggestion_mode = Setting::WorkPackageIdentifier.alphanumeric? ? "semantic" : "legacy" + suggestion_mode = Setting::WorkPackageIdentifier.semantic? ? "semantic" : "classic" { controller: "projects--identifier-suggestion", diff --git a/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.html.erb b/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.html.erb index 0ae9907be13..e3ce2548a28 100644 --- a/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.html.erb +++ b/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.html.erb @@ -50,9 +50,8 @@ # As a courtesy to users, we show a hover card explaining why the toggle is disabled. if toggle_disabled? concat( - content_tag(:div, class: "op-hover-card--hidden-container") do + content_tag(:template, id: unique_hovercard_id) do flex_layout( - id: unique_hovercard_id, classes: "op-project-custom-field--popover", data: { test_selector: "op-project-custom-field--hover-card-#{@project_custom_field.id}" diff --git a/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.rb b/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.rb index ffe15983c00..e42b00b4e99 100644 --- a/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.rb +++ b/app/components/projects/settings/creation_wizard/project_custom_field_sections/custom_field_row_component.rb @@ -72,7 +72,7 @@ module Projects if toggle_disabled? # Add hover card that explains why this toggle switch is disabled data[:hover_card_trigger_target] = "trigger" - data[:hover_card_popover_id] = unique_hovercard_id + data[:hover_card_popover_template_id] = unique_hovercard_id end end end diff --git a/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb b/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb index 757625840dd..293dddeaff5 100644 --- a/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb +++ b/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.html.erb @@ -56,9 +56,8 @@ # As a courtesy to users, we show a hover card explaining why the toggle is disabled. if toggle_disabled? concat( - content_tag(:div, class: "op-hover-card--hidden-container") do + content_tag(:template, id: unique_hovercard_id) do flex_layout( - id: unique_hovercard_id, classes: "op-project-custom-field--popover", data: { test_selector: "op-project-custom-field--hover-card-#{@project_custom_field.id}" diff --git a/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.rb b/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.rb index 16974f62431..e72bde388c3 100644 --- a/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.rb +++ b/app/components/projects/settings/project_custom_field_sections/custom_field_row_component.rb @@ -87,7 +87,7 @@ module Projects if toggle_disabled? # Add hover card that explains why this toggle switch is disabled data[:hover_card_trigger_target] = "trigger" - data[:hover_card_popover_id] = unique_hovercard_id + data[:hover_card_popover_template_id] = unique_hovercard_id end end end diff --git a/app/components/work_packages/admin/settings/change_identifiers_dialog_component.html.erb b/app/components/work_packages/admin/settings/change_identifiers_dialog_component.html.erb index 480cf64bb7c..1895c9bc206 100644 --- a/app/components/work_packages/admin/settings/change_identifiers_dialog_component.html.erb +++ b/app/components/work_packages/admin/settings/change_identifiers_dialog_component.html.erb @@ -58,7 +58,7 @@ ) dialog.with_additional_details(display: :none) do - hidden_field_tag("settings[work_packages_identifier]", Setting::WorkPackageIdentifier::ALPHANUMERIC) + hidden_field_tag("settings[work_packages_identifier]", Setting::WorkPackageIdentifier::SEMANTIC) end end %> diff --git a/app/components/work_packages/admin/settings/identifier_settings_form_component.rb b/app/components/work_packages/admin/settings/identifier_settings_form_component.rb index 3e9c28d41dd..45a7baf99c0 100644 --- a/app/components/work_packages/admin/settings/identifier_settings_form_component.rb +++ b/app/components/work_packages/admin/settings/identifier_settings_form_component.rb @@ -64,7 +64,7 @@ module WorkPackages def form_id = "wp-identifier-settings-form" def show_autofix_section? - state == :edit && Setting::WorkPackageIdentifier.alphanumeric? && has_problematic_projects? + state == :edit && Setting::WorkPackageIdentifier.semantic? && has_problematic_projects? end def change_in_progress? = state == :change_in_progress diff --git a/app/components/work_packages/exports/base_export_settings_component.rb b/app/components/work_packages/exports/base_export_settings_component.rb index 8f9be5894b4..88d6d572815 100644 --- a/app/components/work_packages/exports/base_export_settings_component.rb +++ b/app/components/work_packages/exports/base_export_settings_component.rb @@ -40,7 +40,7 @@ module WorkPackages end def format - raise NotImplementedError, "Must be overridden in subclass" + raise SubclassResponsibilityError end def export_settings diff --git a/app/contracts/projects/base_contract.rb b/app/contracts/projects/base_contract.rb index cbd84a3b725..ab0828a070c 100644 --- a/app/contracts/projects/base_contract.rb +++ b/app/contracts/projects/base_contract.rb @@ -122,7 +122,7 @@ module Projects end def manage_permission - raise NotImplementedError + raise SubclassResponsibilityError end def with_unchanged_id diff --git a/app/contracts/shares/base_contract.rb b/app/contracts/shares/base_contract.rb index 2862511e740..cb76bd2546d 100644 --- a/app/contracts/shares/base_contract.rb +++ b/app/contracts/shares/base_contract.rb @@ -48,7 +48,7 @@ module Shares end def user_allowed_to_manage? - raise NotImplementedError, "Must be overridden by subclass" + raise SubclassResponsibilityError end def single_non_inherited_role @@ -76,7 +76,7 @@ module Shares end def assignable_role_class - raise NotImplementedError, "Must be overridden by subclass" + raise SubclassResponsibilityError end end end diff --git a/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb b/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb index 87323c15a7b..d10bae6ee24 100644 --- a/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb +++ b/app/controllers/admin/custom_fields/hierarchy/items_base_controller.rb @@ -227,7 +227,7 @@ module Admin end def find_custom_field - raise NotImplementedError, "SubclassResponsibility" + raise SubclassResponsibilityError end def find_active_item diff --git a/app/controllers/admin/settings/enumerations_controller_base.rb b/app/controllers/admin/settings/enumerations_controller_base.rb index f1d9ff93623..c3346c8baf1 100644 --- a/app/controllers/admin/settings/enumerations_controller_base.rb +++ b/app/controllers/admin/settings/enumerations_controller_base.rb @@ -136,7 +136,7 @@ module Admin end def enumeration_class - raise NotImplementedError + raise SubclassResponsibilityError end def enumeration_permitted_params diff --git a/app/controllers/concerns/custom_fields/attribute_help_text_actions.rb b/app/controllers/concerns/custom_fields/attribute_help_text_actions.rb index 125eee405be..f5d036f8661 100644 --- a/app/controllers/concerns/custom_fields/attribute_help_text_actions.rb +++ b/app/controllers/concerns/custom_fields/attribute_help_text_actions.rb @@ -51,11 +51,11 @@ module CustomFields end def show_path - raise NotImplementedError, "#{self.class} must implement #show_path" + raise SubclassResponsibilityError, "#{self.class} must implement #show_path" end def render_attribute_help_text_form(status: :ok) - raise NotImplementedError, "#{self.class} must implement #render_attribute_help_text_form" + raise SubclassResponsibilityError, "#{self.class} must implement #render_attribute_help_text_form" end def find_or_initialize_attribute_help_text diff --git a/app/controllers/concerns/notifications/notification_settings_actions.rb b/app/controllers/concerns/notifications/notification_settings_actions.rb new file mode 100644 index 00000000000..a96d0c74fe6 --- /dev/null +++ b/app/controllers/concerns/notifications/notification_settings_actions.rb @@ -0,0 +1,153 @@ +# 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 Notifications + module NotificationSettingsActions + extend ActiveSupport::Concern + + included do + include OpTurbo::ComponentStream + end + + def update_workdays + call = ::Users::UpdateService.new(model: @user, user: current_user).call(pref: workdays_pref_params) + flash[call.success? ? :notice : :error] = update_service_flash_message(call) + redirect_back_or_to(workdays_redirect_path) + end + + def new_project_settings + respond_with_dialog My::Notifications::ProjectSettingsDialogComponent.new( + user: @user, + form_url: project_notifications_create_url + ) + end + + def create_project_settings + update_project_notification_setting + redirect_back_or_to(notifications_settings_path) + rescue ActiveRecord::RecordNotFound + flash[:error] = t(:notice_bad_request) + redirect_back_or_to(notifications_settings_path) + end + + def edit_project_settings + setting = @user.notification_settings.find_by!(project_id: params[:project_id]) + respond_with_dialog My::Notifications::ProjectSettingsDialogComponent.new( + user: @user, + notification_setting: setting, + form_url: project_setting_form_url(setting.project_id) + ) + end + + def update_project_settings + update_project_notification_setting(params[:project_id]) + redirect_back_or_to(notifications_settings_path) + rescue ActiveRecord::RecordNotFound + flash[:error] = t(:notice_bad_request) + redirect_back_or_to(notifications_settings_path) + end + + def destroy_project_settings + @user.notification_settings.find_by!(project_id: params[:project_id]).destroy! + flash[:notice] = I18n.t(:notice_successful_delete) + redirect_back_or_to(notifications_settings_path) + rescue ActiveRecord::RecordNotFound + flash[:error] = t(:notice_bad_request) + redirect_back_or_to(notifications_settings_path) + end + + private + + def update_project_notification_setting(project_id = params.dig(:notification_setting, :project_id)) + project = Project.find(project_id) + setting = @user.notification_settings.find_or_initialize_by(project:) + persist_notification_setting(setting, project_notification_params) + end + + def persist_notification_setting(setting, update_params) + if setting.update(update_params) + flash[:notice] = I18n.t(:notice_successful_update) + else + flash[:error] = I18n.t(:notice_failed_to_save_messages, + count: setting.errors.count, + object: setting.class.model_name.human) + end + end + + def project_notification_params + permitted_params.notification_setting_project.except(:project_id).merge(build_date_alerts_params) + end + + def build_date_alerts_params + ns_params = params.fetch(:notification_setting, {}) + { + start_date: date_alert_value(ns_params, :start_date), + due_date: date_alert_value(ns_params, :due_date), + overdue: date_alert_value(ns_params, :overdue) + } + end + + def workdays_pref_params + pref_params = permitted_params.pref.to_h + pref_params.merge("workdays" => pref_params.fetch("workdays", [])) + end + + def update_service_flash_message(call) + if call.success? + I18n.t(:notice_successful_update) + else + call.errors.full_messages.join(", ") + end + end + + def date_alert_value(ns_params, field) + return nil unless ns_params["#{field}_active"] == "1" + + ns_params[field.to_s].presence&.to_i + end + + # To be implemented by the including controller + def notifications_settings_path + raise SubclassResponsibilityError + end + + def workdays_redirect_path + raise SubclassResponsibilityError + end + + def project_notifications_create_url + raise SubclassResponsibilityError + end + + def project_setting_form_url(_project_id) + raise SubclassResponsibilityError + end + end +end diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 290fba911ac..c226fe18129 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -34,6 +34,7 @@ class MyController < ApplicationController include ActionView::Helpers::TagHelper include OpTurbo::ComponentStream include FlashMessagesOutputSafetyHelper + include Notifications::NotificationSettingsActions layout "my" @@ -46,20 +47,28 @@ class MyController < ApplicationController :locale, :interface, :update_settings, + :update_workdays, + :update_email_alerts, + :update_participating, + :update_non_participating, + :update_date_alerts, :password, :change_password, :password_confirmation_dialog, :notifications, - :reminders, :non_working_times, - :working_hours + :working_hours, + :new_project_settings, + :create_project_settings, + :edit_project_settings, + :update_project_settings, + :destroy_project_settings menu_item :account, only: [:account] menu_item :locale, only: [:locale] menu_item :interface, only: [:interface] menu_item :password, only: [:password] menu_item :notifications, only: [:notifications] - menu_item :reminders, only: [:reminders] menu_item :working_hours, only: %i[working_hours non_working_times] def account; end @@ -74,6 +83,22 @@ class MyController < ApplicationController write_settings end + def update_email_alerts + update_global_notification_setting(permitted_params.notification_setting_email_alerts) + end + + def update_participating + update_global_notification_setting(permitted_params.notification_setting_participating) + end + + def update_non_participating + update_global_notification_setting(permitted_params.notification_setting_non_participating) + end + + def update_date_alerts + update_global_notification_setting(build_date_alerts_params) + end + def interface; end # Manage user's password @@ -93,11 +118,10 @@ class MyController < ApplicationController respond_with_dialog My::PasswordConfirmationDialog.new end - # Configure user's in app notifications - def notifications; end - - # Configure user's mail reminders - def reminders; end + # Configure user's notifications and email reminders + def notifications + set_global_notification_setting + end def working_hours render_403 unless OpenProject::FeatureDecisions.user_working_times_active? @@ -164,6 +188,24 @@ class MyController < ApplicationController permitted_params.user.to_h.merge(params.permit(pref: {})) end + def update_global_notification_setting(update_params) + set_global_notification_setting + persist_notification_setting(@global_notification_setting, update_params) + redirect_back_or_to(my_notifications_path) + end + + def set_global_notification_setting + @global_notification_setting = @user.notification_settings.find_or_initialize_by(project: nil) + end + + def persist_notification_setting(setting, update_params) + if setting.update(update_params) + flash[:notice] = notice_account_updated + else + flash[:error] = error_account_update_failed(nil) + end + end + def notice_account_updated OpenProject::LocaleHelper.with_locale_for(current_user) do t(:notice_account_updated) @@ -175,6 +217,22 @@ class MyController < ApplicationController [t(:notice_account_update_failed), errors] end + def notifications_settings_path + my_notifications_path + end + + def workdays_redirect_path + my_notifications_path + end + + def project_notifications_create_url + my_project_notifications_path + end + + def project_setting_form_url(project_id) + my_project_setting_path(project_id:) + end + def set_current_user @user = current_user end diff --git a/app/controllers/scim_v2/base_controller_actions.rb b/app/controllers/scim_v2/base_controller_actions.rb index 01b3448478c..0617022889d 100644 --- a/app/controllers/scim_v2/base_controller_actions.rb +++ b/app/controllers/scim_v2/base_controller_actions.rb @@ -38,15 +38,9 @@ module ScimV2 rescue_from "ActiveRecord::RecordNotFound", with: :handle_resource_not_found def index - query = if params[:filter].blank? - storage_scope - else - attribute_map = storage_class.new.scim_queryable_attributes - parser = ::Scimitar::Lists::QueryParser.new(attribute_map) - - parser.parse(params[:filter]) - parser.to_activerecord_query(storage_scope) - end + # Applies .distinct to avoid duplicate records caused by + # left_joins in storage_scope (e.g. groups, auth provider links). + query = scim_index_storage_query.distinct pagination_info = scim_pagination_info(query.count) page_of_results = query @@ -75,19 +69,37 @@ module ScimV2 private - def include_attributes - first_level_attrs = storage_class.scim_attributes_map.keys.map(&:to_s) - second_level_attrs = - storage_class - .scim_attributes_map - .find_all { |_, v| v.is_a? Hash } - .flat_map { |parent, childs| childs.map { |child, _| "#{parent}.#{child}" } } - all_possible_attributes = (first_level_attrs + second_level_attrs) + # Builds the base query for the SCIM index action, + # applying any SCIM filter params if present. + def scim_index_storage_query + return storage_scope if params[:filter].blank? + # Returns the list of SCIM attributes to include in the response, + # excluding any attributes specified in the excludedAttributes param. + attribute_map = storage_class.new.scim_queryable_attributes + parser = ::Scimitar::Lists::QueryParser.new(attribute_map) + + parser.parse(params[:filter]) + parser.to_activerecord_query(storage_scope) + end + + def include_attributes + # Collects all possible SCIM attribute names (top-level and nested) + # from the storage class's scim_attributes_map. excluded_attributes = params.fetch(:excludedAttributes, "").split(",") excluded_parents = excluded_attributes.filter_map { |attr| attr.split(".")[-2] } - all_possible_attributes - excluded_attributes - excluded_parents + scim_all_attribute_names - excluded_attributes - excluded_parents + end + + def scim_all_attribute_names + map = storage_class.scim_attributes_map + nested = + map + .find_all { |_, v| v.is_a? Hash } + .flat_map { |parent, childs| childs.map { |child, _| "#{parent}.#{child}" } } + + map.keys.map(&:to_s) + nested end def raise_result_errors_for_scim(result) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 66c45903af7..856d15dcef6 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -31,19 +31,33 @@ class UsersController < ApplicationController include OpTurbo::ComponentStream include WorkingTimesAuthorization + include Notifications::NotificationSettingsActions layout "admin" before_action :authorize_global, except: %i[show deletion_info destroy] + # rubocop:disable Rails/LexicallyScopedActionFilter before_action :find_user, only: %i[show edit update + update_reminders + update_workdays + update_email_alerts + update_participating + update_non_participating + update_date_alerts + new_project_settings + create_project_settings + edit_project_settings + update_project_settings + destroy_project_settings change_status_info change_status destroy deletion_info resend_invitation] + # rubocop:enable Rails/LexicallyScopedActionFilter # should also contain destroy but post data can not be redirected before_action :require_login, only: [:deletion_info] before_action :authorize_for_user, only: [:destroy] @@ -109,6 +123,30 @@ class UsersController < ApplicationController end end + def update_email_alerts + global_setting = @user.notification_settings.find_or_initialize_by(project: nil) + persist_notification_setting(global_setting, permitted_params.notification_setting_email_alerts) + redirect_back_or_to edit_user_path(@user, tab: "reminders") + end + + def update_reminders + call = ::Users::UpdateService.new(model: @user, user: current_user).call(pref: permitted_params.pref.to_h) + flash[call.success? ? :notice : :error] = update_service_flash_message(call) + redirect_back_or_to edit_user_path(@user, tab: "reminders") + end + + def update_participating + update_user_notification_setting(permitted_params.notification_setting_participating) + end + + def update_non_participating + update_user_notification_setting(permitted_params.notification_setting_non_participating) + end + + def update_date_alerts + update_user_notification_setting(build_date_alerts_params) + end + def update # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity update_params = build_user_update_params call = ::Users::UpdateService.new(model: @user, user: current_user).call(update_params) @@ -261,6 +299,28 @@ class UsersController < ApplicationController private + def update_user_notification_setting(update_params) + global_setting = @user.notification_settings.find_or_initialize_by(project: nil) + persist_notification_setting(global_setting, update_params) + redirect_back_or_to edit_user_path(@user, tab: "notifications") + end + + def notifications_settings_path + edit_user_path(@user, tab: "notifications") + end + + def workdays_redirect_path + edit_user_path(@user, tab: "reminders") + end + + def project_notifications_create_url + project_notifications_user_path(@user) + end + + def project_setting_form_url(project_id) + project_setting_user_path(@user, project_id:) + end + def can_show_user? return true if can_manage_or_create_users? return true if @user == User.current diff --git a/app/controllers/work_packages/moves_controller.rb b/app/controllers/work_packages/moves_controller.rb index 861e3c0dd59..15b0a3fad29 100644 --- a/app/controllers/work_packages/moves_controller.rb +++ b/app/controllers/work_packages/moves_controller.rb @@ -33,7 +33,8 @@ class WorkPackages::MovesController < ApplicationController default_search_scope :work_packages before_action :find_work_packages, :check_project_uniqueness - before_action :authorize + before_action :authorize_move_or_copy + authorization_checked! :new, :create def new prepare_for_work_package_move @@ -47,6 +48,11 @@ class WorkPackages::MovesController < ApplicationController private + def authorize_move_or_copy + permission = params.has_key?(:copy) ? :copy_work_packages : :move_work_packages + do_authorize(permission) + end + def perform_operation if within_frontend_treshold? perform_in_frontend diff --git a/app/forms/admin/settings/external_links_settings_form.rb b/app/forms/admin/settings/external_links_settings_form.rb index 5aa54f233f0..1b26481695b 100644 --- a/app/forms/admin/settings/external_links_settings_form.rb +++ b/app/forms/admin/settings/external_links_settings_form.rb @@ -32,6 +32,8 @@ module Admin module Settings class ExternalLinksSettingsForm < ApplicationForm settings_form do |sf| + helpers.call_hook(:component_admin_settings_external_redirect, form: sf) + sf.check_box( name: :capture_external_links, caption: I18n.t(:setting_capture_external_links_text) diff --git a/app/forms/custom_fields/custom_field_rendering.rb b/app/forms/custom_fields/custom_field_rendering.rb index 8e5063e2991..ba2ef874492 100644 --- a/app/forms/custom_fields/custom_field_rendering.rb +++ b/app/forms/custom_fields/custom_field_rendering.rb @@ -69,7 +69,7 @@ module CustomFields::CustomFieldRendering end def custom_fields - raise NotImplementedError, "#custom_fields method needs to be overwritten and provide all custom fields we want to show" + raise SubclassResponsibilityError, "#custom_fields needs to be overwritten and provide all custom fields we want to show" end private diff --git a/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb b/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb index 5317f2db92f..a635482ff44 100644 --- a/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb +++ b/app/forms/custom_fields/inputs/base/autocomplete/multi_value_input.rb @@ -49,7 +49,7 @@ class CustomFields::Inputs::Base::Autocomplete::MultiValueInput < CustomFields:: end def decorated? - raise NotImplementedError + raise SubclassResponsibilityError end def custom_values diff --git a/app/forms/custom_fields/inputs/base/autocomplete/single_value_input.rb b/app/forms/custom_fields/inputs/base/autocomplete/single_value_input.rb index ff38c99ea61..5a0466e2def 100644 --- a/app/forms/custom_fields/inputs/base/autocomplete/single_value_input.rb +++ b/app/forms/custom_fields/inputs/base/autocomplete/single_value_input.rb @@ -49,6 +49,6 @@ class CustomFields::Inputs::Base::Autocomplete::SingleValueInput < CustomFields: end def decorated? - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/forms/my/alerts_form.rb b/app/forms/my/alerts_form.rb index c666a6ca054..7f7f4fe19e9 100644 --- a/app/forms/my/alerts_form.rb +++ b/app/forms/my/alerts_form.rb @@ -30,13 +30,15 @@ class My::AlertsForm < ApplicationForm form do |f| - f.check_box name: :warn_on_leaving_unsaved, - label: I18n.t("activerecord.attributes.user_preference.warn_on_leaving_unsaved") + f.fieldset_group(title: helpers.t("activerecord.attributes.user_preference.header_alerts"), mt: 3) do |fg| + fg.check_box name: :warn_on_leaving_unsaved, + label: helpers.t("activerecord.attributes.user_preference.warn_on_leaving_unsaved") - f.check_box name: :auto_hide_popups, - label: I18n.t("activerecord.attributes.user_preference.auto_hide_popups"), - caption: I18n.t("activerecord.attributes.user_preference.auto_hide_popups_caption") + fg.check_box name: :auto_hide_popups, + label: helpers.t("activerecord.attributes.user_preference.auto_hide_popups"), + caption: helpers.t("activerecord.attributes.user_preference.auto_hide_popups_caption") - f.submit(name: :submit, label: I18n.t("activerecord.attributes.user_preference.button_update_alerts"), scheme: :default) + fg.submit(name: :submit, label: helpers.t("activerecord.attributes.user_preference.button_update_alerts"), scheme: :default) + end end end diff --git a/app/forms/my/look_and_feel_form.rb b/app/forms/my/look_and_feel_form.rb index 5d3904d930c..eccbe3c79ec 100644 --- a/app/forms/my/look_and_feel_form.rb +++ b/app/forms/my/look_and_feel_form.rb @@ -32,53 +32,55 @@ class My::LookAndFeelForm < ApplicationForm include ApplicationHelper form do |f| - f.select_list( - name: :theme, - label: attribute_name(:theme), - caption: attribute_name(:mode_guideline), - required: true, - include_blank: false, - input_width: :small, - data: { - my__look_and_feel_target: "themeSelect", - action: "my--look-and-feel#updateContrastOptions" - } - ) do |select| - theme_options_for_select.each { |(label, value)| select.option(value:, label:) } + f.fieldset_group(title: helpers.t("activerecord.attributes.user_preference.header_look_and_feel")) do |fg| + fg.select_list( + name: :theme, + label: attribute_name(:theme), + caption: attribute_name(:mode_guideline), + required: true, + include_blank: false, + input_width: :small, + data: { + my__look_and_feel_target: "themeSelect", + action: "my--look-and-feel#updateContrastOptions" + } + ) do |select| + theme_options_for_select.each { |(label, value)| select.option(value:, label:) } + end + + fg.check_box_group(data: { my__look_and_feel_target: "autoThemeContrast" }) do |group| + group.check_box name: :force_light_theme_contrast, + label: attribute_name(:force_light_theme_contrast), + caption: attribute_name(:force_light_theme_contrast_caption) + group.check_box name: :force_dark_theme_contrast, + label: attribute_name(:force_dark_theme_contrast), + caption: attribute_name(:force_dark_theme_contrast_caption) + end + + fg.check_box_group(data: { my__look_and_feel_target: "singleThemeContrast" }) do |group| + group.check_box name: :increase_theme_contrast, + label: attribute_name(:increase_contrast), + caption: attribute_name(:increase_contrast_caption) + end + + fg.select_list( + name: :comments_sorting, + label: attribute_name(:comments_sorting), + required: true, + include_blank: false, + input_width: :small + ) do |select| + comment_sort_order_options.each { |(label, value)| select.option(value:, label:) } + end + + fg.check_box name: :disable_keyboard_shortcuts, + label: attribute_name(:disable_keyboard_shortcuts), + caption: disable_keyboard_shortcuts_caption + + fg.submit(name: :submit, + label: attribute_name(:button_update_look_and_feel), + scheme: :default) end - - f.check_box_group(data: { my__look_and_feel_target: "autoThemeContrast" }) do |group| - group.check_box name: :force_light_theme_contrast, - label: attribute_name(:force_light_theme_contrast), - caption: attribute_name(:force_light_theme_contrast_caption) - group.check_box name: :force_dark_theme_contrast, - label: attribute_name(:force_dark_theme_contrast), - caption: attribute_name(:force_dark_theme_contrast_caption) - end - - f.check_box_group(data: { my__look_and_feel_target: "singleThemeContrast" }) do |group| - group.check_box name: :increase_theme_contrast, - label: attribute_name(:increase_contrast), - caption: attribute_name(:increase_contrast_caption) - end - - f.select_list( - name: :comments_sorting, - label: attribute_name(:comments_sorting), - required: true, - include_blank: false, - input_width: :small - ) do |select| - comment_sort_order_options.each { |(label, value)| select.option(value:, label:) } - end - - f.check_box name: :disable_keyboard_shortcuts, - label: attribute_name(:disable_keyboard_shortcuts), - caption: disable_keyboard_shortcuts_caption - - f.submit(name: :submit, - label: attribute_name(:button_update_look_and_feel), - scheme: :default) end private diff --git a/app/forms/my/notifications/date_alerts_form.rb b/app/forms/my/notifications/date_alerts_form.rb new file mode 100644 index 00000000000..8dee7a1f60d --- /dev/null +++ b/app/forms/my/notifications/date_alerts_form.rb @@ -0,0 +1,134 @@ +# 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 My::Notifications::DateAlertsForm < ApplicationForm + START_DUE_TIMES = %w[0 1 3 7].freeze + OVERDUE_TIMES = %w[1 3 7].freeze + + def initialize(show_submit: true) + super() + @show_submit = show_submit + end + + form do |f| + f.fieldset_group(title: helpers.t("my_account.notifications.date_alerts.title"), mt: 3) do |fg| + %i[start_date due_date].each do |field| + active = model.send(:"#{field}_active") + + fg.check_box( + name: :"#{field}_active", + label: helpers.t("my_account.notifications.date_alerts.#{field}"), + id: "op-notification-type-#{field}-date-active--#{SecureRandom.uuid}}", + data: { + show_when_checked_target: "cause", + target_name: field.to_s, + test_selector: "global-notification-type-op-settings-#{field}-date-active" + } + ) do |cb| + cb.nested_form( + classes: [{ "d-none" => !active }], + data: { + show_when_checked_target: "effect", + target_name: field.to_s, + show_when: "checked" + } + ) do |builder| + TimeSelectForm.new(builder, field:, times: START_DUE_TIMES) + end + end + end + + active_overdue = model.overdue_active + + fg.check_box( + name: :overdue_active, + label: helpers.t("my_account.notifications.date_alerts.overdue"), + id: "op-notification-type-overdue-date-active--#{SecureRandom.uuid}}", + data: { + show_when_checked_target: "cause", + target_name: "overdue", + test_selector: "global-notification-type-op-settings-overdue-date-active" + } + ) do |cb| + cb.nested_form( + classes: [{ "d-none" => !active_overdue }], + data: { + show_when_checked_target: "effect", + target_name: "overdue", + show_when: "checked" + } + ) do |builder| + TimeSelectForm.new(builder, field: :overdue, times: OVERDUE_TIMES) + end + end + + if @show_submit + fg.submit(name: :submit, label: helpers.t("my_account.notifications.date_alerts.submit_button"), + scheme: :default) + end + end + end + + class TimeSelectForm < ApplicationForm + def initialize(field:, times:) + super() + @field = field + @times = times + end + + form do |f| + f.select_list( + name: @field, + label: helpers.t("my_account.notifications.date_alerts.#{@field}"), + visually_hide_label: true, + input_width: :xsmall, + data: { test_selector: "global-notification-type-op-reminder-settings-#{@field.to_s.underscore}-alerts" } + ) do |list| + @times.each do |value| + list.option( + label: helpers.t("my_account.notifications.date_alerts.times.#{time_key(value)}"), + value: + ) + end + end + end + + private + + def time_key(value) + case value + when "0" then "same_day" + when "1" then @field == :overdue ? "one_day_after" : "one_day_before" + when "3" then @field == :overdue ? "three_days_after" : "three_days_before" + when "7" then @field == :overdue ? "seven_days_after" : "seven_days_before" + end + end + end +end diff --git a/app/forms/my/notifications/non_participating_form.rb b/app/forms/my/notifications/non_participating_form.rb new file mode 100644 index 00000000000..beed3dd9bc7 --- /dev/null +++ b/app/forms/my/notifications/non_participating_form.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. +#++ + +class My::Notifications::NonParticipatingForm < ApplicationForm + def initialize(show_submit: true) + super() + @show_submit = show_submit + end + + form do |f| + f.fieldset_group(title: helpers.t("my_account.notifications.non_participating.title"), mt: 3) do |fg| + NotificationSetting.non_participating_settings.each do |setting| + fg.check_box( + name: setting, + label: helpers.t("my_account.notifications.non_participating.#{setting}"), + data: { test_selector: "global-notification-type-#{setting}" }, + id: "op-notification-type-#{setting}--#{SecureRandom.uuid}}" + ) + end + + if @show_submit + fg.submit(name: :submit, label: helpers.t("my_account.notifications.non_participating.submit_button"), + scheme: :default) + end + end + end +end diff --git a/app/forms/my/notifications/participating_form.rb b/app/forms/my/notifications/participating_form.rb new file mode 100644 index 00000000000..6d6a25ce84a --- /dev/null +++ b/app/forms/my/notifications/participating_form.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. +#++ + +class My::Notifications::ParticipatingForm < ApplicationForm + def initialize(show_submit: true) + super() + @show_submit = show_submit + end + + form do |f| + f.fieldset_group(title: helpers.t("my_account.notifications.participating.title"), mt: 2) do |fg| + fg.check_box( + name: :mentioned, + label: helpers.t("my_account.notifications.participating.mentioned"), + disabled: true, + data: { test_selector: "global-notification-type-mentioned" }, + id: "op-notification-mentioned--#{SecureRandom.uuid}}" + ) + fg.check_box( + name: :watched, + label: helpers.t("my_account.notifications.participating.watched"), + disabled: true, + data: { test_selector: "global-notification-type-watched" }, + id: "op-notification-watched--#{SecureRandom.uuid}}" + ) + fg.check_box( + name: :assignee, + label: helpers.t("my_account.notifications.participating.assignee"), + data: { test_selector: "global-notification-type-assignee" }, + id: "op-notification-assignee--#{SecureRandom.uuid}}" + ) + fg.check_box( + name: :responsible, + label: helpers.t("my_account.notifications.participating.responsible"), + data: { test_selector: "global-notification-type-responsible" }, + id: "op-notification-responsible--#{SecureRandom.uuid}}" + ) + fg.check_box( + name: :shared, + label: helpers.t("my_account.notifications.participating.shared"), + data: { test_selector: "global-notification-type-shared" }, + id: "op-notification-shared--#{SecureRandom.uuid}}" + ) + + if @show_submit + fg.submit(name: :submit, label: helpers.t("my_account.notifications.participating.submit_button"), + scheme: :default) + end + end + end +end diff --git a/app/forms/my/notifications/project_autocompleter_form.rb b/app/forms/my/notifications/project_autocompleter_form.rb new file mode 100644 index 00000000000..db18efa0608 --- /dev/null +++ b/app/forms/my/notifications/project_autocompleter_form.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. +#++ + +class My::Notifications::ProjectAutocompleterForm < ApplicationForm + def initialize(readonly: false, user: nil) + super() + @readonly = readonly + @excluded_project_ids = if !readonly && user + user.notification_settings.where.not(project: nil).pluck(:project_id).map(&:to_s) + else + [] + end + end + + form do |f| + filters = [{ name: "active", operator: "=", values: ["t"] }] + filters << { name: "id", operator: "!", values: @excluded_project_ids } if @excluded_project_ids.any? + + f.project_autocompleter( + name: :project_id, + label: Project.model_name.human, + required: true, + autocomplete_options: { + data: { test_selector: "my-notifications-project-autocompleter" }, + appendTo: "##{My::Notifications::ProjectSettingsDialogComponent::DIALOG_ID}", + readonly: @readonly, + filters: + } + ) + end +end diff --git a/app/forms/my/reminders/daily_reminders_form.rb b/app/forms/my/reminders/daily_reminders_form.rb new file mode 100644 index 00000000000..7a303b229de --- /dev/null +++ b/app/forms/my/reminders/daily_reminders_form.rb @@ -0,0 +1,62 @@ +# 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 My::Reminders::DailyRemindersForm < ApplicationForm + DailyRemindersFormModel = Data.define(:enabled, :times) + + form do |f| + f.fieldset_group(title: helpers.t("my_account.email_reminders.daily_reminders.title"), mt: 3) do |fg| + fg.check_box( + name: :enabled, + label: helpers.t("my_account.email_reminders.daily_reminders.enabled"), + data: { + target_name: "daily-enabled", + show_when_checked_target: "cause" + } + ) do |cb| + cb.nested_form( + classes: [{ "d-none" => !model.enabled }], + data: { + target_name: "daily-enabled", + show_when_checked_target: "effect", + show_when: "checked" + } + ) do |times_builder| + My::Reminders::DailyTimesComponent.new( + times: model.times || [], + scope: times_builder.object_name + ) + end + end + + fg.submit(name: :submit, label: helpers.t("button_save"), scheme: :default) + end + end +end diff --git a/app/forms/my/reminders/email_alerts_form.rb b/app/forms/my/reminders/email_alerts_form.rb new file mode 100644 index 00000000000..a22c7375446 --- /dev/null +++ b/app/forms/my/reminders/email_alerts_form.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. +#++ + +class My::Reminders::EmailAlertsForm < ApplicationForm + form do |f| + f.fieldset_group(title: helpers.t("my_account.email_reminders.email_alerts.title"), mt: 3) do |fg| + NotificationSetting.email_settings.each do |setting| + fg.check_box( + name: setting, + label: helpers.t("my_account.email_reminders.email_alerts.#{setting}") + ) + end + + fg.submit(name: :submit, label: helpers.t("my_account.email_reminders.email_alerts.submit_button"), scheme: :default) + end + end +end diff --git a/app/forms/my/reminders/immediate_reminders_form.rb b/app/forms/my/reminders/immediate_reminders_form.rb new file mode 100644 index 00000000000..7c5d010462e --- /dev/null +++ b/app/forms/my/reminders/immediate_reminders_form.rb @@ -0,0 +1,48 @@ +# 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 My::Reminders::ImmediateRemindersForm < ApplicationForm + form do |f| + f.fieldset_group(title: helpers.t("my_account.email_reminders.immediate_reminders.title"), mt: 2) do |fg| + fg.check_box( + name: :mentioned, + label: helpers.t("my_account.email_reminders.immediate_reminders.mentioned"), + data: { test_selector: "immediate-reminder-mentioned" } + ) + fg.check_box( + name: :personal_reminder, + label: helpers.t("my_account.email_reminders.immediate_reminders.personal_reminder"), + data: { test_selector: "immediate-reminder-personal_reminder" } + ) + + fg.submit(name: :submit, label: helpers.t("button_save"), scheme: :default) + end + end +end diff --git a/app/forms/my/reminders/pause_reminders_form.rb b/app/forms/my/reminders/pause_reminders_form.rb new file mode 100644 index 00000000000..4e20f282b26 --- /dev/null +++ b/app/forms/my/reminders/pause_reminders_form.rb @@ -0,0 +1,72 @@ +# 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 My::Reminders::PauseRemindersForm < ApplicationForm + PauseRemindersFormModel = Data.define(:enabled, :first_day, :last_day) + + form do |f| + f.fieldset_group(title: helpers.t("my_account.email_reminders.pause_reminders.title"), mt: 3) do |fg| + fg.check_box( + name: :enabled, + id: "pause-reminders-enabled", + label: helpers.t("my_account.email_reminders.pause_reminders.enabled"), + data: { + target_name: "pause-enabled", + show_when_checked_target: "cause" + } + ) do |cb| + cb.nested_form( + classes: [{ "d-none" => !model.enabled }], + data: { + target_name: "pause-enabled", + show_when_checked_target: "effect", + show_when: "checked" + } + ) do |builder| + DateRangeForm.new(builder) + end + end + + fg.submit(name: :submit, label: helpers.t("button_save"), scheme: :default) + end + end + + class DateRangeForm < ApplicationForm + form do |f| + f.range_date_picker( + name: :date_range, + visually_hide_label: true, + input_width: :small, + label: helpers.t("my_account.email_reminders.pause_reminders.date_range"), + value: [model.first_day, model.last_day].compact_blank.join(" - ") + ) + end + end +end diff --git a/app/forms/my/reminders/workdays_form.rb b/app/forms/my/reminders/workdays_form.rb new file mode 100644 index 00000000000..a555a7ab05a --- /dev/null +++ b/app/forms/my/reminders/workdays_form.rb @@ -0,0 +1,46 @@ +# 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 My::Reminders::WorkdaysForm < ApplicationForm + WEEKDAY_NUMBERS = [1, 2, 3, 4, 5, 6, 7].freeze + + form do |f| + f.fieldset_group(title: helpers.t("my_account.email_reminders.workdays.title"), mt: 3) do |fg| + fg.check_box_group(name: :workdays) do |group| + WEEKDAY_NUMBERS.each do |day_num| + day_label = helpers.t("date.day_names")[day_num % 7] + group.check_box(label: day_label, value: day_num) + end + end + + fg.submit(name: :submit, label: helpers.t("my_account.email_reminders.workdays.submit_button"), scheme: :default) + end + end +end diff --git a/app/forms/projects/settings/editable_identifier_form.rb b/app/forms/projects/settings/editable_identifier_form.rb index 2ff8af4b19c..979d60cfe87 100644 --- a/app/forms/projects/settings/editable_identifier_form.rb +++ b/app/forms/projects/settings/editable_identifier_form.rb @@ -31,7 +31,7 @@ module Projects module Settings class EditableIdentifierForm < ApplicationForm form do |f| - if Setting::WorkPackageIdentifier.alphanumeric? + if Setting::WorkPackageIdentifier.semantic? f.text_field( name: :identifier, label: attribute_name(:identifier), diff --git a/app/forms/projects/settings/identifier_form.rb b/app/forms/projects/settings/identifier_form.rb index 872e8de891d..8404602c756 100644 --- a/app/forms/projects/settings/identifier_form.rb +++ b/app/forms/projects/settings/identifier_form.rb @@ -31,7 +31,7 @@ module Projects module Settings class IdentifierForm < ApplicationForm form do |f| - caption_key = if Setting::WorkPackageIdentifier.alphanumeric? + caption_key = if Setting::WorkPackageIdentifier.semantic? :text_project_identifier_description else :text_project_identifier_url_description diff --git a/app/menus/submenu.rb b/app/menus/submenu.rb index a6058e0ee21..92094f83971 100644 --- a/app/menus/submenu.rb +++ b/app/menus/submenu.rb @@ -72,7 +72,7 @@ class Submenu end def default_queries - raise NotImplementedError + raise SubclassResponsibilityError end def global_queries @@ -150,7 +150,7 @@ class Submenu end def query_path(query_params) - raise NotImplementedError + raise SubclassResponsibilityError end def url_helpers diff --git a/app/models/activities/event_mapper.rb b/app/models/activities/event_mapper.rb index e563726ce75..368ccb78a25 100644 --- a/app/models/activities/event_mapper.rb +++ b/app/models/activities/event_mapper.rb @@ -77,11 +77,11 @@ module Activities end def event_data(journal) - raise NotImplementedError + raise SubclassResponsibilityError end def event_title(journal, data) - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/app/models/attribute_help_text.rb b/app/models/attribute_help_text.rb index a9351c28c14..5a4e7313457 100644 --- a/app/models/attribute_help_text.rb +++ b/app/models/attribute_help_text.rb @@ -80,15 +80,15 @@ class AttributeHelpText < ApplicationRecord end def type_caption - raise NotImplementedError + raise SubclassResponsibilityError end def self.visible_condition(_user = nil) - raise NotImplementedError + raise SubclassResponsibilityError end def self.available_attributes - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/models/auth_provider.rb b/app/models/auth_provider.rb index 0ef960a072e..5f69a4f24e3 100644 --- a/app/models/auth_provider.rb +++ b/app/models/auth_provider.rb @@ -49,7 +49,7 @@ class AuthProvider < ApplicationRecord end def human_type - raise NotImplementedError + raise SubclassResponsibilityError end def auth_url diff --git a/app/models/capabilities/scopes/visible.rb b/app/models/capabilities/scopes/visible.rb index 6d7b289d628..521ac4f13a1 100644 --- a/app/models/capabilities/scopes/visible.rb +++ b/app/models/capabilities/scopes/visible.rb @@ -34,7 +34,7 @@ module Capabilities::Scopes class_methods do def visible(user = User.current) - scope = if user.admin? + scope = if user.active_admin? all else where(context_id: nil) diff --git a/app/models/custom_actions/actions/base.rb b/app/models/custom_actions/actions/base.rb index c499282473d..3c5a2f377a8 100644 --- a/app/models/custom_actions/actions/base.rb +++ b/app/models/custom_actions/actions/base.rb @@ -42,7 +42,7 @@ class CustomActions::Actions::Base end def allowed_values - raise NotImplementedError + raise SubclassResponsibilityError end def value_objects @@ -52,11 +52,11 @@ class CustomActions::Actions::Base end def type - raise NotImplementedError + raise SubclassResponsibilityError end def apply(_work_package) - raise NotImplementedError + raise SubclassResponsibilityError end def human_name @@ -64,7 +64,7 @@ class CustomActions::Actions::Base end def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def self.all diff --git a/app/models/custom_actions/actions/custom_field.rb b/app/models/custom_actions/actions/custom_field.rb index 55393c9dcea..dd796273c4f 100644 --- a/app/models/custom_actions/actions/custom_field.rb +++ b/app/models/custom_actions/actions/custom_field.rb @@ -35,7 +35,7 @@ class CustomActions::Actions::CustomField < CustomActions::Actions::Base end def custom_field - raise NotImplementedError + raise SubclassResponsibilityError end def all diff --git a/app/models/custom_actions/conditions/base.rb b/app/models/custom_actions/conditions/base.rb index add9a84c872..9852d382fbc 100644 --- a/app/models/custom_actions/conditions/base.rb +++ b/app/models/custom_actions/conditions/base.rb @@ -67,7 +67,7 @@ class CustomActions::Conditions::Base end def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def validate(errors) diff --git a/app/models/custom_fields/scopes/visible.rb b/app/models/custom_fields/scopes/visible.rb index 1a70ee93961..2709e8ba873 100644 --- a/app/models/custom_fields/scopes/visible.rb +++ b/app/models/custom_fields/scopes/visible.rb @@ -34,9 +34,13 @@ module CustomFields::Scopes class_methods do def visible(user = User.current) - known_subclasses - .inject(none) do |scope, klass| - scope.or(where(type: klass.name).and(klass.visible(user))) + if user.active_admin? + all + else + known_subclasses + .inject(none) do |scope, klass| + scope.or(where(type: klass.name).and(klass.visible(user))) + end end end diff --git a/app/models/custom_value/ar_object_strategy.rb b/app/models/custom_value/ar_object_strategy.rb index f8cbb6f4280..4c9752ff8ed 100644 --- a/app/models/custom_value/ar_object_strategy.rb +++ b/app/models/custom_value/ar_object_strategy.rb @@ -69,7 +69,7 @@ class CustomValue::ARObjectStrategy < CustomValue::FormatStrategy end def ar_class - raise NotImplementedError + raise SubclassResponsibilityError end def ar_object(value) diff --git a/app/models/custom_value/format_strategy.rb b/app/models/custom_value/format_strategy.rb index 85e484bb2a5..5f576e87bdf 100644 --- a/app/models/custom_value/format_strategy.rb +++ b/app/models/custom_value/format_strategy.rb @@ -42,7 +42,7 @@ class CustomValue::FormatStrategy # Returns the value of the CustomValue in a typed fashion (i.e. not as the string # that is used for representation in the database) def typed_value - raise "SubclassResponsibility" + raise SubclassResponsibilityError end # Returns the value of the CustomValue formatted to a string @@ -59,6 +59,6 @@ class CustomValue::FormatStrategy # Validates the type of the custom field and returns a symbol indicating the validation error # if an error occurred; returns nil if no error occurred def validate_type_of_value - raise "SubclassResponsibility" + raise SubclassResponsibilityError end end diff --git a/app/models/exports/exporter.rb b/app/models/exports/exporter.rb index 17ab532b76d..0c42581fef4 100644 --- a/app/models/exports/exporter.rb +++ b/app/models/exports/exporter.rb @@ -63,7 +63,7 @@ module Exports # Run the export, yielding the result of the render output def export! - raise NotImplementedError + raise SubclassResponsibilityError end protected diff --git a/app/models/group_custom_fields/scopes/visible.rb b/app/models/group_custom_fields/scopes/visible.rb index cd55eb2e953..7cb7e9b7706 100644 --- a/app/models/group_custom_fields/scopes/visible.rb +++ b/app/models/group_custom_fields/scopes/visible.rb @@ -34,7 +34,7 @@ module GroupCustomFields::Scopes class_methods do def visible(user = User.current) - if user.admin? + if user.active_admin? all else where(admin_only: false) diff --git a/app/models/members/scopes/visible.rb b/app/models/members/scopes/visible.rb index 8fe3adafe2e..f6429e5e468 100644 --- a/app/models/members/scopes/visible.rb +++ b/app/models/members/scopes/visible.rb @@ -35,7 +35,7 @@ module Members::Scopes class_methods do # Find all members visible to the inquiring user def visible(user = User.current) - if user.admin? + if user.active_admin? visible_for_admins else visible_for_non_admins(user) diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb index 5640853e178..a717b650e47 100644 --- a/app/models/notification_setting.rb +++ b/app/models/notification_setting.rb @@ -68,6 +68,16 @@ class NotificationSetting < ApplicationRecord ] end + def self.non_participating_settings + [ + WORK_PACKAGE_CREATED, + WORK_PACKAGE_COMMENTED, + WORK_PACKAGE_PROCESSED, + WORK_PACKAGE_PRIORITIZED, + WORK_PACKAGE_SCHEDULED + ] + end + def self.date_alert_settings [ START_DATE, @@ -94,4 +104,18 @@ class NotificationSetting < ApplicationRecord include Scopes::Scoped scopes :applicable + + # rubocop:disable Naming/PredicateMethod + def start_date_active + start_date.present? + end + + def due_date_active + due_date.present? + end + + def overdue_active + overdue.present? + end + # rubocop:enable Naming/PredicateMethod end diff --git a/app/models/permitted_params.rb b/app/models/permitted_params.rb index 5870bdd695a..3470efc1006 100644 --- a/app/models/permitted_params.rb +++ b/app/models/permitted_params.rb @@ -274,7 +274,31 @@ class PermittedParams :comments_sorting, :disable_keyboard_shortcuts, :warn_on_leaving_unsaved, - :auto_hide_popups) + :auto_hide_popups, + immediate_reminders: %i[mentioned personal_reminder], + daily_reminders: [:enabled, { times: [] }], + workdays: [], + pause_reminders: %i[enabled date_range]) + end + + def notification_setting_email_alerts + params.fetch(:notification_setting, {}).permit(*NotificationSetting.email_settings) + end + + def notification_setting_participating + params.fetch(:notification_setting, {}).permit(:assignee, :responsible, :shared) + end + + def notification_setting_non_participating + params.fetch(:notification_setting, {}).permit(*NotificationSetting.non_participating_settings) + end + + def notification_setting_project + params.fetch(:notification_setting, {}).permit( + :project_id, + :assignee, :responsible, :shared, + *NotificationSetting.non_participating_settings + ) end def project diff --git a/app/models/principal.rb b/app/models/principal.rb index adbe8fa58c3..1ec1c24a00e 100644 --- a/app/models/principal.rb +++ b/app/models/principal.rb @@ -137,7 +137,7 @@ class Principal < ApplicationRecord # Columns required for formatting the principal's name. def self.columns_for_name(formatter = nil) - raise NotImplementedError, "Redefine in subclass" unless self == Principal + raise SubclassResponsibilityError, "Redefine in subclass" unless self == Principal [User, Group, PlaceholderUser].map { it.columns_for_name(formatter) }.inject(:|) end diff --git a/app/models/principals/scopes/visible.rb b/app/models/principals/scopes/visible.rb index 1a25ab8da6a..20445de2230 100644 --- a/app/models/principals/scopes/visible.rb +++ b/app/models/principals/scopes/visible.rb @@ -42,7 +42,7 @@ module Principals::Scopes class_methods do def visible(user = ::User.current) - if user.allowed_globally?(:view_all_principals) || user.admin? + if user.allowed_globally?(:view_all_principals) all else in_visible_project_or_me_or_same_groups(user) diff --git a/app/models/project.rb b/app/models/project.rb index 3a5aa7853fd..8d01e969e0c 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -39,6 +39,7 @@ class Project < ApplicationRecord include Projects::WorkPackageCustomFields include Projects::CreationWizard include Projects::Identifier + include Projects::SemanticIdentifier include ::Scopes::Scoped diff --git a/app/models/project_custom_field.rb b/app/models/project_custom_field.rb index 10c3aadcb33..c13e97f75ee 100644 --- a/app/models/project_custom_field.rb +++ b/app/models/project_custom_field.rb @@ -56,7 +56,7 @@ class ProjectCustomField < CustomField class << self def visible(user = User.current, project: nil) - if user.admin? + if user.active_admin? all elsif user.allowed_in_any_project?(:select_project_custom_fields) || user.allowed_globally?(:add_project) where(admin_only: false) diff --git a/app/models/project_custom_fields/scopes/visible.rb b/app/models/project_custom_fields/scopes/visible.rb index eee99147a52..d0fec1ba6cf 100644 --- a/app/models/project_custom_fields/scopes/visible.rb +++ b/app/models/project_custom_fields/scopes/visible.rb @@ -34,7 +34,7 @@ module ProjectCustomFields::Scopes class_methods do def visible(user = User.current, project: nil) - if user.admin? + if user.active_admin? all elsif user.allowed_in_any_project?(:select_project_custom_fields) || user.allowed_globally?(:add_project) where(admin_only: false) diff --git a/app/models/projects/identifier.rb b/app/models/projects/identifier.rb index 5f7d690e770..24edb5adc5c 100644 --- a/app/models/projects/identifier.rb +++ b/app/models/projects/identifier.rb @@ -53,36 +53,30 @@ module Projects::Identifier limit: IDENTIFIER_MAX_LENGTH, blacklist: RESERVED_IDENTIFIERS, adapter: OpenProject::ActsAsUrl::Adapter::OpActiveRecord, # use a custom adapter able to handle edge cases - skip_if: -> { Setting::WorkPackageIdentifier.alphanumeric? } + skip_if: -> { Setting::WorkPackageIdentifier.semantic? } # Generate semantic identifier (when in the semantic mode) before_validation :generate_semantic_identifier, on: :create, - if: -> { Setting::WorkPackageIdentifier.alphanumeric? && identifier.blank? } + if: -> { Setting::WorkPackageIdentifier.semantic? && identifier.blank? } ### ID validators - # Validators for the legacy underscored identifier format (e.g. "project_one") + # Shared validators for all identifier formats validates :identifier, presence: true, - uniqueness: { case_sensitive: true }, + uniqueness: { case_sensitive: false }, length: { maximum: IDENTIFIER_MAX_LENGTH }, - exclusion: RESERVED_IDENTIFIERS, if: ->(p) { p.persisted? || p.identifier.present? } - # Contains only a-z, 0-9, dashes and underscores but cannot consist of numbers only as it would clash with the id. - validates :identifier, - format: { with: /\A(?!^\d+\z)[a-z0-9\-_]+\z/ }, - if: ->(p) { - p.identifier_changed? && p.identifier.present? && Setting::WorkPackageIdentifier.numeric? - } - # Validators for the semantic identifier format - validates :identifier, - format: { with: /\A[A-Z]/, message: :must_start_with_letter }, - if: ->(p) { p.identifier_changed? && p.identifier.present? && Setting::WorkPackageIdentifier.alphanumeric? } - validates :identifier, - format: { with: /\A[A-Z][A-Z0-9_]*\z/, message: :no_special_characters }, - length: { maximum: SEMANTIC_IDENTIFIER_MAX_LENGTH }, - if: ->(p) { p.identifier_changed? && p.identifier.present? && Setting::WorkPackageIdentifier.alphanumeric? } + # Validators for the numeric (legacy) identifier format (e.g. "my-project", "project_one") + validate :identifier_numeric_format, + if: ->(p) { p.identifier_changed? && p.identifier.present? && Setting::WorkPackageIdentifier.classic? } + + # Validators for the semantic (alphanumeric) identifier format (e.g. "PROJ1") + validate :identifier_alphanumeric_format, + if: ->(p) { p.identifier_changed? && p.identifier.present? && Setting::WorkPackageIdentifier.semantic? } + + validate :identifier_not_reserved, if: -> { identifier.present? } # Complements the uniqueness validation above: once an identifier has been used by a # project, it remains reserved for that project even after the project moves to a new @@ -102,7 +96,7 @@ module Projects::Identifier class_methods do def suggest_identifier(name) - if Setting::WorkPackageIdentifier.alphanumeric? + if Setting::WorkPackageIdentifier.semantic? WorkPackages::IdentifierAutofix::ProjectIdentifierSuggestionGenerator.suggest_identifier(name) else # This should closely enough emulate Project models' usage of acts_as_url name.to_url.first(IDENTIFIER_MAX_LENGTH).presence || "project" @@ -130,14 +124,38 @@ module Projects::Identifier private + # Contains only a-z, 0-9, dashes and underscores but cannot consist of numbers only + # as that would clash with the numeric id. + def identifier_numeric_format + unless identifier.match?(/\A(?!^\d+\z)[a-z0-9\-_]+\z/) + errors.add(:identifier, :invalid) + end + end + + def identifier_alphanumeric_format + errors.add(:identifier, :must_start_with_letter) unless identifier.match?(/\A[A-Z]/) + errors.add(:identifier, :no_special_characters) unless identifier.match?(/\A[A-Z0-9_]*\z/) + if identifier.length > SEMANTIC_IDENTIFIER_MAX_LENGTH + errors.add(:identifier, :too_long, count: SEMANTIC_IDENTIFIER_MAX_LENGTH) + end + end + + def identifier_not_reserved + if RESERVED_IDENTIFIERS.include?(identifier&.downcase) + errors.add(:identifier, :exclusion) + end + end + # Checks friendly_id_slugs for any project that previously used this identifier and - # has since changed it. It allows to switch back to an identifier the project itself - # has used before. + # has since changed it. It allows a project to switch back to an identifier it has + # used before. Uses LOWER() because slugs may be stored in a different case than the + # incoming identifier (e.g. old lowercase slug vs new uppercase alphanumeric identifier). def identifier_not_historically_reserved return if errors.any? { |error| error.attribute == :identifier && error.type == :taken } already_existing = FriendlyId::Slug - .where(slug: identifier, sluggable_type: self.class.to_s) + .where("LOWER(slug) = LOWER(?)", identifier) + .where(sluggable_type: self.class.to_s) .where.not(sluggable_id: id) .exists? diff --git a/app/models/projects/scopes/visible.rb b/app/models/projects/scopes/visible.rb index 6ecae100fc3..ee627718e34 100644 --- a/app/models/projects/scopes/visible.rb +++ b/app/models/projects/scopes/visible.rb @@ -40,7 +40,7 @@ module Projects::Scopes def visible(user = User.current) # Use a shortcut for admins and anonymous where # we don't need to calculate for work package roles which is more expensive - if user.admin? || user.anonymous? + if user.active_admin? || user.anonymous? allowed_to(user, :view_project) else active.public_projects.or(active.where(id: user.members.select(:project_id))) diff --git a/app/models/projects/semantic_identifier.rb b/app/models/projects/semantic_identifier.rb new file mode 100644 index 00000000000..100f04de7f3 --- /dev/null +++ b/app/models/projects/semantic_identifier.rb @@ -0,0 +1,97 @@ +# 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::SemanticIdentifier + extend ActiveSupport::Concern + + # Atomically allocates the next sequence number for a work package in this project + # and returns it paired with the resulting semantic identifier (e.g. [42, "PROJ-42"]). + # Uses an advisory lock scoped to this project to serialize concurrent allocations + # without blocking unrelated project row writes. + def allocate_wp_semantic_identifier! + seq = OpenProject::Mutex.with_advisory_lock( + self.class, + "wp_sequence_#{id}" + ) do + self.class.connection.select_value(<<~SQL.squish) + UPDATE projects + SET wp_sequence_counter = wp_sequence_counter + 1 + WHERE id = #{self.class.connection.quote(id)} + RETURNING wp_sequence_counter + SQL + end + + [seq, "#{identifier}-#{seq}"] + end + + # Called after this project's identifier is renamed. Atomically: + # 1. Appends new-prefix aliases for every WP that ever carried an old-prefix alias. + # 2. Updates identifier on resident WPs to the new prefix. + def handle_semantic_rename(old_identifier, batch_size: 1000) + like_pattern = "#{self.class.sanitize_sql_like(old_identifier)}-%" + prefix = "#{old_identifier}-" + new_prefix = "#{identifier}-" + + WorkPackageSemanticAlias.transaction do + append_aliases_with_new_prefix(like_pattern:, prefix:, new_prefix:, batch_size:) + rewrite_semantic_ids(like_pattern:, prefix:, new_prefix:, batch_size:) + end + end + + private + + # For every alias row whose identifier starts with the old prefix, inserts a + # corresponding row with the new prefix. This covers WPs still in the project + # as well as any that have moved out but still carry old-prefix alias rows. + def append_aliases_with_new_prefix(like_pattern:, prefix:, new_prefix:, batch_size:) + WorkPackageSemanticAlias + .where("identifier LIKE ?", like_pattern) + .in_batches(of: batch_size) do |relation| + now = Time.current + WorkPackageSemanticAlias.connection.execute( + WorkPackageSemanticAlias.sanitize_sql([<<~SQL.squish, { prefix:, new_prefix:, now: }]) + INSERT INTO work_package_semantic_aliases (identifier, work_package_id, created_at, updated_at) + SELECT REPLACE(identifier, :prefix, :new_prefix), work_package_id, :now, :now + FROM (#{relation.to_sql}) AS batch + ON CONFLICT (identifier) DO NOTHING + SQL + ) + end + end + + # Updates the identifier column on all resident WPs to replace the old prefix with the new one. + def rewrite_semantic_ids(like_pattern:, prefix:, new_prefix:, batch_size:) + WorkPackage + .where("identifier LIKE ?", like_pattern) + .in_batches(of: batch_size) do |relation| + relation.update_all(["identifier = REPLACE(identifier, ?, ?)", prefix, new_prefix]) + end + end +end diff --git a/app/models/queries/filters/base.rb b/app/models/queries/filters/base.rb index bf739596141..1b049ce549a 100644 --- a/app/models/queries/filters/base.rb +++ b/app/models/queries/filters/base.rb @@ -79,11 +79,11 @@ class Queries::Filters::Base end def human_name - raise NotImplementedError + raise SubclassResponsibilityError end def type - raise NotImplementedError + raise SubclassResponsibilityError end def allowed_values diff --git a/app/models/queries/filters/shared/parsed_filter.rb b/app/models/queries/filters/shared/parsed_filter.rb index 7d16415cb06..928cc314970 100644 --- a/app/models/queries/filters/shared/parsed_filter.rb +++ b/app/models/queries/filters/shared/parsed_filter.rb @@ -50,11 +50,11 @@ module Queries::Filters::Shared::ParsedFilter private def split_values - raise NotImplementedError + raise SubclassResponsibilityError end def value_conditions - raise NotImplementedError + raise SubclassResponsibilityError end def validate_values diff --git a/app/models/queries/filters/strategies/numeric.rb b/app/models/queries/filters/strategies/numeric.rb index f2b1d938418..66531229922 100644 --- a/app/models/queries/filters/strategies/numeric.rb +++ b/app/models/queries/filters/strategies/numeric.rb @@ -42,11 +42,11 @@ module Queries::Filters::Strategies private def numeric_class - raise NotImplementedError + raise SubclassResponsibilityError end def error_message - raise NotImplementedError + raise SubclassResponsibilityError end def validate_values_all_numeric diff --git a/app/models/queries/group_bys/base.rb b/app/models/queries/group_bys/base.rb index ec2b2772db4..b1adc00d224 100644 --- a/app/models/queries/group_bys/base.rb +++ b/app/models/queries/group_bys/base.rb @@ -45,7 +45,7 @@ module Queries end def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def association_class diff --git a/app/models/queries/operators/base.rb b/app/models/queries/operators/base.rb index 0aa864e84de..c70601a6de1 100644 --- a/app/models/queries/operators/base.rb +++ b/app/models/queries/operators/base.rb @@ -53,7 +53,7 @@ module Queries::Operators end def self.sql_for_field(_values, _db_table, _db_field) - raise NotImplementedError + raise SubclassResponsibilityError end def self.connection diff --git a/app/models/queries/orders/base.rb b/app/models/queries/orders/base.rb index 4d8f4630e6b..ab72786bae3 100644 --- a/app/models/queries/orders/base.rb +++ b/app/models/queries/orders/base.rb @@ -50,7 +50,7 @@ module Queries end def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def apply_to(query_scope) diff --git a/app/models/queries/projects/filters/dynamically_from_project_phase.rb b/app/models/queries/projects/filters/dynamically_from_project_phase.rb index 1b27413f406..7d92fa3df50 100644 --- a/app/models/queries/projects/filters/dynamically_from_project_phase.rb +++ b/app/models/queries/projects/filters/dynamically_from_project_phase.rb @@ -56,7 +56,7 @@ module Queries::Projects::Filters::DynamicallyFromProjectPhase end def key - raise NotImplementedError + raise SubclassResponsibilityError end private @@ -77,7 +77,7 @@ module Queries::Projects::Filters::DynamicallyFromProjectPhase end def create_from_phase(_phase, _context) - raise NotImplementedError + raise SubclassResponsibilityError end def accessor_matches?(definition, match) diff --git a/app/models/queries/projects/filters/filter_on_project_phase.rb b/app/models/queries/projects/filters/filter_on_project_phase.rb index 0b6a5bf4613..ee137dd9492 100644 --- a/app/models/queries/projects/filters/filter_on_project_phase.rb +++ b/app/models/queries/projects/filters/filter_on_project_phase.rb @@ -74,23 +74,23 @@ module Queries::Projects::Filters::FilterOnProjectPhase private def on_date - raise NotImplementedError + raise SubclassResponsibilityError end def on_today - raise NotImplementedError + raise SubclassResponsibilityError end def between_date - raise NotImplementedError + raise SubclassResponsibilityError end def this_week - raise NotImplementedError + raise SubclassResponsibilityError end def none - raise NotImplementedError + raise SubclassResponsibilityError end def project_phase_scope_limit(scope) diff --git a/app/models/queries/relations/filters/visibility_checking.rb b/app/models/queries/relations/filters/visibility_checking.rb index 4a5d102175e..6f4999cb581 100644 --- a/app/models/queries/relations/filters/visibility_checking.rb +++ b/app/models/queries/relations/filters/visibility_checking.rb @@ -54,7 +54,7 @@ module Queries private def visibility_checked_sql(_operator, _values, _visible_sql) - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/app/models/queries/selects/base.rb b/app/models/queries/selects/base.rb index a1d25a8e160..ec4175a41e9 100644 --- a/app/models/queries/selects/base.rb +++ b/app/models/queries/selects/base.rb @@ -32,7 +32,7 @@ class Queries::Selects::Base include ActiveModel::Validations def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def self.available? diff --git a/app/models/queries/work_packages/filter/attachment_base_filter.rb b/app/models/queries/work_packages/filter/attachment_base_filter.rb index a8d97e4b7a4..c4fdb72c69a 100644 --- a/app/models/queries/work_packages/filter/attachment_base_filter.rb +++ b/app/models/queries/work_packages/filter/attachment_base_filter.rb @@ -68,7 +68,7 @@ class Queries::WorkPackages::Filter::AttachmentBaseFilter < Queries::WorkPackage end def search_column - raise NotImplementedError + raise SubclassResponsibilityError end def normalization_type diff --git a/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb b/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb index 5594e280e57..0fefe8cf7cc 100644 --- a/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb +++ b/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb @@ -47,7 +47,7 @@ module Queries::WorkPackages::Filter::FilterOnDirectedRelationsMixin end def relation_type - raise NotImplementedError + raise SubclassResponsibilityError end def normalized_relation_type @@ -57,10 +57,10 @@ module Queries::WorkPackages::Filter::FilterOnDirectedRelationsMixin private def relation_filter - raise NotImplementedError + raise SubclassResponsibilityError end def relation_select - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb b/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb index d8956e65ce6..131c6d37179 100644 --- a/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb +++ b/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb @@ -42,7 +42,7 @@ module Queries::WorkPackages::Filter::FilterOnUndirectedRelationsMixin end def relation_type - raise NotImplementedError + raise SubclassResponsibilityError end private diff --git a/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb b/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb index 3ce0a5cd23d..8bab4565716 100644 --- a/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb +++ b/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb @@ -50,7 +50,7 @@ module Queries::WorkPackages::Filter::OrFilterForWpMixin end def filter_configurations - raise NotImplementedError + raise SubclassResponsibilityError end def create_instances diff --git a/app/models/query.rb b/app/models/query.rb index d72939dcb9c..ed9b540a515 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -223,10 +223,12 @@ class Query < ApplicationRecord end def self.available_columns(project = nil) - Queries::Register - .selects[self] - .map { |col| col.instances(project) } - .flatten + RequestStore.fetch(:"available_columns_#{project&.id}") do + Queries::Register + .selects[self] + .map { |col| col.instances(project) } + .flatten + end end def self.displayable_columns diff --git a/app/models/setting/work_package_identifier.rb b/app/models/setting/work_package_identifier.rb index fb0004bada8..4ae7ee08e10 100644 --- a/app/models/setting/work_package_identifier.rb +++ b/app/models/setting/work_package_identifier.rb @@ -30,11 +30,11 @@ class Setting module WorkPackageIdentifier - NUMERIC = "numeric" - ALPHANUMERIC = "alphanumeric" - ALLOWED_VALUES = [NUMERIC, ALPHANUMERIC].freeze + CLASSIC = "classic" + SEMANTIC = "semantic" + ALLOWED_VALUES = [CLASSIC, SEMANTIC].freeze - def self.alphanumeric? = Setting[:work_packages_identifier] == ALPHANUMERIC - def self.numeric? = Setting[:work_packages_identifier] == NUMERIC + def self.semantic? = Setting[:work_packages_identifier] == SEMANTIC + def self.classic? = Setting[:work_packages_identifier] == CLASSIC end end diff --git a/app/models/sharing_strategies/base_strategy.rb b/app/models/sharing_strategies/base_strategy.rb index cb47cf22d90..a9061e11974 100644 --- a/app/models/sharing_strategies/base_strategy.rb +++ b/app/models/sharing_strategies/base_strategy.rb @@ -40,36 +40,36 @@ module SharingStrategies def available_roles # format: [{ label: "Role name", value: 42, description: "Role description", default: true }] - raise NotImplementedError, "Override in a subclass and return an array of roles that should be displayed" + raise SubclassResponsibilityError, "Override in a subclass and return an array of roles that should be displayed" end def viewable? - raise NotImplementedError, + raise SubclassResponsibilityError, "Override in a subclass and return true if the current user can view who the entity is shared with" end def manageable? - raise NotImplementedError, "Override in a subclass and return true if the current user can manage sharing" + raise SubclassResponsibilityError, "Override in a subclass and return true if the current user can manage sharing" end def create_contract_class - raise NotImplementedError, "Override in a subclass and return the contract class for creating a share" + raise SubclassResponsibilityError, "Override in a subclass and return the contract class for creating a share" end def update_contract_class - raise NotImplementedError, "Override in a subclass and return the contract class for updating a share" + raise SubclassResponsibilityError, "Override in a subclass and return the contract class for updating a share" end def delete_contract_class - raise NotImplementedError, "Override in a subclass and return the contract class for deleting a share" + raise SubclassResponsibilityError, "Override in a subclass and return the contract class for deleting a share" end def share_description(share) - raise NotImplementedError, "Override in a subclass and return a description for the shared user" + raise SubclassResponsibilityError, "Override in a subclass and return a description for the shared user" end def title - raise NotImplementedError, "Override in a subclass and return a title for the sharing dialog" + raise SubclassResponsibilityError, "Override in a subclass and return a title for the sharing dialog" end def enterprise_feature diff --git a/app/models/type/attributes.rb b/app/models/type/attributes.rb index 343258db690..bc4e787a1f5 100644 --- a/app/models/type/attributes.rb +++ b/app/models/type/attributes.rb @@ -82,10 +82,10 @@ module Type::Attributes WorkPackageCustomField.pluck(Arel.sql("max(updated_at), count(id)")).flatten end - OpenProject::Cache.fetch("all_work_package_form_attributes", - *wp_cf_cache_parts, - EXCLUDED.length, - merge_date) do + OpenProject::Cache.fetch_request_cached("all_work_package_form_attributes", + *wp_cf_cache_parts, + EXCLUDED.length, + merge_date) do calculate_all_work_package_form_attributes(merge_date) end end diff --git a/app/models/type/form_group.rb b/app/models/type/form_group.rb index 7014859dff5..dbdb95d27af 100644 --- a/app/models/type/form_group.rb +++ b/app/models/type/form_group.rb @@ -57,10 +57,10 @@ class Type::FormGroup end def members - raise NotImplementedError + raise SubclassResponsibilityError end def active_members(_project) - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/models/user.rb b/app/models/user.rb index 707eeaa0b26..03e104a129b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -514,6 +514,10 @@ class User < Principal !logged? end + def active_admin? + admin? && active? + end + def consent_expired? # Always if the user has not consented return true if consented_at.blank? diff --git a/app/models/user_custom_fields/scopes/visible.rb b/app/models/user_custom_fields/scopes/visible.rb index ae7bfab44ba..e1fe79488e0 100644 --- a/app/models/user_custom_fields/scopes/visible.rb +++ b/app/models/user_custom_fields/scopes/visible.rb @@ -34,7 +34,7 @@ module UserCustomFields::Scopes class_methods do def visible(user = User.current) - if user.admin? + if user.active_admin? all else where(admin_only: false) diff --git a/app/models/user_password.rb b/app/models/user_password.rb index 35f027946d9..4b8fd2290cf 100644 --- a/app/models/user_password.rb +++ b/app/models/user_password.rb @@ -103,10 +103,10 @@ class UserPassword < ApplicationRecord # Require the implementation to provide a secure comparison def hash_matches?(_plain) - raise NotImplementedError, "Must be overridden by subclass" + raise SubclassResponsibilityError end def derive_password!(_input) - raise NotImplementedError, "Must be overridden by subclass" + raise SubclassResponsibilityError end end diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index ecedec0b147..2bf64584442 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -173,18 +173,51 @@ class UserPreference < ApplicationRecord super.presence || { enabled: true, times: ["08:00:00+00:00"] }.with_indifferent_access end + def daily_reminders=(value) + hash = value.to_h.with_indifferent_access + self.settings = settings.merge( + "daily_reminders" => { + "enabled" => ActiveRecord::Type::Boolean.new.cast(hash[:enabled]), + "times" => Array(hash[:times]).compact_blank + } + ) + end + def workdays super || WORKDAYS_FROM_MONDAY_TO_FRIDAY end + def workdays=(value) + self.settings = settings.merge("workdays" => Array(value).map(&:to_i)) + end + def immediate_reminders super.presence || { mentioned: true, personal_reminder: true }.with_indifferent_access end + def immediate_reminders=(value) + self.settings = settings.merge( + "immediate_reminders" => value.to_h.with_indifferent_access.transform_values { |v| ActiveRecord::Type::Boolean.new.cast(v) } + ) + end + + def mentioned + immediate_reminders[:mentioned] + end + + def personal_reminder + immediate_reminders[:personal_reminder] + end + def pause_reminders super.presence || { enabled: false }.with_indifferent_access end + def pause_reminders=(value) + hash = value.to_h.with_indifferent_access + self.settings = settings.merge("pause_reminders" => pause_reminders_hash(hash)) + end + def dismissed_banner?(feature) dismissed_enterprise_banners.key?(feature.to_s) end @@ -206,4 +239,21 @@ class UserPreference < ApplicationRecord def attribute?(name) %i[user user_id].include?(name.to_sym) end + + def pause_reminders_hash(hash) + result = { "enabled" => ActiveRecord::Type::Boolean.new.cast(hash[:enabled]) } + date_fields = if hash[:date_range].present? + parsed_date_range(hash[:date_range]) + else + { "first_day" => hash[:first_day].presence, "last_day" => hash[:last_day].presence } + end + result.merge(date_fields).compact + end + + def parsed_date_range(date_range) + return {} if date_range.blank? + + first_day, last_day = date_range.split(" - ", 2) + { "first_day" => first_day.presence, "last_day" => last_day.presence } + end end diff --git a/app/models/users/function_user.rb b/app/models/users/function_user.rb index f1bbf2f8d3a..95b1c04c04f 100644 --- a/app/models/users/function_user.rb +++ b/app/models/users/function_user.rb @@ -45,7 +45,7 @@ module Users::FunctionUser def builtin? = true - def name(*_args); raise NotImplementedError end + def name(*_args) = raise SubclassResponsibilityError def mail = nil diff --git a/app/models/work_package.rb b/app/models/work_package.rb index 93aa490b535..b80a5ef9427 100644 --- a/app/models/work_package.rb +++ b/app/models/work_package.rb @@ -29,6 +29,7 @@ #++ class WorkPackage < ApplicationRecord + include WorkPackage::SemanticIdentifier include WorkPackage::Validations include WorkPackage::SchedulingRules include WorkPackage::StatusTransitions diff --git a/app/models/work_package/semantic_identifier.rb b/app/models/work_package/semantic_identifier.rb new file mode 100644 index 00000000000..d3ad6fe93b1 --- /dev/null +++ b/app/models/work_package/semantic_identifier.rb @@ -0,0 +1,106 @@ +# 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 WorkPackage::SemanticIdentifier + extend ActiveSupport::Concern + + included do + has_many :semantic_aliases, + class_name: "WorkPackageSemanticAlias", + foreign_key: :work_package_id, + inverse_of: :work_package, + dependent: :delete_all + + after_create :allocate_and_register_semantic_id, if: -> { Setting::WorkPackageIdentifier.semantic? } + end + + class_methods do + def semantic_id?(identifier) + identifier.to_s.to_i.to_s != identifier.to_s + end + + # Resolves any identifier form to a WorkPackage. + # - Numeric string ("12345") → find by primary key + # - Semantic string ("PROJ-42") → lookup via work_packages table and alias table + # + # Returns nil on miss. + def find_by_id_or_identifier(identifier) + return find_by(id: identifier) unless semantic_id?(identifier) + + find_by_semantic_identifier(identifier) + end + + # Same as find_by_id_or_identifier but raises ActiveRecord::RecordNotFound on miss. + def find_by_id_or_identifier!(identifier) + find_by_id_or_identifier(identifier) || raise(ActiveRecord::RecordNotFound, "WorkPackage not found: #{identifier}") + end + + private + + def find_by_semantic_identifier(identifier) + wp = find_by(identifier:) + return wp if wp + + # Fallback: alias table lookup. The table holds every identifier a WP has ever been known by: + # Done via a single join to: + # * Respect any parent scoping (e.g. when called as WorkPackage.visible.find_by_semantic_identifier) + # * Reduce lookup to a single DB round trip + joins(:semantic_aliases).find_by(work_package_semantic_aliases: { identifier: }) + end + end + + # Allocates the next semantic identifier in the current project and assigns it to the WP. + # Also writes alias rows for every identifier the project has ever used (including "ghost" aliases). + # + # This should generally be run following project_id-mutating operations on WorkPackage records (like create or move). + def allocate_and_register_semantic_id + WorkPackageSemanticAlias.transaction do + sequence_number, identifier = project.allocate_wp_semantic_identifier! + # Re-map the semantic identifier to the new project + update_columns(sequence_number:, identifier:) + # Insert current, historical + ghost aliases for the new project + # Note: In case of WP move, the previous mapping for the old project is assumed + # to be present in the alias table already, ever since its prior create/move operation. + semantic_aliases.insert_all(alias_rows_for_sequence_number(sequence_number), + unique_by: :identifier) + end + end + + private + + # Builds alias rows for every identifier this project has ever used at the given sequence (including the current one). + # This also includes "ghost identifiers" -- i.e. those that weren't ever actually generated, but should work + # as a historical alias (e.g. OLDPROJ-42 should work even if WP #42 was created after rename to NEWPROJ) + def alias_rows_for_sequence_number(seq) + project.slugs + .pluck(:slug) + .map { |prefix| { identifier: "#{prefix}-#{seq}", work_package_id: id } } + end +end diff --git a/app/models/work_package_semantic_alias.rb b/app/models/work_package_semantic_alias.rb new file mode 100644 index 00000000000..e85e56cbad2 --- /dev/null +++ b/app/models/work_package_semantic_alias.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. +#++ + +# Maps a semantic identifier (e.g. "PROJ-42") to a work package. +# This acts as a registry of all semantic identifiers for a work package, +# including both the current identifier and any retired ones created by moves +# or project renames. The current identifier is also stored directly on +# work_packages.identifier for faster access. +# +# The write side of the registry lives in WorkPackage::SemanticIdentifier: +# wp.allocate_and_register_semantic_id # on WP project change (call post-save) +# project.handle_semantic_rename(old_identifier) # on project identifier change +class WorkPackageSemanticAlias < ApplicationRecord + belongs_to :work_package, inverse_of: :semantic_aliases + + validates :identifier, presence: true, uniqueness: true + validates :work_package, presence: true +end diff --git a/app/policies/scm/authorization_policy.rb b/app/policies/scm/authorization_policy.rb index 132aa7cddff..4e67e48b74d 100644 --- a/app/policies/scm/authorization_policy.rb +++ b/app/policies/scm/authorization_policy.rb @@ -51,7 +51,7 @@ class SCM::AuthorizationPolicy # Determines whether the given request is a read access # Must be implemented by descendents of this policy. def readonly_request?(_params) - raise NotImplementedError + raise SubclassResponsibilityError end ## diff --git a/app/seeders/basic_data/model_seeder.rb b/app/seeders/basic_data/model_seeder.rb index 5aece770aee..f2f032a0d0e 100644 --- a/app/seeders/basic_data/model_seeder.rb +++ b/app/seeders/basic_data/model_seeder.rb @@ -68,7 +68,7 @@ module BasicData end def model_attributes(model_data) - raise NotImplementedError + raise SubclassResponsibilityError end def applicable? diff --git a/app/seeders/basic_data_seeder.rb b/app/seeders/basic_data_seeder.rb index 733566e3d19..c2919e13860 100644 --- a/app/seeders/basic_data_seeder.rb +++ b/app/seeders/basic_data_seeder.rb @@ -29,7 +29,7 @@ #++ class BasicDataSeeder < CompositeSeeder def data_seeder_classes - raise NotImplementedError + raise SubclassResponsibilityError end def namespace diff --git a/app/seeders/composite_seeder.rb b/app/seeders/composite_seeder.rb index 80fb3625140..3c2ac929f30 100644 --- a/app/seeders/composite_seeder.rb +++ b/app/seeders/composite_seeder.rb @@ -50,7 +50,7 @@ class CompositeSeeder < Seeder end def data_seeder_classes - raise NotImplementedError, "has to be implemented by subclasses" + raise SubclassResponsibilityError end def discovered_seeders @@ -71,7 +71,7 @@ class CompositeSeeder < Seeder end def namespace - raise NotImplementedError, "has to be implemented by subclasses" + raise SubclassResponsibilityError end ## diff --git a/app/seeders/seeder.rb b/app/seeders/seeder.rb index d858dd84432..52174768f9b 100644 --- a/app/seeders/seeder.rb +++ b/app/seeders/seeder.rb @@ -68,7 +68,7 @@ class Seeder end def seed_data! - raise NotImplementedError + raise SubclassResponsibilityError end def applicable? diff --git a/app/services/api/parse_resource_params_service.rb b/app/services/api/parse_resource_params_service.rb index 23a863e82d6..fbfcf90ce99 100644 --- a/app/services/api/parse_resource_params_service.rb +++ b/app/services/api/parse_resource_params_service.rb @@ -60,7 +60,7 @@ module API private def deduce_representer(_model) - raise NotImplementedError + raise SubclassResponsibilityError end def parsing_representer diff --git a/app/services/authorization/user_permissible_service.rb b/app/services/authorization/user_permissible_service.rb index 2099903aef3..2579bdb82a0 100644 --- a/app/services/authorization/user_permissible_service.rb +++ b/app/services/authorization/user_permissible_service.rb @@ -39,37 +39,38 @@ module Authorization def allowed_globally?(permission) perms = contextual_permissions(permission, :global) return false unless authorizable_user? + return true if admin_and_all_granted_to_admin?(perms) cached_permissions(nil).intersect?(perms.map(&:name)) end def allowed_in_project?(permission, projects_to_check) - perms = contextual_permissions(permission, :project) + permissions = contextual_permissions(permission, :project) return false if projects_to_check.blank? return false unless authorizable_user? Array(projects_to_check).all? do |project| - allowed_in_single_project?(perms, project) + allowed_in_single_project?(permissions, project) end end def allowed_in_any_project?(permission) - perms = contextual_permissions(permission, :project) + permissions = contextual_permissions(permission, :project) return false unless authorizable_user? - cached_in_any_project?(perms) + cached_in_any_project?(permissions) end def allowed_in_entity?(permission, entities_to_check, entity_class) return false if entities_to_check.blank? return false unless authorizable_user? - perms = contextual_permissions(permission, context_name(entity_class)) + permissions = contextual_permissions(permission, context_name(entity_class)) entities = Array(entities_to_check) entities.all? do |entity| - allowed_in_single_entity?(perms, entity, entity_class) + allowed_in_single_entity?(permissions, entity, entity_class) end end @@ -110,6 +111,7 @@ module Authorization permissions_filtered_for_project = permissions_by_enabled_project_modules(project, permissions) return false if permissions_filtered_for_project.empty? + return true if admin_and_all_granted_to_admin?(permissions) cached_permissions(project).intersect?(permissions_filtered_for_project) end @@ -164,7 +166,7 @@ module Authorization end def admin_and_all_granted_to_admin?(permissions) - user.admin? && permissions.all?(&:grant_to_admin?) + user.active_admin? && permissions.all?(&:grant_to_admin?) end def authorizable_user? diff --git a/app/services/base_services/base_callable.rb b/app/services/base_services/base_callable.rb index 7b746f3f0cd..b5951d18025 100644 --- a/app/services/base_services/base_callable.rb +++ b/app/services/base_services/base_callable.rb @@ -59,7 +59,7 @@ module BaseServices attr_accessor :params def perform(*) - raise NotImplementedError + raise SubclassResponsibilityError end private diff --git a/app/services/base_services/base_contracted.rb b/app/services/base_services/base_contracted.rb index 8f028b15eb4..0cf0c93bde6 100644 --- a/app/services/base_services/base_contracted.rb +++ b/app/services/base_services/base_contracted.rb @@ -117,7 +117,7 @@ module BaseServices end def default_contract_class - raise NotImplementedError + raise SubclassResponsibilityError end def namespace diff --git a/app/services/base_services/write.rb b/app/services/base_services/write.rb index 45edaf4705e..348a105f108 100644 --- a/app/services/base_services/write.rb +++ b/app/services/base_services/write.rb @@ -67,11 +67,11 @@ module BaseServices end def instance(_params) - raise NotImplementedError + raise SubclassResponsibilityError end def default_contract_class - raise NotImplementedError + raise SubclassResponsibilityError end def instance_class diff --git a/app/services/bulk_services/project_mappings/base_create_service.rb b/app/services/bulk_services/project_mappings/base_create_service.rb index fea2c8bbd43..644b56a1409 100644 --- a/app/services/bulk_services/project_mappings/base_create_service.rb +++ b/app/services/bulk_services/project_mappings/base_create_service.rb @@ -107,12 +107,12 @@ module BulkServices # @return [Symbol] the permission required to create the mapping def permission - raise NotImplementedError + raise SubclassResponsibilityError end # @return [Symbol] the column name of the mapping def model_foreign_key_id - raise NotImplementedError + raise SubclassResponsibilityError end def attributes_service_class diff --git a/app/services/bulk_services/project_mappings/mapping_context_base.rb b/app/services/bulk_services/project_mappings/mapping_context_base.rb index 9a27261df03..d2bc8df05f8 100644 --- a/app/services/bulk_services/project_mappings/mapping_context_base.rb +++ b/app/services/bulk_services/project_mappings/mapping_context_base.rb @@ -39,11 +39,11 @@ module BulkServices end def mapping_attributes_for_all_projects(params) - raise NotImplementedError, "This method must be implemented in a subclass" + raise SubclassResponsibilityError end def incoming_projects - raise NotImplementedError, "This method must be implemented in a subclass" + raise SubclassResponsibilityError end end end diff --git a/app/services/copy/dependency.rb b/app/services/copy/dependency.rb index 2f90ca1fd20..f52a9bdf4f8 100644 --- a/app/services/copy/dependency.rb +++ b/app/services/copy/dependency.rb @@ -96,7 +96,7 @@ module Copy end def copy_dependency(params:) - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/app/services/custom_fields/hierarchy/hierarchical_item_service.rb b/app/services/custom_fields/hierarchy/hierarchical_item_service.rb index 2d0cb23593b..98fec862060 100644 --- a/app/services/custom_fields/hierarchy/hierarchical_item_service.rb +++ b/app/services/custom_fields/hierarchy/hierarchical_item_service.rb @@ -159,9 +159,9 @@ module CustomFields Success() end + # Soft delete the item and children def soft_delete_item(item:) - # Soft delete the item and children - raise NotImplementedError + raise SubclassResponsibilityError end # Returns a hash of Item => { Item => [Item] } diff --git a/app/services/groups/concerns/membership_manipulation.rb b/app/services/groups/concerns/membership_manipulation.rb index dc074a1110d..2d24f38fb2f 100644 --- a/app/services/groups/concerns/membership_manipulation.rb +++ b/app/services/groups/concerns/membership_manipulation.rb @@ -65,7 +65,7 @@ module Groups::Concerns end def modify_members_and_roles(_params) - raise NotImplementedError + raise SubclassResponsibilityError end def execute_query(query) diff --git a/app/services/incoming_emails/handlers/base.rb b/app/services/incoming_emails/handlers/base.rb index a580f2502f9..bcb709775f1 100644 --- a/app/services/incoming_emails/handlers/base.rb +++ b/app/services/incoming_emails/handlers/base.rb @@ -41,12 +41,12 @@ module IncomingEmails::Handlers # Override in subclasses to determine if this handler can process the email def self.handles?(email, reference:, automated_email:) - raise NotImplementedError, "Subclasses must implement can_handle? method" + raise SubclassResponsibilityError, "Subclasses must implement handles? method" end # Override in subclasses to process the email def process - raise NotImplementedError, "Subclasses must implement handle method" + raise SubclassResponsibilityError, "Subclasses must implement process method" end def cleaned_up_text_body diff --git a/app/services/ldap/base_service.rb b/app/services/ldap/base_service.rb index d6e81cb8052..51d7e059b9f 100644 --- a/app/services/ldap/base_service.rb +++ b/app/services/ldap/base_service.rb @@ -45,7 +45,7 @@ module Ldap end def perform - raise NotImplementedError + raise SubclassResponsibilityError end protected diff --git a/app/services/members/concerns/notification_sender.rb b/app/services/members/concerns/notification_sender.rb index 8255d6720e9..91b1acc226f 100644 --- a/app/services/members/concerns/notification_sender.rb +++ b/app/services/members/concerns/notification_sender.rb @@ -55,7 +55,7 @@ module Members::Concerns::NotificationSender end def event_type - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/app/services/projects/update_service.rb b/app/services/projects/update_service.rb index 2c0b4678cff..d4cbacef8ed 100644 --- a/app/services/projects/update_service.rb +++ b/app/services/projects/update_service.rb @@ -57,6 +57,7 @@ module Projects ret = super touch_on_custom_values_update + update_semantic_ids_on_identifier_change if Setting::WorkPackageIdentifier.semantic? notify_on_identifier_renamed send_update_notification update_wp_versions_on_parent_change @@ -69,6 +70,13 @@ module Projects model.touch if only_custom_values_updated? end + def update_semantic_ids_on_identifier_change + return unless memoized_changes["identifier"] + + old_identifier = memoized_changes["identifier"].first + model.handle_semantic_rename(old_identifier) + end + def notify_on_identifier_renamed return unless memoized_changes["identifier"] diff --git a/app/services/reports/report.rb b/app/services/reports/report.rb index 5cbc37b9153..a78bb3b4f27 100644 --- a/app/services/reports/report.rb +++ b/app/services/reports/report.rb @@ -47,18 +47,18 @@ class Reports::Report # ---- every report needs to implement these methods to supply all needed data for a report ----- def field - raise NotImplementedError + raise SubclassResponsibilityError end def rows - raise NotImplementedError + raise SubclassResponsibilityError end def data - raise NotImplementedError + raise SubclassResponsibilityError end def title - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/services/work_packages/bulk/bulked_service.rb b/app/services/work_packages/bulk/bulked_service.rb index 06f6eae2772..0c6b29080c1 100644 --- a/app/services/work_packages/bulk/bulked_service.rb +++ b/app/services/work_packages/bulk/bulked_service.rb @@ -73,11 +73,11 @@ module WorkPackages end def alter_work_package(_work_package, _params) - raise NotImplementedError + raise SubclassResponsibilityError end def call_move_hook(_work_package, _params) - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/app/services/work_packages/identifier_autofix/preview_query.rb b/app/services/work_packages/identifier_autofix/preview_query.rb index 0c3c54ea341..36cbfeda612 100644 --- a/app/services/work_packages/identifier_autofix/preview_query.rb +++ b/app/services/work_packages/identifier_autofix/preview_query.rb @@ -35,54 +35,34 @@ module WorkPackages DISPLAY_COUNT = 5 def call - total = problematic_scope.count - preview = problematic_scope - .select(:id, :name, :identifier) - .limit(DISPLAY_COUNT) - .to_a + analysis = ProblematicIdentifiers.new + total_count = analysis.count + projects_data = build_projects_data(analysis) - suggestions = WorkPackages::IdentifierAutofix::ProjectIdentifierSuggestionGenerator.call( - preview, - exclude: reserved_identifiers | in_use_identifiers - ) - - projects_data = suggestions.map do |entry| - entry.merge(error_reason: error_reason(entry[:current_identifier])) - end - - Result.new(projects_data:, total_count: total) + Result.new(projects_data:, total_count:) end private - def problematic_scope - @problematic_scope ||= Project.where( - "length(identifier) > ? OR identifier ~ ?", - ProjectIdentifierSuggestionGenerator::IDENTIFIER_LENGTH[:max], - "[^a-zA-Z0-9_]" - ) - end - - def error_reason(identifier) - if identifier.length > ProjectIdentifierSuggestionGenerator::IDENTIFIER_LENGTH[:max] - :too_long - elsif identifier.match?(/[^a-zA-Z0-9_]/) - :special_characters - elsif in_use_identifiers.include?(identifier) - :in_use - elsif reserved_identifiers.include?(identifier) - :reserved + def build_projects_data(analysis) + generate_suggestions(analysis).map do |entry| + entry.merge(error_reason: analysis.error_reason(entry[:current_identifier])) end end - def in_use_identifiers - @in_use_identifiers ||= Project.where.not(id: problematic_scope.select(:id)).pluck(:identifier).to_set + def generate_suggestions(analysis) + ProjectIdentifierSuggestionGenerator.call( + preview_projects(analysis.scope), + exclude: analysis.exclusion_set.to_set(&:upcase) + ) end - def reserved_identifiers - # TODO: OldProjectIdentifier.pluck(:identifier).to_set - # once the OldProjectIdentifier model and migration are added. - Set.new + def preview_projects(scope) + scope + .select(:id, :name, :identifier) + .order(:id) + .limit(DISPLAY_COUNT) + .to_a end end end diff --git a/app/services/work_packages/identifier_autofix/problematic_identifiers.rb b/app/services/work_packages/identifier_autofix/problematic_identifiers.rb new file mode 100644 index 00000000000..71d561d7622 --- /dev/null +++ b/app/services/work_packages/identifier_autofix/problematic_identifiers.rb @@ -0,0 +1,118 @@ +# 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 WorkPackages + module IdentifierAutofix + # Identifies projects whose identifiers violate the semantic identifier format + # and provides classification and exclusion sets for suggestion generation. + # + # For main use by admin UI preview and batch migration job. + # + # == Performance notes + # + # * +#exclusion_set+ loads all non-problematic identifiers and historical slugs + # into memory. Fine for a one-off admin migration; if this ever becomes a hot + # path, consider a DB-backed exclusion check instead. + # + # * The regex scope conditions (+identifier ~ ?+) and +UPPER(identifier)+ won't + # hit a regular index. If queries get slow on large tables, a functional index + # on +UPPER(identifier)+ or a +pg_trgm+ GIN index would help. + # + # + class ProblematicIdentifiers + # Priority-ordered format rules for identifier classification. + FORMAT_RULES = [ + [:too_long, ->(id, max) { id.length > max }], + [:numerical, ->(id, _) { id.match?(/\A\d+\z/) }], + [:starts_with_number, ->(id, _) { id.match?(/\A\d/) }], + [:special_characters, ->(id, _) { id.match?(/[^a-zA-Z0-9_]/) }], + [:not_fully_uppercased, ->(id, _) { id != id.upcase }] + ].freeze + + def scope + @scope ||= exceeds_max_length + .or(contains_non_alphanumeric) + .or(starts_with_digit) + .or(not_fully_uppercased) + end + + delegate :count, to: :scope + + # Returns a symbol classifying why the identifier is problematic. + # Must handle all identifiers matched by #scope. + def error_reason(identifier) + format_error_reason(identifier) || collision_error_reason(identifier) || :unknown + end + + # Returns a Set of identifiers that must not be suggested for new assignments. + # Combines currently active identifiers from non-problematic projects with + # historically reserved identifiers from FriendlyId slug history. + def exclusion_set + reserved_identifiers | in_use_identifiers + end + + private + + def exceeds_max_length = Project.where("length(identifier) > ?", max_identifier_length) + def contains_non_alphanumeric = Project.where("identifier ~ ?", "[^a-zA-Z0-9_]") + def starts_with_digit = Project.where("identifier ~ ?", "^[0-9]") + def not_fully_uppercased = Project.where("identifier != UPPER(identifier)") + + def max_identifier_length = ProjectIdentifierSuggestionGenerator::IDENTIFIER_LENGTH[:max] + + def format_error_reason(identifier) + FORMAT_RULES.each do |reason, check| + return reason if check.call(identifier, max_identifier_length) + end + nil + end + + def collision_error_reason(identifier) + if in_use_identifiers.include?(identifier) + :in_use + elsif reserved_identifiers.include?(identifier) + :reserved + end + end + + def in_use_identifiers + @in_use_identifiers ||= Project.where.not(id: scope.select(:id)).pluck(:identifier).to_set + end + + def reserved_identifiers + @reserved_identifiers ||= FriendlyId::Slug + .where(sluggable_type: Project.name) + .where("LOWER(slug) NOT IN (SELECT LOWER(identifier) FROM projects)") + .pluck(:slug) + .to_set + end + end + end +end diff --git a/app/services/work_packages/update_service.rb b/app/services/work_packages/update_service.rb index 89c4aa2da5d..833c2744447 100644 --- a/app/services/work_packages/update_service.rb +++ b/app/services/work_packages/update_service.rb @@ -102,12 +102,17 @@ class WorkPackages::UpdateService < BaseServices::Update delete_relations(moved_work_packages) move_time_entries(moved_work_packages, work_package.project_id) move_work_package_memberships(moved_work_packages, work_package.project_id) + update_semantic_ids(moved_work_packages) if Setting::WorkPackageIdentifier.semantic? end if work_package.saved_change_to_type_id? reset_custom_values(work_package) end end + def update_semantic_ids(work_packages) + work_packages.each(&:allocate_and_register_semantic_id) + end + def delete_relations(work_packages) unless Setting.cross_project_work_package_relations? Relation diff --git a/app/views/announcement_mailer/announce.html.erb b/app/views/announcement_mailer/announce.html.erb index a3fabee74f6..bdfdb3a701a 100644 --- a/app/views/announcement_mailer/announce.html.erb +++ b/app/views/announcement_mailer/announce.html.erb @@ -46,7 +46,7 @@ <%= render partial: "mailer/notification_settings_table", locals: { - button_url: my_reminders_url, + button_url: my_notifications_url(tab: "reminders"), button_text: I18n.t(:"mail.notification.settings") } %> <% end %> diff --git a/app/views/digest_mailer/work_packages.html.erb b/app/views/digest_mailer/work_packages.html.erb index 4c981c96b7c..36e9bcbd94c 100644 --- a/app/views/digest_mailer/work_packages.html.erb +++ b/app/views/digest_mailer/work_packages.html.erb @@ -42,7 +42,7 @@ <%= render layout: "mailer/notification_settings_table", locals: { - button_url: my_reminders_url, + button_url: my_notifications_url(tab: "reminders"), button_text: I18n.t(:"mail.notification.settings") } do %> <% if @aggregated_notifications.length > DigestMailer::MAX_SHOWN_WORK_PACKAGES %> diff --git a/app/views/my/interface.html.erb b/app/views/my/interface.html.erb index 3bc01fb9f44..dddb9c37ec4 100644 --- a/app/views/my/interface.html.erb +++ b/app/views/my/interface.html.erb @@ -41,11 +41,6 @@ See COPYRIGHT and LICENSE files for more details. <%= error_messages_for "user" %> -<%= - render(Primer::Beta::Subhead.new) do |component| - component.with_heading(tag: :h3, size: :medium) { I18n.t("activerecord.attributes.user_preference.header_look_and_feel") } - end -%> <%= settings_primer_form_with( model: @user.pref, @@ -58,11 +53,6 @@ See COPYRIGHT and LICENSE files for more details. end %> -<%= - render(Primer::Beta::Subhead.new(mt: 3)) do |component| - component.with_heading(tag: :h3, size: :medium) { I18n.t("activerecord.attributes.user_preference.header_alerts") } - end -%> <%= settings_primer_form_with(model: @user.pref, scope: :pref, url: { action: "update_settings" }, data: { turbo: false }) do |form| render(My::AlertsForm.new(form)) diff --git a/app/views/my/notifications.html.erb b/app/views/my/notifications.html.erb index 641c1ba1ef4..90aa8342e6e 100644 --- a/app/views/my/notifications.html.erb +++ b/app/views/my/notifications.html.erb @@ -27,16 +27,31 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<% html_title(t(:label_my_account), I18n.t("js.notifications.settings.title")) -%> +<% html_title(t(:label_my_account), t("my_account.notifications_and_email.title")) -%> -<%= - render(Primer::OpenProject::PageHeader.new) do |header| - header.with_title { I18n.t("js.notifications.settings.title") } - header.with_breadcrumbs( - [{ href: my_account_path, text: t(:label_my_account) }, - I18n.t("js.notifications.settings.title")] - ) - end -%> +<%= render(My::Notifications::ShowPageHeaderComponent.new) %> -<%= angular_component_tag "opce-notification-settings" %> +<% if params[:tab] == "reminders" %> + <%= render( + My::Reminders::ShowPageComponent.new( + user: @user, + global_notification_setting: @global_notification_setting, + update_url: { action: "update_settings" }, + update_workdays_url: { action: "update_workdays" }, + update_email_alerts_url: { action: "update_email_alerts" } + ) + ) %> +<% else %> + <%= render( + My::Notifications::ShowPageComponent.new( + user: @user, + global_notification_setting: @global_notification_setting, + update_participating_url: { action: "update_participating" }, + update_non_participating_url: { action: "update_non_participating" }, + update_date_alerts_url: { action: "update_date_alerts" }, + new_project_settings_url: new_my_project_settings_path, + edit_project_settings_url: ->(project_id) { edit_my_project_settings_path(project_id:) }, + project_setting_url: ->(project_id) { my_project_setting_path(project_id:) } + ) + ) %> +<% end %> diff --git a/app/views/reminders/notification_mailer/reminder_notification.html.erb b/app/views/reminders/notification_mailer/reminder_notification.html.erb index f6967d68d87..4d9fe588831 100644 --- a/app/views/reminders/notification_mailer/reminder_notification.html.erb +++ b/app/views/reminders/notification_mailer/reminder_notification.html.erb @@ -38,7 +38,7 @@ <%= render layout: "mailer/notification_settings_table", locals: { - button_url: my_reminders_url, + button_url: my_notifications_url(tab: "reminders"), button_text: I18n.t(:"mail.notification.settings") } do %> <% end %> diff --git a/app/views/sharing_mailer/shared_work_package.html.erb b/app/views/sharing_mailer/shared_work_package.html.erb index 2f15ffa749e..b80954e2ba0 100644 --- a/app/views/sharing_mailer/shared_work_package.html.erb +++ b/app/views/sharing_mailer/shared_work_package.html.erb @@ -49,7 +49,7 @@ <%= render partial: "mailer/notification_settings_table", locals: { - button_url: my_reminders_url, + button_url: my_notifications_url(tab: "reminders"), button_text: I18n.t(:"mail.notification.settings") } %> diff --git a/app/views/users/_notifications.html.erb b/app/views/users/_notifications.html.erb index 24c7dde5162..b13ecbe6818 100644 --- a/app/views/users/_notifications.html.erb +++ b/app/views/users/_notifications.html.erb @@ -26,4 +26,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See COPYRIGHT and LICENSE files for more details. ++#%> -<%= angular_component_tag "opce-notification-settings", inputs: { userId: @user.id } %> +<%= render( + My::Notifications::ShowPageComponent.new( + user: @user, + global_notification_setting: @user.notification_settings.find_or_initialize_by(project: nil), + update_participating_url: update_participating_user_path(@user), + update_non_participating_url: update_non_participating_user_path(@user), + update_date_alerts_url: update_date_alerts_user_path(@user), + new_project_settings_url: new_project_settings_user_path(@user), + edit_project_settings_url: ->(project_id) { edit_project_settings_user_path(@user, project_id:) }, + project_setting_url: ->(project_id) { project_setting_user_path(@user, project_id:) } + ) + ) %> diff --git a/app/views/users/_reminders.html.erb b/app/views/users/_reminders.html.erb index bb5d1fb15d0..b3bfad50477 100644 --- a/app/views/users/_reminders.html.erb +++ b/app/views/users/_reminders.html.erb @@ -26,4 +26,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See COPYRIGHT and LICENSE files for more details. ++#%> -<%= angular_component_tag "opce-reminder-settings", inputs: { userId: @user.id } %> +<%= render( + My::Reminders::ShowPageComponent.new( + user: @user, + global_notification_setting: @user.notification_settings.find_or_initialize_by(project: nil), + update_url: update_reminders_user_path(@user), + update_workdays_url: update_workdays_user_path(@user), + update_email_alerts_url: update_email_alerts_user_path(@user) + ) + ) %> diff --git a/app/views/work_package_mailer/mentioned.html.erb b/app/views/work_package_mailer/mentioned.html.erb index 9ebcfeded0f..69831d69f34 100644 --- a/app/views/work_package_mailer/mentioned.html.erb +++ b/app/views/work_package_mailer/mentioned.html.erb @@ -34,7 +34,7 @@ <%= render partial: "mailer/notification_settings_table", locals: { - button_url: my_reminders_url, + button_url: my_notifications_url(tab: "reminders"), button_text: I18n.t(:"mail.notification.settings") } %> diff --git a/app/workers/exports/export_job.rb b/app/workers/exports/export_job.rb index 4fad5e98617..14af27ffd7a 100644 --- a/app/workers/exports/export_job.rb +++ b/app/workers/exports/export_job.rb @@ -66,7 +66,7 @@ module Exports attr_accessor :export, :current_user, :mime_type, :query, :options def prepare! - raise NotImplementedError + raise SubclassResponsibilityError end def list_export? diff --git a/app/workers/import/jira_import_projects_job.rb b/app/workers/import/jira_import_projects_job.rb index e391216981a..aea7b64f492 100644 --- a/app/workers/import/jira_import_projects_job.rb +++ b/app/workers/import/jira_import_projects_job.rb @@ -68,7 +68,8 @@ 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 + project_key = jira_project.payload.fetch("key") + identifier = Setting::WorkPackageIdentifier.semantic? ? project_key.upcase : project_key.downcase service_call = Projects::CreateService .new(user:, contract_class: EmptyContract) .call( diff --git a/app/workers/mails/deliver_job.rb b/app/workers/mails/deliver_job.rb index 4cff5c80bb4..0aefee241ee 100644 --- a/app/workers/mails/deliver_job.rb +++ b/app/workers/mails/deliver_job.rb @@ -57,12 +57,12 @@ class Mails::DeliverJob < ApplicationJob # To be implemented by subclasses. # Returns a Mail::Message, or nil if no message should be sent. def render_mail - raise NotImplementedError, "SubclassResponsibility" + raise SubclassResponsibilityError end def build_mail render_mail - rescue NotImplementedError + rescue SubclassResponsibilityError # Notify subclass of the need to implement raise rescue StandardError => e diff --git a/app/workers/mails/member_job.rb b/app/workers/mails/member_job.rb index f0b861ef773..e86ed27a4f1 100644 --- a/app/workers/mails/member_job.rb +++ b/app/workers/mails/member_job.rb @@ -69,11 +69,11 @@ class Mails::MemberJob < ApplicationJob end def send_for_group_user(_current_user, _member, _group, _message) - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def send_for_project_user(_current_user, _member, _message) - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def send_updated_global(current_user, member, member_message) diff --git a/app/workers/mails/watcher_job.rb b/app/workers/mails/watcher_job.rb index 8f1ecc64070..494360b9db9 100644 --- a/app/workers/mails/watcher_job.rb +++ b/app/workers/mails/watcher_job.rb @@ -73,6 +73,6 @@ class Mails::WatcherJob < Mails::DeliverJob end def action - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end end diff --git a/app/workers/work_packages/bulk_job.rb b/app/workers/work_packages/bulk_job.rb index de61a594fad..63330701ba0 100644 --- a/app/workers/work_packages/bulk_job.rb +++ b/app/workers/work_packages/bulk_job.rb @@ -72,7 +72,7 @@ module WorkPackages end def service_class - raise NotImplementedError + raise SubclassResponsibilityError end def successful_status_update(call) diff --git a/config/constants/settings/definition.rb b/config/constants/settings/definition.rb index 2d6d5375dcc..28b96933759 100644 --- a/config/constants/settings/definition.rb +++ b/config/constants/settings/definition.rb @@ -1323,12 +1323,12 @@ module Settings }, work_packages_identifier: { description: "Defines how work packages are identified in the UI (e.g. in links and titles). " \ - "The 'numeric' option uses the work package numerical ID, " \ - "while 'alphanumeric' uses the project identifier and the work package ID separated by a dash " \ + "The 'classic' option uses the work package numerical ID, " \ + "while 'semantic' uses the project identifier and the work package ID separated by a dash " \ "(e.g. 'PROJA-123').", format: :string, allowed: -> { Setting::WorkPackageIdentifier::ALLOWED_VALUES }, - default: "numeric" + default: "classic" }, work_package_list_default_highlighted_attributes: { default: ["status", "priority", "due_date"], @@ -1428,6 +1428,10 @@ module Settings end def value + unless (override = resolve_value_override).nil? + return cast(override) + end + cast(@value) end @@ -1444,6 +1448,8 @@ module Settings end def writable? + return false if value_override? + if writable.respond_to?(:call) writable.call else @@ -1567,6 +1573,38 @@ module Settings @all ||= {} end + # Registers a value override block for a setting. The block is called + # whenever the setting's value or writability is evaluated. + # + # If the block returns a non-nil value, that value is used as the setting's + # value and the setting becomes non-writable. If the block returns nil, + # no override is applied. + # + # To override a setting with nil, return a callable: +-> { nil }+ + # + # @param name [Symbol] The setting name to override. + # @yield A block that returns the override value, or nil to skip. + # + # @example Force a setting to true when a condition is met + # Settings::Definition.add_value_override(:capture_external_links) do + # true if MyPlugin.active? + # end + def add_value_override(name, &block) + (value_overrides[name.to_sym] ||= []) << block + end + + def value_overrides + @value_overrides ||= {} + end + + def clear_value_overrides(name = nil) + if name + value_overrides.delete(name.to_sym) + else + @value_overrides = {} + end + end + private def file_config @@ -1760,6 +1798,18 @@ module Settings attr_accessor :serialized, :writable + def value_override? + !resolve_value_override.nil? + end + + def resolve_value_override + self.class.value_overrides[name.to_sym]&.each do |block| + result = block.call + return result unless result.nil? + end + nil + end + def cast(value) return nil if value.nil? diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb index 733be24b1cd..6389aae189b 100644 --- a/config/initializers/menus.rb +++ b/config/initializers/menus.rb @@ -167,6 +167,7 @@ Redmine::MenuManager.map :account_menu do |menu| menu.push :logout, :signout_path, icon: :"sign-out", + show_divider_before: true, scheme: :danger, if: ->(_) { User.current.logged? }, html: { @@ -303,12 +304,8 @@ Redmine::MenuManager.map :my_menu do |menu| icon: "devices" menu.push :notifications, { controller: "/my", action: "notifications" }, - caption: I18n.t("js.notifications.settings.title"), + caption: I18n.t("my_account.notifications_and_email.title"), icon: "bell" - menu.push :reminders, - { controller: "/my", action: "reminders" }, - caption: I18n.t("js.reminders.settings.title"), - icon: "unread" end Redmine::MenuManager.map :admin_menu do |menu| @@ -629,6 +626,12 @@ Redmine::MenuManager.map :admin_menu do |menu| caption: :label_announcement, icon: "megaphone" + menu.push :admin_integrations, + { controller: "/github_integration/admin/settings", action: "show" }, + if: ->(_) { User.current.admin? }, + icon: :"git-compare", + caption: :label_integrations + menu.push :plugins, { controller: "/admin", action: "plugins" }, if: ->(_) { User.current.admin? }, diff --git a/config/initializers/permissions.rb b/config/initializers/permissions.rb index 9d7eca42f1e..d50df0fb4d6 100644 --- a/config/initializers/permissions.rb +++ b/config/initializers/permissions.rb @@ -81,7 +81,11 @@ Rails.application.reloader.to_prepare do map.permission :manage_user, { - users: %i[index show edit update change_status change_status_info], + users: %i[index show edit update change_status change_status_info + update_reminders update_email_alerts update_workdays + update_participating update_non_participating update_date_alerts + new_project_settings create_project_settings + edit_project_settings update_project_settings destroy_project_settings], "users/memberships": %i[create update destroy], admin: %i[index] }, diff --git a/config/locales/crowdin/af.yml b/config/locales/crowdin/af.yml index eb42e744c61..fb0703276c9 100644 --- a/config/locales/crowdin/af.yml +++ b/config/locales/crowdin/af.yml @@ -114,7 +114,7 @@ af: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ af: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ af: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ af: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ af: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ af: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ af: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ af: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Vertoon tot attachment: @@ -3391,6 +3398,11 @@ af: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ af: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Gedelegeerde @@ -3484,6 +3562,7 @@ af: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Rekening + label_actions: Actions label_active: Aktiewe label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ af: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Toegepaste status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ af: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Gebaseer op die gebruiker se taal label_last_activity: Last activity @@ -5030,12 +5110,12 @@ af: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index 10ecf89720c..4316923ac65 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -114,7 +114,7 @@ ar: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ar: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ar: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -411,9 +411,9 @@ ar: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -422,10 +422,14 @@ ar: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: zero: "... %{count} more projects" one: "... 1 more project" @@ -444,7 +448,7 @@ ar: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1608,6 +1612,9 @@ ar: dependencies: الاعتماديات activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1616,7 +1623,7 @@ ar: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: أظهِر حتّى attachment: @@ -3625,6 +3632,11 @@ ar: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3687,6 +3699,72 @@ ar: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: المُسند إليه @@ -3718,6 +3796,7 @@ ar: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: الحساب + label_actions: Actions label_active: مفعّل label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3758,6 +3837,7 @@ ar: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: الحالة المطبقة label_archive_project: Archive project @@ -4008,7 +4088,7 @@ ar: label_external_links: External links label_locale: Language and region label_jump_to_a_project: الانتقال بسرعة إلى مشروع... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: استناداً إلى لغة المستخدم label_last_activity: آخر نشاط @@ -5276,12 +5356,12 @@ ar: setting_welcome_text: نص كتلة الترجيب setting_welcome_title: عنوان كتلة الترحيب setting_welcome_on_homescreen: كتلة الترحيب عرض على الشاشة الرئيسية - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/az.yml b/config/locales/crowdin/az.yml index c416002675c..efb2b1ad484 100644 --- a/config/locales/crowdin/az.yml +++ b/config/locales/crowdin/az.yml @@ -114,7 +114,7 @@ az: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ az: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ az: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ az: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ az: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ az: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ az: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ az: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ az: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ az: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ az: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ az: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ az: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ az: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/be.yml b/config/locales/crowdin/be.yml index 48774b98d06..8f2424653dd 100644 --- a/config/locales/crowdin/be.yml +++ b/config/locales/crowdin/be.yml @@ -114,7 +114,7 @@ be: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ be: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ be: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +399,9 @@ be: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +410,14 @@ be: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -430,7 +434,7 @@ be: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1572,6 +1576,9 @@ be: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1580,7 +1587,7 @@ be: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3509,6 +3516,11 @@ be: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3571,6 +3583,72 @@ be: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Прызначаная асоба @@ -3602,6 +3680,7 @@ be: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3642,6 +3721,7 @@ be: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3892,7 +3972,7 @@ be: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5158,12 +5238,12 @@ be: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 9efe399f0d6..d1fe9ed25e4 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -114,7 +114,7 @@ bg: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ bg: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ bg: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ bg: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ bg: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ bg: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ bg: dependencies: Зависимости activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ bg: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Показване до attachment: @@ -3389,6 +3396,11 @@ bg: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3451,6 +3463,72 @@ bg: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Изпълнител @@ -3482,6 +3560,7 @@ bg: invalid_filter: Invalid notification filter label_accessibility: Достъпност label_account: Акаунт + label_actions: Действия label_active: Активен label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3522,6 +3601,7 @@ bg: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Отмени + label_integrations: Integrations label_add_column: Add column label_applied_status: Приложен статус label_archive_project: Archive project @@ -3772,7 +3852,7 @@ bg: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Отиди на проект... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Ключови думи label_language_based: Въз основа на езика на потребителя label_last_activity: Последна активност @@ -5024,12 +5104,12 @@ bg: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index eda8b90f7d2..8b68d01acae 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -114,7 +114,7 @@ ca: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ca: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ca: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ ca: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ ca: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ ca: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1535,6 +1539,9 @@ ca: dependencies: Dependències activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1543,7 +1550,7 @@ ca: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Mostrar fins attachment: @@ -3388,6 +3395,11 @@ ca: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3450,6 +3462,72 @@ ca: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignat a @@ -3481,6 +3559,7 @@ ca: invalid_filter: Invalid notification filter label_accessibility: Accessibilitat label_account: Compte + label_actions: Accions label_active: Actiu label_activate_user: Activa usuari label_active_in_new_projects: Activa a nous projectes @@ -3521,6 +3600,7 @@ ca: label_ical_access_key_generation_hint: Generat automàticament en subscriure't a un calendari. label_ical_access_key_latest: úlitma label_ical_access_key_revoke: Revoca + label_integrations: Integrations label_add_column: Add column label_applied_status: Estat aplicat label_archive_project: Arxiva el projecte @@ -3771,7 +3851,7 @@ ca: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Anar al projecte... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Paraules clau label_language_based: Basat en l'idioma de l'usuari label_last_activity: Darrera activitat @@ -5017,12 +5097,12 @@ ca: setting_welcome_text: Bloc de text de benvinguda setting_welcome_title: Títol del bloc de benvinguda setting_welcome_on_homescreen: Mostrar el bloc de benvinguda a la pàgina d'inici - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Mètode de destacament per defecte diff --git a/config/locales/crowdin/ckb-IR.yml b/config/locales/crowdin/ckb-IR.yml index 953730cf1b2..471cb9c9878 100644 --- a/config/locales/crowdin/ckb-IR.yml +++ b/config/locales/crowdin/ckb-IR.yml @@ -114,7 +114,7 @@ ckb-IR: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ckb-IR: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ckb-IR: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ ckb-IR: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ ckb-IR: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ ckb-IR: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ ckb-IR: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ ckb-IR: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ ckb-IR: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ ckb-IR: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ ckb-IR: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ ckb-IR: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ ckb-IR: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ ckb-IR: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index e58bd2eba08..ad36bbea7aa 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -114,7 +114,7 @@ cs: import: title: Import jira: - title: Jira import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ cs: title: Jira configuration new: Nová konfigurace banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ cs: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +399,9 @@ cs: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +410,14 @@ cs: label_autofixed_suggestion: Budoucí identifikátor label_example_work_package_id: Příklad ID pracovního balíčku autofix_preview: - error_too_long: Musí mít méně než 5 znaků + error_too_long: Musí obsahovat maximálně 10 znaků + error_numerical: Nemůže být čistě číselný + error_starts_with_number: Nemůže začínat číslem error_special_characters: Speciální znaky nejsou povoleny + error_not_fully_uppercased: Musí být napsán velkými písmeny error_in_use: Již se používá jako identifikátor jiného projektu error_reserved: Již se dříve používal jako identifikátor jiného projektu + error_unknown: Potřebuje ruční revizi remaining_projects: one: "... 1 další projekt" few: "... %{count} more projects" @@ -430,7 +434,7 @@ cs: checkbox_label: Chápu, že se tím trvale změní všechny identifikátory pracovních balíčků success_banner: Úspěšně aktualizován formát identifikátoru pracovního balíčku. in_progress: - banner_message: Identifikátory projektů jsou v současné době aktualizovány na alfanumerické projektové identifikátory. To může chvíli trvat. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1572,6 +1576,9 @@ cs: dependencies: Závislosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projekty import/jira: @@ -1580,7 +1587,7 @@ cs: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Zobrazit do attachment: @@ -1697,7 +1704,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 @@ -2102,7 +2109,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í ' @@ -2409,11 +2416,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 @@ -3505,12 +3512,17 @@ cs: ai: Umělá Inteligence (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: Protokol MCP (Model Context Protocol) quick_add: label: Přidat… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Poskytovatelské tokeny vydává OpenProject a umožňuje tím přístup ostatním aplikacím. Klientské tokeny jsou vydávány jinými aplikacemi a umožňují OpenProjectu přistup k nim. no_results: @@ -3573,6 +3585,72 @@ cs: disabled_text: RSS tokeny nejsou administrátorem povoleny. Pro použití této funkce kontaktujte svého správce. storages: unknown_storage: Neznámé úložiště + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Řešitel @@ -3591,7 +3669,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. @@ -3604,6 +3682,7 @@ cs: invalid_filter: Neplatný filtr oznámení label_accessibility: Přístupnost label_account: Účet + label_actions: Akce label_active: Aktivní label_activate_user: Aktivovat uživatele label_active_in_new_projects: Aktivní v nových projektech @@ -3644,6 +3723,7 @@ cs: label_ical_access_key_generation_hint: Automaticky vygenerováno při odebírání kalendáře. label_ical_access_key_latest: poslední label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Přidat sloupec label_applied_status: Přiřazený stav label_archive_project: Archivovat projekt @@ -3894,7 +3974,7 @@ cs: label_external_links: Externí odkazy label_locale: Jazyk a region label_jump_to_a_project: Přejít na projekt... - label_jira_import: Jira import + label_jira_import: Jira Migrator label_keyword_plural: Klíčová slova label_language_based: Na základě jazyka uživatele label_last_activity: Poslední aktivita @@ -4043,9 +4123,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 @@ -4070,7 +4150,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 @@ -4231,7 +4311,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 @@ -4369,28 +4449,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: Dobrý den, %{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 @@ -4399,7 +4479,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 @@ -4712,7 +4792,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 @@ -5160,12 +5240,12 @@ cs: setting_welcome_text: Text uvítacího bloku setting_welcome_title: Název uvítacího bloku setting_welcome_on_homescreen: Zobrazit uvítací blok na domovské obrazovce - setting_work_packages_identifier_numeric: Číselná posloupnost pro celou instanci (výchozí) - setting_work_packages_identifier_numeric_caption: 'Každý pracovní balíček dostane pořadové číslo začínající číslem 1 a zvyšující se s každým novým. Čísla jsou v rámci této instance jedinečná, takže zůstávají stejná, i když se pracovní balíčky přesouvají mezi projekty. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Projektové alfanumerické identifikátory - setting_work_packages_identifier_alphanumeric_caption: 'Každý projekt má jedinečný identifikátor, který je předřazen ID pracovního balíčku. Pokud se pracovní balíček přesune do jiného projektu, vygeneruje se nový identifikátor, ale starý zůstává funkční. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Výchozí režim zvýraznění @@ -5240,7 +5320,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 @@ -5445,7 +5525,7 @@ cs: 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. + Doručení e-mailu není nakonfigurováno a oznámení jsou zakázána. Nakonfigurujte 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ě." diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index 6b11707ed26..bf5bb0f51d9 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -114,7 +114,7 @@ da: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ da: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ da: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ da: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ da: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ da: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1535,6 +1539,9 @@ da: dependencies: Aflæggere activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1543,7 +1550,7 @@ da: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3390,6 +3397,11 @@ da: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3452,6 +3464,72 @@ da: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Tildelt @@ -3483,6 +3561,7 @@ da: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Konto + label_actions: Handlinger label_active: Aktiv label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3523,6 +3602,7 @@ da: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Tildelt status label_archive_project: Archive project @@ -3773,7 +3853,7 @@ da: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Gå til et projekt... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Baseret på brugerens sprog label_last_activity: Seneste aktivitet @@ -5023,12 +5103,12 @@ da: setting_welcome_text: Velkomstblok-tekst setting_welcome_title: Velkomstblok-titel setting_welcome_on_homescreen: Vis velkomstblok på hjemmeskærm - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index 0d86fa24ae4..525a7c876a1 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -87,11 +87,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 @@ -114,7 +114,7 @@ de: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Verwenden Sie dieses Tool, um Daten aus Ihrer Jira-Instanz zu importieren. Sie können mehrere Jira-Hosts konfigurieren und auswählen, was bei jedem Importlauf importiert werden soll. errors: cannot_delete_with_imports: Jira-Hosts mit laufenden Importen können nicht gelöscht werden @@ -125,8 +125,8 @@ de: title: Jira-Konfiguration new: Neue Konfiguration banner: - title: Begrenzter Import - description: 'Dieses Importtool befindet sich derzeit in der Betaphase und kann nur grundlegende Daten importieren: Projekte, Tickets (Name, Titel, Beschreibung, Anhänge), Nutzer (Name, E-Mail, Projektmitgliedschaft), Status und Typen. Es kann keine Workflows, benutzerdefinierten Felder, Ticketbeziehungen oder Berechtigungen importieren. Wir unterstützen derzeit nur die Jira Server/Data Center Versionen 10.x und 11.x. Cloud-Instanzen werden derzeit nicht unterstützt.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ de: run: title: Importlauf history: Historie - remove_error: Ein Jira-Import kann nicht entfernt werden während er läuft + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Ein anderer Jira-Importlauf wird gerade durchgeführt oder wartet auf die Überprüfung. Bitte schließen Sie ihn ab oder machen Sie ihn rückgängig, bevor Sie einen neuen Import starten. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ de: notification_text_default: "

Hallo,

Ein neues Projekt wurde erstellt: projectValue:name

Vielen Dank

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ de: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ de: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Standard-Übergänge @@ -1294,10 +1298,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: @@ -1351,7 +1355,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. @@ -1454,7 +1458,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 @@ -1533,6 +1537,9 @@ de: dependencies: Abhängigkeiten activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projekte import/jira: @@ -1541,7 +1548,7 @@ de: personal_access_token: Persönlicher Zugangs-Token import/jira_open_project_reference: jira: Jira - jira_import: Jira Import + jira_import: Jira Migrator announcements: show_until: Anzeigen bis attachment: @@ -1711,7 +1718,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 @@ -2247,7 +2254,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: @@ -3066,7 +3073,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}' @@ -3354,7 +3361,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" @@ -3386,6 +3393,11 @@ de: quick_add: label: Hinzufügen… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider-Tokens werden von OpenProject ausgestellt und ermöglichen anderen Anwendungen den Zugriff darauf. Client-Tokens werden von anderen Anwendungen ausgestellt und ermöglichen OpenProject den Zugriff auf sie. no_results: @@ -3448,6 +3460,72 @@ de: disabled_text: RSS-Token sind vom Administrator nicht aktiviert. Bitte kontaktieren Sie Ihren Administrator, um diese Funktion zu nutzen. storages: unknown_storage: Unbekannter Speicher + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Zugewiesen an @@ -3479,6 +3557,7 @@ de: invalid_filter: Ungültiger Benachrichtigungsfilter label_accessibility: Barrierefreiheit label_account: Konto + label_actions: Aktionen label_active: Aktiv label_activate_user: Benutzer aktivieren label_active_in_new_projects: Aktiv in neuen Projekten @@ -3519,6 +3598,7 @@ de: label_ical_access_key_generation_hint: Wird automatisch generiert, wenn ein Kalender abonniert wird. label_ical_access_key_latest: neueste label_ical_access_key_revoke: Widerrufen + label_integrations: Integrations label_add_column: Spalte hinzufügen label_applied_status: Zugewiesener Status label_archive_project: Projekt archivieren @@ -3676,7 +3756,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 @@ -3769,7 +3849,7 @@ de: label_external_links: Externe Links label_locale: Sprache und Region label_jump_to_a_project: Zu einem Projekt springen... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Schlüsselwörter label_language_based: Sprachabhängig label_last_activity: Letzte Aktivität @@ -4786,7 +4866,7 @@ de: Erhöhen Sie diesen Wert zur Verbesserung der Performance, da die Erfassung des genutzten Festplattenspeichers Ressourcen-intensiv ist. oauth_application_details_html: 'The client secret value will not be accessible again after you close this window. Please copy these values into the Nextcloud OpenProject Integration settings:' 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: @@ -5027,12 +5107,12 @@ de: setting_welcome_text: Text für Willkommens-Block setting_welcome_title: Titel des Willkommens-Block setting_welcome_on_homescreen: Willkommens-Block auf Startseite anzeigen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standard Hervorhebung @@ -5496,7 +5576,7 @@ de: warning_user_limit_reached_admin_html: 'Adding additional users will exceed the current limit. Please [upgrade your plan](upgrade_url) to be able to ensure external users are able to access this instance. ' - 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. + 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. ' warning_protocol_mismatch_html: '' diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index 011c6b862ea..16fb6c6537f 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -114,7 +114,7 @@ el: import: title: Import jira: - title: Εισαγωγή από Jira + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ el: title: Jira configuration new: New configuration banner: - title: Περιορισμένη εισαγωγή - description: 'Αυτό το εργαλείο εισαγωγής βρίσκεται επί του παρόντος σε beta έκδοση και μπορεί να εισάγει μόνο βασικά δεδομένα: έργα, ζητήματα (όνομα, τίτλος, περιγραφή, συνημμένα), χρήστες (όνομα, email, συμμετοχή σε έργα), καταστάσεις και τύπους. Δεν μπορεί να εισαγάγει ροές εργασίας, προσαρμοσμένα πεδία, σχέσεις ζητημάτων ή δικαιώματα. Προς το παρόν υποστηρίζουμε μόνο τις εκδόσεις 10.x και 11.x του Jira Server/Data Center. Προς το παρόν δεν υποστηρίζονται περιπτώσεις cloud.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ el: run: title: Import run history: History - remove_error: Μια εισαγωγή Jira δεν μπορεί να αφαιρεθεί ενώ εκτελείται + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ el: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ el: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ el: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1535,6 +1539,9 @@ el: dependencies: Εξαρτήσεις activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1543,7 +1550,7 @@ el: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Εμφάνιση μέχρι attachment: @@ -3390,6 +3397,11 @@ el: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3452,6 +3464,72 @@ el: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Ανάθεση σε @@ -3483,6 +3561,7 @@ el: invalid_filter: Invalid notification filter label_accessibility: Προσβασιμότητα label_account: Λογαριασμός + label_actions: Ενέργειες label_active: Ενεργό label_activate_user: Ενεργοποίηση χρήστη label_active_in_new_projects: Ενεργό σε καινούργια έργα @@ -3523,6 +3602,7 @@ el: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Ανάκληση + label_integrations: Integrations label_add_column: Add column label_applied_status: Εφαρμόστηκε η κατάσταση label_archive_project: Αρχειοθέτηση έργου @@ -3773,7 +3853,7 @@ el: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Μεταβείτε σε ένα έργο... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Λέξεις-κλειδιά label_language_based: Με βάση τη γλώσσα του χρήστη label_last_activity: Τελευταία δραστηριότητα @@ -5025,12 +5105,12 @@ el: setting_welcome_text: Μπλοκ κειμένου καλωσορίσματος setting_welcome_title: Μπλοκ τίτλου καλωσορίσματος setting_welcome_on_homescreen: Εμφάνιση μπλοκ καλωσορίσματος στην αρχική σελίδα - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Προεπιλεγμένη λειτουργία επισήμανσης diff --git a/config/locales/crowdin/eo.yml b/config/locales/crowdin/eo.yml index aa66f2747ff..5143bdccc65 100644 --- a/config/locales/crowdin/eo.yml +++ b/config/locales/crowdin/eo.yml @@ -114,7 +114,7 @@ eo: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ eo: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ eo: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ eo: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ eo: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ eo: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ eo: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ eo: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Montri ĝis attachment: @@ -3391,6 +3398,11 @@ eo: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ eo: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Asignita al @@ -3484,6 +3562,7 @@ eo: invalid_filter: Invalid notification filter label_accessibility: Alirebleco label_account: Konto + label_actions: Agoj label_active: Aktiva label_activate_user: Aktivigi uzanton label_active_in_new_projects: Aktiva en novaj projektoj @@ -3524,6 +3603,7 @@ eo: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Eksvalidigi + label_integrations: Integrations label_add_column: Add column label_applied_status: Aplikita stato label_archive_project: Aktivi projekton @@ -3774,7 +3854,7 @@ eo: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Iri al la projekto... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Ŝlosilvortoj label_language_based: Bazita sur la lingvo de la uzanto label_last_activity: Lasta aktiveco @@ -5030,12 +5110,12 @@ eo: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index 0594e359f16..7076819d871 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -114,7 +114,7 @@ es: import: title: Importar jira: - title: Importación desde Jira + title: Jira Migrator description: Utilice esta herramienta para importar datos desde su instancia de Jira. Puede configurar varios hosts de Jira y elegir qué importar en cada ejecución de importación. errors: cannot_delete_with_imports: No se puede eliminar el host de Jira con importaciones existentes @@ -125,8 +125,8 @@ es: title: Configuración de Jira new: Nueva configuración banner: - title: Importación limitada - description: 'Esta herramienta de importación se encuentra actualmente en fase beta y solo puede importar datos básicos: proyectos, incidencias (nombre, título, descripción, archivos adjuntos), usuarios (nombre, correo electrónico, pertenencia a proyectos), estados y tipos. No puede importar flujos de trabajo, campos personalizados, relaciones entre incidencias ni permisos. Actualmente solo es compatible con las versiones 10.x y 11.x de Jira Server/Data Center. Por el momento, no es compatible con instancias en la nube.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Nombre @@ -162,7 +162,7 @@ es: run: title: Importar ejecución history: Historial - remove_error: No se puede eliminar una importación de Jira mientras se está ejecutando + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Actualmente hay otra importación de Jira en curso o pendiente de revisión. Complétela o revierta antes de iniciar una nueva importación. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ es: notification_text_default: "

Hola,

Se ha creado un nuevo proyecto: projectValue:name

Muchas gracias

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ es: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ es: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Transiciones predeterminadas @@ -1352,7 +1356,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. @@ -1531,6 +1535,9 @@ es: dependencies: Dependencias activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Proyectos import/jira: @@ -1539,7 +1546,7 @@ es: personal_access_token: Token de acceso personal import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Mostrar hasta attachment: @@ -3382,6 +3389,11 @@ es: quick_add: label: Añadir… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Los tokens de proveedor son emitidos por OpenProject y permiten a otras aplicaciones acceder a él. Los tokens de cliente son emitidos por otras aplicaciones y permiten a OpenProject acceder a ellos. no_results: @@ -3444,6 +3456,72 @@ es: disabled_text: Los tokens RSS no están habilitados por el administrador. Póngase en contacto con su administrador para utilizar esta función. storages: unknown_storage: Almacenamiento desconocido + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Asignado a @@ -3475,6 +3553,7 @@ es: invalid_filter: Filtro de notificación no válido label_accessibility: Accesibilidad label_account: Cuenta + label_actions: Comportamiento label_active: Activo label_activate_user: Activar usuario label_active_in_new_projects: Activo en nuevos proyectos @@ -3515,6 +3594,7 @@ es: label_ical_access_key_generation_hint: Se genera automáticamente al suscribirse a un calendario. label_ical_access_key_latest: último label_ical_access_key_revoke: Revocar + label_integrations: Integrations label_add_column: Añadir columna label_applied_status: Estado aplicado label_archive_project: Archivar proyecto @@ -3765,7 +3845,7 @@ es: label_external_links: Enlaces externos label_locale: Idioma y región label_jump_to_a_project: Saltar a un proyecto... - label_jira_import: Importación desde Jira + label_jira_import: Jira Migrator label_keyword_plural: Palabras clave label_language_based: Basado en el idioma del usuario label_last_activity: Última actividad @@ -5015,12 +5095,12 @@ es: setting_welcome_text: Bloque de texto de bienvenida setting_welcome_title: Título del bloque de bienvenida setting_welcome_on_homescreen: Mostrar bloque de bienvenida en la pagina de inicio - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Modo de resaltado predeterminado diff --git a/config/locales/crowdin/et.yml b/config/locales/crowdin/et.yml index 6b4c80645d8..b7f8780c0a3 100644 --- a/config/locales/crowdin/et.yml +++ b/config/locales/crowdin/et.yml @@ -114,7 +114,7 @@ et: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ et: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ et: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ et: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ et: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ et: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ et: dependencies: Sõltuvused activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ et: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ et: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ et: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Määratud tegija @@ -3484,6 +3562,7 @@ et: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Konto + label_actions: Tegevused label_active: Aktiivne label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ et: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: viimased label_ical_access_key_revoke: Võta tagasi + label_integrations: Integrations label_add_column: Lisa veerg label_applied_status: Rakendatud olek label_archive_project: Arhiveeri projekt @@ -3774,7 +3854,7 @@ et: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Mine projekti... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Märksõnad label_language_based: Põhineb kasutaja keelel label_last_activity: Viimane tegevus @@ -5028,12 +5108,12 @@ et: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/eu.yml b/config/locales/crowdin/eu.yml index c720818a989..6610ae3800e 100644 --- a/config/locales/crowdin/eu.yml +++ b/config/locales/crowdin/eu.yml @@ -114,7 +114,7 @@ eu: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ eu: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ eu: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ eu: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ eu: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ eu: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ eu: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ eu: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ eu: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ eu: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ eu: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ eu: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ eu: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ eu: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fa.yml b/config/locales/crowdin/fa.yml index c9e7039680f..27b528869c7 100644 --- a/config/locales/crowdin/fa.yml +++ b/config/locales/crowdin/fa.yml @@ -114,7 +114,7 @@ fa: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ fa: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ fa: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ fa: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ fa: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ fa: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ fa: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ fa: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ fa: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ fa: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: نماینده @@ -3484,6 +3562,7 @@ fa: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: حساب کاربری + label_actions: اقدامات label_active: Active label_activate_user: کاربر فعال label_active_in_new_projects: فعال در پروژه های جدید @@ -3524,6 +3603,7 @@ fa: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ fa: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ fa: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index f7ad960ff5d..d0d721919bf 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -114,7 +114,7 @@ fi: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ fi: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ fi: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ fi: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ fi: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ fi: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ fi: dependencies: Riippuvuudet activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ fi: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Näytä tähän päivään asti attachment: @@ -3389,6 +3396,11 @@ fi: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3451,6 +3463,72 @@ fi: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Työn suorittaja @@ -3482,6 +3560,7 @@ fi: invalid_filter: Invalid notification filter label_accessibility: Helppokäyttötoiminnot label_account: Käyttäjätili + label_actions: Toiminnot label_active: Aktiivinen label_activate_user: Aktivoi käyttäjä label_active_in_new_projects: Active in new projects @@ -3522,6 +3601,7 @@ fi: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Käytetty tila label_archive_project: Arkisto projekti @@ -3772,7 +3852,7 @@ fi: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Siirry projektiin... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Pohjautuen käyttäjän kieleen label_last_activity: Viimeinen toiminta @@ -5028,12 +5108,12 @@ fi: setting_welcome_text: Tervehdysteksti setting_welcome_title: Tervehdyspalkki setting_welcome_on_homescreen: Näytä tervehdyspalkki kotinäkymässä - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index 5c3dede20f2..4b8877c3676 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -114,7 +114,7 @@ fil: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ fil: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ fil: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ fil: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ fil: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ fil: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ fil: dependencies: Dependencia activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ fil: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: I-displey hanggang attachment: @@ -3391,6 +3398,11 @@ fil: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ fil: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Naitalaga @@ -3484,6 +3562,7 @@ fil: invalid_filter: Invalid notification filter label_accessibility: Aksibilidad label_account: Akawnt + label_actions: Mga Aksyon label_active: Aktibo label_activate_user: Aktibong gumagamit label_active_in_new_projects: Aktibo sa bagong proyekto @@ -3524,6 +3603,7 @@ fil: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Estadong nilapat label_archive_project: I-archive ang proyekto @@ -3774,7 +3854,7 @@ fil: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Tumalon sa isang proyekto... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Naka-base sa linggwahe ng gumagamit label_last_activity: Huling aktibidad @@ -5026,12 +5106,12 @@ fil: setting_welcome_text: Teskstong welcome block setting_welcome_title: Titulo ng welcome back setting_welcome_on_homescreen: I-display ang welcome block sa homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 33f7d6da7d2..0c9fc8d0761 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -114,7 +114,7 @@ fr: import: title: Importation jira: - title: Importation de Jira + title: Migrateur Jira description: Utilisez cet outil pour importer des données à partir de votre instance Jira. Vous pouvez configurer plusieurs hôtes Jira et choisir les données à importer à chaque cycle d'importation. errors: cannot_delete_with_imports: Impossible de supprimer un hôte Jira avec des importations existantes @@ -125,8 +125,8 @@ fr: title: Configuration de Jira new: Nouvelle configuration banner: - title: Importation limitée - description: 'Cet outil d''importation est actuellement en version bêta et ne peut importer que des données de base : projets, tickets (nom, titre, description, pièces jointes), utilisateurs (nom, e-mail, appartenance à un projet), statuts et types. Il ne peut pas importer les flux de travail, les champs personnalisés, les relations entre les tickets ou les autorisations. Nous ne prenons actuellement en charge que les versions 10.x et 11.x de Jira Server/Data Center. Les instances cloud ne sont pas prises en charge pour le moment.' + title: Capacités d'importation limitées + description: 'Cet outil Jora Migrator est actuellement en version bêta et ne peut importer que des données de base : projets, tickets (nom, titre, description, pièces jointes), utilisateurs (nom, e-mail, appartenance à un projet), statuts et types. Il ne peut pas importer les flux de travail, les champs personnalisés, les relations entre les tickets ou les autorisations. Nous ne prenons actuellement en charge que les versions 10.x et 11.x de Jira Server/Data Center. Les instances cloud ne sont pas prises en charge pour le moment.' form: fields: name: Nom @@ -162,7 +162,7 @@ fr: run: title: Cycle d'importation history: Historique - remove_error: Une importation Jira ne peut pas être supprimée lorsqu'elle est en cours d'exécution + remove_error: Une exécution d'importation Jira ne peut pas être supprimée lorsqu'elle est en cours d'exécution import_blocked_error: Un autre cycle d'importation Jira est actuellement en cours ou en attente de révision. Veuillez le terminer ou l'annuler avant de commencer une nouvelle importation. project_identifier_taken: 'Vous essayez d''importer un projet avec un identifiant déjà utilisé : %{taken_identifier}. Veuillez mettre à jour l''identifiant du projet dans Jira, puis cliquez sur Réessayer.' blank: @@ -387,9 +387,9 @@ fr: notification_text_default: "

Bonjour,

Un nouveau projet a été créé : projectValue:name

Nous vous remercions de votre attention et vous prions d'agréer nos salutations distinguées.

\n" work_packages_identifier: page_header: - description: Vous avez le choix entre des identifiants numériques de base pour les lots de travail et des identifiants spécifiques aux projets qui ajoutent l'identifiant du projet à l'identifiant du lot de travail. + description: Vous avez le choix entre des identifiants numériques classiques pour les lots de travaux et des identifiants sémantiques spécifiques aux projets, qui ajoutent l'identifiant du projet à l'identifiant du lot de travaux. banner: - existing_identifiers_notice: 'Les identifiants existants de %{project_count} projets ne répondent pas aux exigences des identifiants alphanumériques basés sur les projets. OpenProject peut les mettre à jour automatiquement pour qu''ils soient valides, comme dans les exemples ci-dessous. Cliquez sur "Autofixer et sauvegarder" pour mettre à jour les identifiants de tous les projets de cette manière et activer les identifiants alphanumériques basés sur le projet. + existing_identifiers_notice: 'Les identifiants existants de %{project_count} projets ne répondent pas aux exigences des identifiants sémantiques basés sur les projets. OpenProject peut les mettre à jour automatiquement pour qu''ils soient valides, comme dans les exemples ci-dessous. Cliquez sur « Corriger automatiquement et enregistrer » pour mettre à jour les identifiants de tous les projets de cette manière et activer les identifiants sémantiques basés sur les projets. ' box_header: @@ -398,10 +398,14 @@ fr: label_autofixed_suggestion: Futur identifiant label_example_work_package_id: Exemple d'identifiant de lot de travaux autofix_preview: - error_too_long: Doit avoir moins de 5 caractères + error_too_long: Doit être composé de 10 caractères ou moins + error_numerical: Ne peut être purement numérique + error_starts_with_number: Ne peut pas commencer par un chiffre error_special_characters: Caractères spéciaux non autorisés + error_not_fully_uppercased: Doit être en majuscules error_in_use: Déjà utilisé comme alias actif d'un autre projet error_reserved: Réservé par l'historique des alias d'un autre projet + error_unknown: Nécessite une vérification manuelle remaining_projects: one: "... 1 projet supplémentaire" other: "... %{count} projets supplémentaires" @@ -416,7 +420,7 @@ fr: checkbox_label: Je comprends que cela changera définitivement tous les identifiants du lot de travaux success_banner: Le format de l'identifiant du lot de travaux a été mis à jour. in_progress: - banner_message: Les identifiants de projet sont actuellement mis à jour pour devenir des identifiants alphanumériques basés sur les projets. Cela peut prendre un certain temps. + banner_message: Les identifiants de projet sont actuellement mis à jour pour devenir des identifiants sémantiques basés sur les projets. Cela peut prendre un certain temps. workflows: tabs: default_transitions: Transitions par défaut @@ -1529,6 +1533,9 @@ fr: dependencies: Dépendances activerecord: attributes: + work_package_semantic_alias: + identifier: Identifiant + work_package: Lot de travaux jira_import: projects: Projets import/jira: @@ -1537,7 +1544,7 @@ fr: personal_access_token: Jeton d'accès personnel import/jira_open_project_reference: jira: Jira - jira_import: Importation de Jira + jira_import: Migrateur Jira announcements: show_until: Afficher jusqu'à attachment: @@ -3384,6 +3391,11 @@ fr: quick_add: label: Ajouter… my_account: + notifications_and_email: + title: Notification et e-mail + tabs: + notifications: Paramètres de notifications + email_reminders: Rappels par e-mail access_tokens: description: Les jetons de fournisseur sont émis par OpenProject, ce qui permet à d'autres applications d'y accéder. Les jetons clients sont émis par d'autres applications, permettant à OpenProject d'y accéder. no_results: @@ -3446,6 +3458,72 @@ fr: disabled_text: Les jetons RSS ne sont pas activés par l'administrateur. Veuillez contacter votre administrateur pour utiliser cette fonctionnalité. storages: unknown_storage: Espace de stockage inconnu + email_reminders: + immediate_reminders: + title: M'envoyer un rappel par e-mail + mentioned: M'avertir lorsque je suis mentionné(e) + personal_reminder: M'avertir pour les rappels personnels + daily_reminders: + title: M'envoyer des rappels quotidiens par e-mail pour les notifications non lues + caption: Vous ne recevrez ces rappels que pour les notifications non lues et seulement aux heures que vous aurez spécifiées. Tant que vous n'aurez pas configuré un fuseau horaire pour votre compte, les heures seront interprétées comme étant en UTC. + enabled: Activer les rappels quotidiens par e-mail + add_time: Ajouter une heure + remove_time: Supprimer l'heure + time_slot_label: Heure de rappel (UTC) + workdays: + title: Recevoir des rappels par e-mail aux jours spécifiés + submit_button: Mettre à jour les jours de rappel + pause_reminders: + title: Suspendre les notifications par e-mail + enabled: Suspendre temporairement les rappels quotidiens par e-mail + date_range: Période de pause + email_alerts: + title: Alertes par e-mail pour les autres éléments qui ne sont pas des lots de travaux + news_added: Actualités ajoutées + news_commented: Commenter un article d'actualité + document_added: Document ajouté + forum_messages: Message de forum publié + wiki_page_added: Page wiki ajoutée + wiki_page_updated: Page wiki mise à jour + membership_added: Adhésion ajoutée + membership_updated: Adhésion mise à jour + submit_button: Mettre à jour les alertes + notifications: + participating: + title: Participant + submit_button: Mettre à jour les préférences + mentioned: Mentionné + watched: En cours + assignee: Personne assignée + responsible: Responsable + shared: Partagé avec moi + date_alerts: + title: Alertes de date + submit_button: Mettre à jour les alertes de date + start_date: Date de début + due_date: Date de fin + overdue: En retard + times: + same_day: Le même jour + one_day_before: 1 jour avant + three_days_before: 3 jours avant + seven_days_before: 7 jours avant + one_day_after: 1 jour après + three_days_after: 3 jours après + seven_days_after: 7 jours après + non_participating: + title: Non participant + submit_button: Mettre à jour les préférences + work_package_created: Nouveaux lots de travaux + work_package_commented: Tous les nouveaux commentaires + work_package_processed: Tous les changements de statut + work_package_prioritized: Tous les changements de priorité + work_package_scheduled: Tous les changements de date + project_specific_settings: + title: Paramètres de notification spécifiques au projet + add_button: Ajouter des notifications spécifiques au projet + dialog_title: Ajouter des notifications spécifiques au projet + list_header: Projets avec notifications spécifiques notifications: reasons: assigned: Personne assignée @@ -3477,6 +3555,7 @@ fr: invalid_filter: Filtre de notification invalide label_accessibility: Accessibilité label_account: Compte + label_actions: Actions label_active: Actif label_activate_user: Activer l’utilisateur label_active_in_new_projects: Activer dans de nouveaux projets @@ -3517,6 +3596,7 @@ fr: label_ical_access_key_generation_hint: Généré automatiquement lors de l'abonnement à un calendrier. label_ical_access_key_latest: Dernier label_ical_access_key_revoke: Révoquer + label_integrations: Intégrations label_add_column: Ajouter une colonne label_applied_status: Statut appliqué label_archive_project: Archiver le projet @@ -3767,7 +3847,7 @@ fr: label_external_links: Liens externes label_locale: Langue et région label_jump_to_a_project: Aller à un projet… - label_jira_import: Importation de Jira + label_jira_import: Jira Migrator label_keyword_plural: Mots clés label_language_based: Basé sur la langue de l'utilisateur label_last_activity: Dernière activité @@ -5021,12 +5101,12 @@ fr: setting_welcome_text: Bloc de texte de bienvenue setting_welcome_title: Bloc de titre de bienvenue setting_welcome_on_homescreen: Afficher le bloc de bienvenue sur l'écran d'accueil - setting_work_packages_identifier_numeric: Séquence numérique à l'échelle de l'instance (par défaut) - setting_work_packages_identifier_numeric_caption: 'Chaque lot de travaux obtient un numéro séquentiel commençant par 1 et incrémenté à chaque nouveau. Les nombres sont uniques dans cette instance, donc ils restent les mêmes même si les lots de travaux sont déplacés entre les projets. + setting_work_packages_identifier_classic: Séquence numérique à l'échelle de l'instance (par défaut) + setting_work_packages_identifier_classic_caption: 'Chaque lot de travaux obtient un numéro séquentiel commençant par 1 et incrémenté à chaque nouveau. Les nombres sont uniques dans cette instance, donc ils restent les mêmes même si les lots de travaux sont déplacés entre les projets. ' - setting_work_packages_identifier_alphanumeric: Identifiants alphanumériques basés sur le projet - setting_work_packages_identifier_alphanumeric_caption: 'Chaque projet a un identifiant unique qui est préfixé à l''ID du lot de travaux. Si un lot de travaux a été déplacé vers un autre projet, un nouvel identifiant est généré, mais l''ancien continue de fonctionner. + setting_work_packages_identifier_semantic: Identifiants sémantiques basés sur le projet + setting_work_packages_identifier_semantic_caption: 'Chaque projet a un identifiant unique qui est préfixé à l''ID du lot de travaux. Si un lot de travaux a été déplacé vers un autre projet, un nouvel identifiant est généré, mais l''ancien continue de fonctionner. ' setting_work_package_list_default_highlighting_mode: Mode de surbrillance par défaut diff --git a/config/locales/crowdin/he.yml b/config/locales/crowdin/he.yml index 78cc43c89f3..b6aecb8c1b5 100644 --- a/config/locales/crowdin/he.yml +++ b/config/locales/crowdin/he.yml @@ -114,7 +114,7 @@ he: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ he: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ he: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +399,9 @@ he: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +410,14 @@ he: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" two: "... %{count} more projects" @@ -430,7 +434,7 @@ he: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1572,6 +1576,9 @@ he: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1580,7 +1587,7 @@ he: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3509,6 +3516,11 @@ he: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3571,6 +3583,72 @@ he: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: משויך אל @@ -3602,6 +3680,7 @@ he: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: חשבון + label_actions: Actions label_active: פעיל label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3642,6 +3721,7 @@ he: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: מצב יישומית label_archive_project: Archive project @@ -3892,7 +3972,7 @@ he: label_external_links: External links label_locale: Language and region label_jump_to_a_project: קפוץ אל פרוייקט... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5158,12 +5238,12 @@ he: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/hi.yml b/config/locales/crowdin/hi.yml index b2a678ee29f..1c7917e13ab 100644 --- a/config/locales/crowdin/hi.yml +++ b/config/locales/crowdin/hi.yml @@ -114,7 +114,7 @@ hi: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ hi: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ hi: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ hi: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ hi: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ hi: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ hi: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ hi: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ hi: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ hi: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: अनुदिष्ट @@ -3484,6 +3562,7 @@ hi: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: खाता + label_actions: क्रियाएँ label_active: सक्रिय label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ hi: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: लागू की गई स्थिति label_archive_project: Archive project @@ -3774,7 +3854,7 @@ hi: label_external_links: External links label_locale: Language and region label_jump_to_a_project: किसी प्रोजेक्ट पर जंप करें... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ hi: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: डिफ़ॉल्ट हाइलाइटिंग तरीका diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index 1524d801db2..f741963e4ca 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -114,7 +114,7 @@ hr: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ hr: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ hr: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +393,9 @@ hr: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +404,14 @@ hr: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -423,7 +427,7 @@ hr: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1554,6 +1558,9 @@ hr: dependencies: Ovisnosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1569,7 @@ hr: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Prikaži do attachment: @@ -3448,6 +3455,11 @@ hr: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3510,6 +3522,72 @@ hr: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Opunomoćeno @@ -3541,6 +3619,7 @@ hr: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Korisnički račun + label_actions: Actions label_active: Aktivno label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3581,6 +3660,7 @@ hr: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Dodijeljeni status label_archive_project: Archive project @@ -3831,7 +3911,7 @@ hr: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Pređi na projekt... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Na osnovi jezika korisnika label_last_activity: Zadnja aktivnost @@ -5090,12 +5170,12 @@ hr: setting_welcome_text: Tekst dobrodošlice setting_welcome_title: Naslov dobrodošlice setting_welcome_on_homescreen: Prikaži tekst dobrodošlice na početnoj stranici - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index 7a8be474511..18297ecae28 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -114,7 +114,7 @@ hu: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ hu: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ hu: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ hu: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ hu: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ hu: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1556,6 +1560,9 @@ hu: dependencies: Szükséges összetevők activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1564,7 +1571,7 @@ hu: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Megjelenít eddig attachment: @@ -3453,6 +3460,11 @@ hu: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3515,6 +3527,72 @@ hu: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Megbízott @@ -3548,6 +3626,7 @@ hu: invalid_filter: Invalid notification filter label_accessibility: Kisegítő lehetőségek label_account: Felhasználói fiók + label_actions: Műveletek label_active: aktív label_activate_user: Felhasználó aktiválása label_active_in_new_projects: Új projektekben aktív @@ -3588,6 +3667,7 @@ hu: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Visszavonás + label_integrations: Integrations label_add_column: Add column label_applied_status: Alkalmazott státusz label_archive_project: Projekt archiválása @@ -3838,7 +3918,7 @@ hu: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Ugrás a projekthez - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Kulcsszavak label_language_based: A felhasználói nyelv alapján label_last_activity: Utolsó aktivitás @@ -5120,12 +5200,12 @@ hu: setting_welcome_text: Üdvözlő blokk szöveg setting_welcome_title: Üdvözlő blokk címe setting_welcome_on_homescreen: Üdvözlő blokk megjelenítése a kezdőképernyőn - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Alapértelmezett kiemelési mód diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index a8339b34b6f..bf6a584c0c3 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -114,7 +114,7 @@ id: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ id: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ id: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +381,9 @@ id: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ id: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ id: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1522,6 +1526,9 @@ id: dependencies: Dependensi activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1530,7 +1537,7 @@ id: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3343,6 +3350,11 @@ id: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3405,6 +3417,72 @@ id: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Pelimpahan @@ -3436,6 +3514,7 @@ id: invalid_filter: Invalid notification filter label_accessibility: Kemampuan akses label_account: Akun + label_actions: Tindakan label_active: Aktif label_activate_user: Pengguna diaktifkan label_active_in_new_projects: Aktif dalam proyek-proyek baru @@ -3476,6 +3555,7 @@ id: label_ical_access_key_generation_hint: Dibuat secara otomatis saat berlangganan kalender. label_ical_access_key_latest: terbaru label_ical_access_key_revoke: Menarik kembali + label_integrations: Integrations label_add_column: Add column label_applied_status: Status berjalan label_archive_project: Proyek arsip @@ -3726,7 +3806,7 @@ id: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Lompat ke Project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Kata kunci label_language_based: Berdasarkan bahasa user label_last_activity: Aktivitas terakhir @@ -4967,12 +5047,12 @@ id: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 84b397b64cf..fff657a1db3 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -114,7 +114,7 @@ it: import: title: Importa jira: - title: Importazione di Jira + title: Jira Migrator description: Utilizza questo strumento per importare dati dalla tua istanza Jira. Puoi configurare più host Jira e scegliere cosa importare a ogni importazione. errors: cannot_delete_with_imports: Impossibile eliminare l'host Jira con importazioni esistenti @@ -125,8 +125,8 @@ it: title: Configurazione Jira new: Nuova configurazione banner: - title: Importazione limitata - description: 'Questo strumento di importazione è attualmente in versione beta e può importare solo dati di base: progetti, issue (nome, titolo, descrizione, allegati), utenti (nome, email, appartenenza al progetto), stati e tipi. Non può importare flussi di lavoro, campi personalizzati, relazioni tra issue o autorizzazioni. Al momento supportiamo solo le versioni 10.x e 11.x di Jira Server/Data Center. Le istanze cloud non sono supportate al momento.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Nome @@ -162,7 +162,7 @@ it: run: title: Importa esecuzione history: Cronologia - remove_error: Un'importazione di Jira non può essere rimossa mentre è in esecuzione + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Un'altra importazione Jira è attualmente in corso o in attesa di revisione. Completala o annullala prima di iniziare una nuova importazione. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ it: notification_text_default: "

Ciao,

È stato creato un nuovo progetto: projectValue:name

Grazie

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ it: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ it: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Transizioni predefinite @@ -1535,6 +1539,9 @@ it: dependencies: Dipendenze activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Progetti import/jira: @@ -1543,7 +1550,7 @@ it: personal_access_token: Token di accesso personale import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Visualizza fino a attachment: @@ -3392,6 +3399,11 @@ it: quick_add: label: Aggiungi… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: I token provider vengono emessi da OpenProject, consentendo ad altre applicazioni di accedervi. I token client vengono emessi da altre applicazioni, consentendo a OpenProject di accedervi. no_results: @@ -3454,6 +3466,72 @@ it: disabled_text: I token RSS non sono abilitati dall'amministratore. Contatta il tuo amministratore per utilizzare questa funzione. storages: unknown_storage: Archivio sconosciuto + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assegnatario @@ -3485,6 +3563,7 @@ it: invalid_filter: Filtro notifica non valido label_accessibility: Accessibilità label_account: Account + label_actions: Azioni label_active: Attivo label_activate_user: Attiva utente label_active_in_new_projects: Attivo nei nuovi progetti @@ -3525,6 +3604,7 @@ it: label_ical_access_key_generation_hint: Generato automaticamente all'iscrizione a un calendario. label_ical_access_key_latest: più recente label_ical_access_key_revoke: Revoca + label_integrations: Integrations label_add_column: Aggiungi colonna label_applied_status: Stato applicato label_archive_project: Archivia progetto @@ -3775,7 +3855,7 @@ it: label_external_links: Link esterni label_locale: Lingua e paese label_jump_to_a_project: Salta ad altro progetto... - label_jira_import: Importazione di Jira + label_jira_import: Jira Migrator label_keyword_plural: Parole chiave label_language_based: Basato sulla lingua dell'utente label_last_activity: Ultima attività @@ -5029,12 +5109,12 @@ it: setting_welcome_text: Blocco di testo di benvenuto setting_welcome_title: Blocco di testo del titolo setting_welcome_on_homescreen: Mostra il blocco testo di benvenuto nella pagina home - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Modalità evidenziazione predefinita diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index 08fda701046..d9c868dc517 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -114,7 +114,7 @@ ja: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ja: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: 名前 @@ -162,7 +162,7 @@ ja: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -324,13 +324,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: トークンが生成されました @@ -343,21 +343,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クライアントがまだ設定されていません @@ -381,9 +381,9 @@ ja: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ ja: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ ja: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -996,26 +1000,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: @@ -1039,7 +1043,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 @@ -1060,7 +1064,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: ブラウザ @@ -1074,17 +1078,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_html: Member of %{names} and %{count_link}. - more: "%{count} もっと見る" + more: "%{count} 以上" summary_html: Member of %{names}. memberships: no_results_title_text: このユーザは現在プロジェクトのメンバーではありません。 - open_profile: プロフィール + open_profile: プロファイルを開く invite_user_modal: invite: 招待 title: @@ -1201,7 +1205,7 @@ ja: right_to_manage_members_missing: 'プレースホルダーユーザを削除する権限がありません。 プレースホルダー ユーザーがメンバーであるすべてのプロジェクトのメンバーを管理する権利はありません。 ' - delete_tooltip: プレースホルダー・ユーザーの削除 + delete_tooltip: プレースホルダー ユーザーを削除 deletion_info: heading_html: Delete placeholder user %{name} data_consequences: 'プレースホルダー ユーザのすべての発生(担当者、担当者、その他のユーザ値など)は、「削除されたユーザー」というアカウントに再割り当てられます。 削除されたすべてのアカウントのデータがこのアカウントに再割り当てられるため、ユーザーが作成したデータと別の削除されたアカウントのデータを区別することはできません。 @@ -1220,11 +1224,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: 現在、ステータス報告はありません。 @@ -1234,19 +1238,20 @@ ja: status_color_text: | このステータスの色を割り当てたり変更する場合にクリックします。 ステータスボタンに表示され、テーブル内のワークパッケージを強調表示するために使用できます。 - status_default_text: 新しいワークパッケージは、デフォルトでこのタイプに設定される。読み取り専用にはできない。 + status_default_text: 新しいワークパッケージはデフォルトでこのタイプに設定されています。読み取り専用にすることはできません。 status_excluded_from_totals_text: |- - このステータスを持つワークパッケージを、階層内の「作業」、「 - 残作業」、「完了率」の合計から除外するには、このオプションをオンにします。 + このオプションをオンにすると、このステータスのワークパッケージを合計作業量、 + 残作業量、および階層構造で完了させることができます。 status_percent_complete_text_html: |- In [status-based progress calculation mode](setting_url), the % Complete of a work package is automatically set to this value when this status is selected. Ignored in work-based mode. status_readonly_html: | - このステータスを持つワークパッケージを読み取り専用としてマークするには、このオプションをチェックする。 - ステータス以外の属性は変更できません。 + ワークパッケージを読み取り専用としてマークするには、このオプションをオンにしてください。 + ステータスを除いて変更することはできません。 +
- 注意: 継承された値 (子やリレーションなど) は適用されます。 + メモ: 継承された値 (例えば、子や関連) が適用されます。 index: no_results_title_text: 現在、ワークパッケージのステータスはありません。 no_results_content_text: 新しいステータスを追加 @@ -1256,7 +1261,7 @@ ja: is_readonly: 読み取り専用 excluded_from_totals: 合計から除外 themes: - dark: 暗い + dark: ダーク light: ライト sync_with_os: 自動(OSのテーマ設定に追従) types: @@ -1374,15 +1379,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: 'ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + current_type_not_available_in_target_project: 'ターゲット プロジェクトで現在のワークパッケージのタイプが有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 ' - bulk_current_type_not_available_in_target_project: 'ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + bulk_current_type_not_available_in_target_project: '現在のタイプのワークパッケージはターゲット プロジェクトで有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 ' sharing: @@ -1408,9 +1413,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} を追加" @@ -1518,6 +1523,9 @@ ja: dependencies: 依存関係 activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1526,7 +1534,7 @@ ja: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: までを表示 attachment: @@ -3334,6 +3342,11 @@ ja: quick_add: label: 追加 my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3396,6 +3409,72 @@ ja: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: 不明なストレージ + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: 担当者 @@ -3427,6 +3506,7 @@ ja: invalid_filter: 無効な通知フィルター label_accessibility: アクセシビリティ label_account: アカウント + label_actions: 操作 label_active: アクティブ label_activate_user: ユーザーのアクティベート label_active_in_new_projects: 新しいプロジェクトでアクティブ @@ -3467,6 +3547,7 @@ ja: label_ical_access_key_generation_hint: カレンダーを購読するときに自動的に生成されます。 label_ical_access_key_latest: 最新 label_ical_access_key_revoke: 取り消し + label_integrations: Integrations label_add_column: カラムを追加 label_applied_status: 適用されるステータス label_archive_project: プロジェクトをアーカイブ @@ -3717,7 +3798,7 @@ ja: label_external_links: External links label_locale: 言語と地域 label_jump_to_a_project: プロジェクトへ移動... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: キーワード label_language_based: ユーザの言語設定に基づく label_last_activity: 最新の活動 @@ -4962,12 +5043,12 @@ ja: setting_welcome_text: ようこそブロックのテキスト setting_welcome_title: ようこそブロックのタイトル setting_welcome_on_homescreen: ホーム画面にようこそブロックを表示 - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: デフォルトの強調表示モード diff --git a/config/locales/crowdin/js-af.yml b/config/locales/crowdin/js-af.yml index f7b34cc5e9a..98fe7588b6c 100644 --- a/config/locales/crowdin/js-af.yml +++ b/config/locales/crowdin/js-af.yml @@ -592,49 +592,6 @@ af: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Gedelegeerde - responsible: Accountable - shared: Shared - watched: Dophouer - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ af: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Nuus bygevoeg - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ar.yml b/config/locales/crowdin/js-ar.yml index 3eebc12b88a..468530c7949 100644 --- a/config/locales/crowdin/js-ar.yml +++ b/config/locales/crowdin/js-ar.yml @@ -596,49 +596,6 @@ ar: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: المُسند إليه - responsible: Accountable - shared: Shared - watched: المشاهد - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: أنت في الصفحة فقط. pages_skipped: Pages skipped. @@ -662,39 +619,6 @@ ar: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: تم إضافة أحداث - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: تم إضافة صفحة wiki - wiki_page_updated: تم تحديث صفحة wiki - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: هل أنت متأكد؟ text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-az.yml b/config/locales/crowdin/js-az.yml index 76c557aa86b..2d4633ef946 100644 --- a/config/locales/crowdin/js-az.yml +++ b/config/locales/crowdin/js-az.yml @@ -592,49 +592,6 @@ az: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ az: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-be.yml b/config/locales/crowdin/js-be.yml index 34de2459891..c4e874a6234 100644 --- a/config/locales/crowdin/js-be.yml +++ b/config/locales/crowdin/js-be.yml @@ -594,49 +594,6 @@ be: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Прызначаная асоба - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Паведамленні аб актыўнасці ў працоўных пакетах, у якіх вы ўдзельнічаеце (адказны, падсправаздачны або назіральнік). - delayed: - title: Non-participating - description: Дадатковыя паведамленні аб актыўнасці ва ўсіх праектах. - date_alerts: - title: Date alerts - description: Аўтаматычныя паведамленні аб важных падыходзячых датах у адчыняннях працоўных пакетах, у якіх вы ўдзельнічаеце (адказны, падсправаздачны або назіральнік). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: Гэтыя спецыфічныя налады праекта перапісваюць налады па ўмаўчанні вышэй. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -660,39 +617,6 @@ be: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-bg.yml b/config/locales/crowdin/js-bg.yml index f890525f205..7aa82a3b261 100644 --- a/config/locales/crowdin/js-bg.yml +++ b/config/locales/crowdin/js-bg.yml @@ -592,49 +592,6 @@ bg: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Изпълнител - responsible: Accountable - shared: Shared - watched: Наблюдател - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ bg: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Добавени новини - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki страница добавена - wiki_page_updated: Wiki страница обновена - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Сигурни ли сте? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ca.yml b/config/locales/crowdin/js-ca.yml index 28b3fb06363..9212d8758b9 100644 --- a/config/locales/crowdin/js-ca.yml +++ b/config/locales/crowdin/js-ca.yml @@ -109,7 +109,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 @@ -592,49 +592,6 @@ ca: settings: change_notification_settings: Pots modificar la teva configuració de notificacions per assegurar-te que mai et perds informació important. title: Configuració de notificacions - notify_me: Notifica’m - reminders: - no_notification: Sense notificació - timeframes: - normal: - PT0S: el mateix dia - P1D: 1 dia abans - P3D: 3 dies abans - P7D: una setmana abans - overdue: - P1D: cada dia - P3D: cada 3 dies - P7D: cada setmana - reasons: - mentioned: - title: Mencionat - description: Rep una notificació cada vegada que algú em menciona a qualsevol lloc - assignee: Assignat a - responsible: Responsable - shared: Compartida - watched: Observador - work_package_commented: Tots els nous comentaris - work_package_created: Paquets de treball nou - work_package_processed: Tots els canvis d'estat - work_package_prioritized: Tots els canvis de prioritat - work_package_scheduled: Tots els canvis de data - global: - immediately: - title: Participant - description: Notificacions per a totes les activitats en els paquets de treball en els que estàs involucrat (assignat, responsable o observador). - delayed: - title: No participant - description: Notificacions addicionals per activitats en tots els projectes. - date_alerts: - title: Alertes per dates - description: Notificacions automàtiques quan dates importants s'aproximen per a paquets de treball oberts en els quals estàs involucrat (assignat, responsable o observador). - overdue: Quan vençut - project_specific: - title: Configuracions de notificacions específiques del projecte - description: Aquestes configuracions específiques de projecte anul·len les configuracions per defecte especificades a sobre. - add: Afegeix configuracions pel projecte - already_selected: Aquest projecte ja està seleccionat - remove: Elimina les configuracions del projecte pagination: no_other_page: Estàs en la pàgina única. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ ca: required_outside_context: 'Si us plau, selecciona un projecte on crear el paquet de treball per a veure tots els atributs. Només pots seleccionar projectes que tinguin activat la classe indicada anteriorment. ' - reminders: - settings: - daily: - add_time: Afegeix temps - enable: Activa els correus electrònics recordatori diaris - explanation: Rebràs aquests recordatoris per a notificacions no llegides a les hores que especifiquis. %{no_time_zone} - no_time_zone: Fins que no configuris una zona horària per al teu compte, els temps seran interpretats en UTC. - time_label: 'Temps %{counter}:' - title: Envia'm correus electrònics recordatori diaris per a notificacions no llegides. - workdays: - title: Rep recordatoris de correus electrònics recordatori aquests dies - immediate: - title: Envia'm un correu electrònic recordatori - mentioned: Immediatament quan algú em @menciona - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Alertes en correu electrònic per altres elements (que no són paquets de treball) - explanation: 'Ara mateix les notificacions estan limitades als paquets de treball. Pots seleccionar continuar rebent alertes en correus electrònics per aquells esdeveniments fins que s''incloguin a les notificacions: - - ' - news_added: Notícies afegides - news_commented: Comenta a un element de notícia - document_added: Documents afegits - forum_messages: Nou missatges de fòrum - wiki_page_added: S'ha afegit la pàgina wiki - wiki_page_updated: S'ha actualitzat la pàgina wiki - membership_added: Afiliació afegida - membership_updated: Afiliació actualitzada - title: Correus electrònics recordatori - pause: - label: Pausa els correu electrònic recordatori temporalment - first_day: Primer dia - last_day: Últim dia text_are_you_sure: N'esteu segur? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ckb-IR.yml b/config/locales/crowdin/js-ckb-IR.yml index dd8da879f22..d2e5a82cd85 100644 --- a/config/locales/crowdin/js-ckb-IR.yml +++ b/config/locales/crowdin/js-ckb-IR.yml @@ -592,49 +592,6 @@ ckb-IR: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ ckb-IR: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-cs.yml b/config/locales/crowdin/js-cs.yml index 6b8e5cd449d..29b90a29153 100644 --- a/config/locales/crowdin/js-cs.yml +++ b/config/locales/crowdin/js-cs.yml @@ -598,51 +598,6 @@ cs: settings: change_notification_settings: Můžete změnit nastavení oznámení , abyste se ujistili, že nikdy nezmeškáte důležitou aktualizaci. title: Nastavení oznámení - notify_me: Upozornit mě - reminders: - no_notification: Žádné oznámení - timeframes: - normal: - PT0S: Týž den - P1D: před 1 dnem - P3D: 3 dny před - P7D: před týdnem - overdue: - P1D: každý den - P3D: Každé 3 dny - P7D: každý týden - reasons: - mentioned: - title: Zmíněné - description: Dostat upozornění, kdykoli mě někdo kdekoli zmíní - assignee: Řešitel - responsible: Odpovědný - shared: Sdílené - watched: Sledující - work_package_commented: Všechny nové komentáře - work_package_created: Nový pracovní balíček - work_package_processed: Všechny změny stavu - work_package_prioritized: Všechny prioritní změny - work_package_scheduled: Všechny změny data - global: - immediately: - title: Participace - description: Upozornění pro všechny aktivity v pracovních balíčcích, do kterých jste zapojeni (Řešitel, odpovědný nebo sledující). - delayed: - title: Neúčast - description: Další oznámení pro činnosti ve všech projektech. - date_alerts: - title: Upozornění na datum - description: Automatická oznámení, pokud se blíží důležitá data pro otevřené pracovní balíčky, které jste zapojeni (přiřazený, zodpovědný nebo sledovaný). - overdue: Po termínu - project_specific: - title: 'Nastavení upozornění pro konkrétní projekt - - ' - description: Toto specifické nastavení projektu přepíše výchozí nastavení výše. - add: Přidat nastavení pro projekt - already_selected: Tento projekt je již vybrán - remove: Odstranit nastavení projektu pagination: no_other_page: Jste na jediné stránce. pages_skipped: Stránky přeskočeny. @@ -666,39 +621,6 @@ cs: required_outside_context: 'Vyberte projekt pro vytvoření pracovního balíčku, abyste viděli všechny atributy. Můžete vybrat pouze projekty, které mají výše uvedený typ aktivován. ' - reminders: - settings: - daily: - add_time: Přidat čas - enable: Zapnout denní posílání oznámení e-mailem - explanation: Tyto připomínky obdržíte pouze pro nepřečtená oznámení a pouze v hodinách, které zadáte. %{no_time_zone} - no_time_zone: Dokud nenastavíte časové pásmo pro svůj účet, budou časy interpretovány v UTC. - time_label: 'Čas %{counter}:' - title: 'Posílat mi denní souhrn nepřečtených oznámení e-mailem ' - workdays: - title: posílat e-mailem v těchto dnech - immediate: - title: Pošlete mi připomenutí e-mailem - mentioned: V momente když mě někdo @zmiňuje - personal_reminder: Okamžitě po osobní připomenutí - alerts: - title: E-mailová upozornění pro ostatní položky (které nejsou pracovní balíčky) - explanation: 'Oznámení jsou dnes omezena na pracovní balíčky. Můžete se rozhodnout nadále dostávat e-mailová upozornění pro tyto události, dokud nebudou zahrnuta do oznámení: - - ' - news_added: Přidané novinky - news_commented: Komentář k novince - document_added: Dokumenty přidány - forum_messages: Nové zprávy o fóru - wiki_page_added: Přidána stránka wiki - wiki_page_updated: Wiki stránka aktualizována - membership_added: Členství přidáno - membership_updated: Členství bylo aktualizováno - title: E-mailová upozornění - pause: - label: Dočasně pozastavit denní připomenutí e-mailem - first_day: První den - last_day: Poslední den text_are_you_sure: Jste si jisti? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Navigační lišta diff --git a/config/locales/crowdin/js-da.yml b/config/locales/crowdin/js-da.yml index edb28721551..a7fe0238d93 100644 --- a/config/locales/crowdin/js-da.yml +++ b/config/locales/crowdin/js-da.yml @@ -592,49 +592,6 @@ da: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Tildelt - responsible: Accountable - shared: Shared - watched: Tilsynsførende - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ da: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Nyheder tilføjet - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wikiside tilføjet - wiki_page_updated: Wikiside opdateret - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Sikker? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-de.yml b/config/locales/crowdin/js-de.yml index 8638de4479b..54e3d1a9dfb 100644 --- a/config/locales/crowdin/js-de.yml +++ b/config/locales/crowdin/js-de.yml @@ -143,7 +143,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 @@ -460,7 +460,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 @@ -594,49 +594,6 @@ de: settings: change_notification_settings: Sie können Ihre Benachrichtigungseinstellungen ändern, um sicherzustellen, dass Sie keine wichtige Aktualisierung verpassen. title: Benachrichtigungseinstellungen - notify_me: Benachrichtige mich - reminders: - no_notification: Keine Benachrichtigung - timeframes: - normal: - PT0S: am selben Tag - P1D: 1 Tag vorher - P3D: 3 Tage vorher - P7D: eine Woche vorher - overdue: - P1D: jeden Tag - P3D: alle 3 Tage - P7D: jede Woche - reasons: - mentioned: - title: Erwähnt - description: Jedes Mal eine Benachrichtigung erhalten, wenn ich irgendwo erwähnt werde - assignee: Zugewiesen an - responsible: Verantwortlich - shared: Geteilt - watched: Beobachter - work_package_commented: Alle neuen Kommentare - work_package_created: Neue Arbeitspakete - work_package_processed: Änderungen am Status - work_package_prioritized: Änderungen der Priorität - work_package_scheduled: Alle Datumsänderungen - global: - immediately: - title: Beteiligt - description: Benachrichtigungen für alle Aktivitäten in Arbeitspaketen, an denen Sie beteiligt sind (Zugewiesen an, Verantwortlicher oder Beobachter). - delayed: - title: Nicht beteiligt - description: Zusätzliche Benachrichtigungen für Aktivitäten in allen Projekten. - date_alerts: - title: Datums-Erinnerungen - description: Automatische Benachrichtigungen, wenn wichtige Termine für offene Arbeitspakete bevorstehen, an denen Sie beteiligt (z. B. zugewiesen, verantwortlich oder Beobachter) sind. - overdue: Wenn überfällig - project_specific: - title: Projektspezifische Benachrichtigungseinstellungen - description: Diese projektspezifischen Einstellungen überschreiben die Standardeinstellungen oben - add: Einstellung für Projekt hinzufügen - already_selected: Dieses Projekt ist bereits ausgewählt - remove: Projektspezifische Einstellungen entfernen pagination: no_other_page: Sie befinden sich auf der einzigen Seite. pages_skipped: Seiten übersprungen. @@ -660,39 +617,6 @@ de: required_outside_context: 'Bitte wählen Sie ein Projekt für das Arbeitspaket, um alle Attribute anzuzeigen. Sie können nur Projekte auswählen, für die der ausgewählte Typ oben aktiviert ist. ' - reminders: - settings: - daily: - add_time: Zeit hinzufügen - enable: Tägliche E-Mail-Erinnerungen aktivieren - explanation: Sie erhalten diese Erinnerungen nur für ungelesene Benachrichtigungen und nur zu Uhrzeiten, die Sie angegeben haben. %{no_time_zone} - no_time_zone: Bis Sie eine Zeitzone für Ihr Konto konfigurieren, sind diese Zeiten als UTC (GMT+0) zu verstehen. - time_label: 'Zeit %{counter}:' - title: Tägliche E-Mail-Erinnerungen für ungelesene Benachrichtigungen zusenden - workdays: - title: E-Mail-Erinnerungen an diesen Tagen erhalten - immediate: - title: Eine E-Mail Erinnerung an mich senden - mentioned: Sofort, wenn mich jemand @erwähnt - personal_reminder: Sofort, wenn ich eine persönliche Erinnerung erhalte - alerts: - title: E-Mail-Benachrichtigungen für andere Objekte (die keine Arbeitspakete sind) - explanation: 'Die Benachrichtigungen sind aktuell auf Arbeitspakete beschränkt. Sie können die E-Mail-Benachrichtigungen für diese Ereignisse so lange erhalten, bis sie in den Benachrichtigungen enthalten sind: - - ' - news_added: Neuigkeiten hinzugefügt - news_commented: Kommentar zu einer Neuigkeit - document_added: Dokumente hinzugefügt - forum_messages: Neue Nachrichten im Forum - wiki_page_added: Wiki-Seite hinzugefügt - wiki_page_updated: Wiki-Seite aktualisiert - membership_added: Mitgliedschaft hinzugefügt - membership_updated: Mitgliedschaft aktualisiert - title: E-Mail-Erinnerungen - pause: - label: Tägliche E-Mail-Erinnerungen vorübergehend pausieren - first_day: Erster Tag - last_day: Letzter Tag text_are_you_sure: Sind Sie sicher? text_are_you_sure_to_cancel: Sie haben ungespeicherte Änderungen auf dieser Seite. Sind Sie sicher, dass Sie diese verwerfen möchten? breadcrumb: Navigationspfad @@ -872,7 +796,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 @@ -1070,7 +994,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-el.yml b/config/locales/crowdin/js-el.yml index b0d35cfaa7a..c283211a09f 100644 --- a/config/locales/crowdin/js-el.yml +++ b/config/locales/crowdin/js-el.yml @@ -592,49 +592,6 @@ el: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Ανάθεση σε - responsible: Υπόλογος - shared: Shared - watched: Παρατηρητής - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Βρίσκεστε στη μοναδική σελίδα. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ el: required_outside_context: 'Παρακαλούμε επιλέξτε ένα έργο μέσα στο οποίο θα δημιουργήσετε το πακέτο εργασίας για να δείτε όλα τα χαρακτηριστικά. Μπορείτε να επιλέξετε μόνο έργα που έχουν τον παραπάνω τύπο ενεργό. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Νέα προστέθηκαν - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Η σελίδα Wiki προστέθηκε - wiki_page_updated: Η σελίδα Wiki ενημερώθηκε - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Είστε σίγουρος/η; text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-eo.yml b/config/locales/crowdin/js-eo.yml index 0dbf981dd45..cef526628dc 100644 --- a/config/locales/crowdin/js-eo.yml +++ b/config/locales/crowdin/js-eo.yml @@ -592,49 +592,6 @@ eo: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Asignita al - responsible: Respondeculo - shared: Shared - watched: Atentanto - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ eo: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Novaĵoj aldonitaj - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-es.yml b/config/locales/crowdin/js-es.yml index 4ed14c8dcf1..caa979fb181 100644 --- a/config/locales/crowdin/js-es.yml +++ b/config/locales/crowdin/js-es.yml @@ -590,49 +590,6 @@ es: settings: change_notification_settings: Puede modificar su configuración de notificaciones para asegurarse que nunca se pierda una actualización importante. title: Ajustes de notificación - notify_me: Notifícame - reminders: - no_notification: Sin notificación - timeframes: - normal: - PT0S: mismo día - P1D: 1 día antes - P3D: 3 días antes - P7D: una semana antes - overdue: - P1D: todos los días - P3D: cada 3 días - P7D: todas las semanas - reasons: - mentioned: - title: Mencionado - description: Recibir una notificación cada vez que un usuario me mencione - assignee: Asignado a - responsible: Responsable - shared: Compartido - watched: Observador - work_package_commented: Todos los nuevos comentarios - work_package_created: Nuevos paquetes de trabajo - work_package_processed: Todos los cambios de estado - work_package_prioritized: Todos los cambios de prioridad - work_package_scheduled: Todos los cambios de fecha - global: - immediately: - title: Participado - description: Notificaciones para todas las actividades en paquetes de trabajo en los que usted está involucrado (asignado, responsable u observador). - delayed: - title: No participando - description: Notificaciones adicionales para actividades en todos los proyectos. - date_alerts: - title: Alertas de fecha - description: Notificaciones automáticas cuando se acercan fechas importantes para paquetes de trabajo abiertos en los que está involucrado (asignado, responsable u observador). - overdue: Cuando vencido - project_specific: - title: Configuración de notificaciones específicas de proyectos - description: Esta configuración específica de proyectos reemplaza los ajustes predeterminados anteriores. - add: Añadir configuración para el proyecto - already_selected: Este proyecto ya está seleccionado - remove: Eliminar configuración del proyecto pagination: no_other_page: Usted está en la única página. pages_skipped: Páginas omitidas. @@ -656,39 +613,6 @@ es: required_outside_context: 'Seleccione el proyecto donde quiere crear el paquete de trabajo para ver todos los atributos. Solo puede seleccionar proyectos que tengan activado el tipo de paquete utilizado. ' - reminders: - settings: - daily: - add_time: Añadir tiempo - enable: Habilitar recordatorios por correo electrónico diarios - explanation: Solo recibirá estos recordatorios para las notificaciones sin leer y en las horas que especifique. %{no_time_zone} - no_time_zone: Hasta que configure una zona horaria para su cuenta, las horas se interpretarán en UTC. - time_label: 'Tiempo %{counter}:' - title: Enviarme recordatorios por correo electrónico diarios con las notificaciones sin leer - workdays: - title: Recibir recordatorios por correo electrónico en estos días - immediate: - title: Enviarme un recordatorio por correo electrónico - mentioned: De inmediato cuando alguien me @mencione - personal_reminder: Inmediatamente al recibir un recordatorio personal - alerts: - title: Alertas por correo electrónico de otros elementos (que no sean paquetes de trabajo) - explanation: 'Las notificaciones por ahora están limitadas a los paquetes de trabajo. Puede elegir seguir recibiendo alertas por correo electrónico de estos eventos hasta que se incluyan en las notificaciones: - - ' - news_added: Noticias añadidas - news_commented: Comentarios en un elemento de noticias - document_added: Documentos añadidos - forum_messages: Nuevos mensajes en foro - wiki_page_added: Página wiki añadida - wiki_page_updated: Página wiki actualizada - membership_added: Subscripción añadida - membership_updated: Subscripción actualizada - title: Recordatorios por correo electrónico - pause: - label: Pausar temporalmente los recordatorios por correo electrónico diarios - first_day: Primer día - last_day: Último día text_are_you_sure: "¿Estás seguro?" text_are_you_sure_to_cancel: Tiene cambios sin guardar en esta página. ¿Seguro que quiere descartarlos? breadcrumb: Ruta de navegación diff --git a/config/locales/crowdin/js-et.yml b/config/locales/crowdin/js-et.yml index 2c43842063e..0672928caf3 100644 --- a/config/locales/crowdin/js-et.yml +++ b/config/locales/crowdin/js-et.yml @@ -592,49 +592,6 @@ et: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mainitud - description: Receive a notification every time someone mentions me anywhere - assignee: Määratud tegija - responsible: Accountable - shared: Jagatud - watched: Jälgija - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ et: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Uudis lisatud - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Viki leht on lisatud - wiki_page_updated: Viki leht on uuendatud - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Kas oled kindel? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-eu.yml b/config/locales/crowdin/js-eu.yml index d5a00cb840d..911b2ca0191 100644 --- a/config/locales/crowdin/js-eu.yml +++ b/config/locales/crowdin/js-eu.yml @@ -592,49 +592,6 @@ eu: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ eu: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-fa.yml b/config/locales/crowdin/js-fa.yml index ef7ba039e6f..f1b1aaa85d7 100644 --- a/config/locales/crowdin/js-fa.yml +++ b/config/locales/crowdin/js-fa.yml @@ -592,49 +592,6 @@ fa: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: نماینده - responsible: Accountable - shared: Shared - watched: ناظر - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: اطلاعیه (اعلان) برای تمام فعالیت‌ها در پکیج‌های کاری که در آن‌ها شرکت دارید (به عنوان مسئول، متولی یا تماشاگر). - delayed: - title: Non-participating - description: اعلان‌های اضافی برای فعالیت‌ها در تمام پروژه‌ها - date_alerts: - title: Date alerts - description: اعلان‌های خودکار هنگام نزدیک شدن تاریخ‌های مهم برای پکیج‌های کاری باز که در آن‌ها شرکت دارید (به عنوان مسئول، متولی یا تماشاگر). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: این تنظیمات خاص پروژه تنظیمات پیش‌فرض بالا را بازنویسی می‌کنند. - add: Add setting for project - already_selected: این پروژه قبلاً انتخاب شده است - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ fa: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: آیا مطمئن هستید؟ text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-fi.yml b/config/locales/crowdin/js-fi.yml index 053f8f05791..e9aae948d58 100644 --- a/config/locales/crowdin/js-fi.yml +++ b/config/locales/crowdin/js-fi.yml @@ -592,49 +592,6 @@ fi: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Työn suorittaja - responsible: Vastuuhenkilö - shared: Shared - watched: Seuraajat - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Olet jo ainoalla sivulla. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ fi: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Uutinen lisätty - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki-sivu lisätty - wiki_page_updated: Wiki-sivu päivitetty - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Oletko varma? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-fil.yml b/config/locales/crowdin/js-fil.yml index f14b44cfc35..1b73ab6a0a0 100644 --- a/config/locales/crowdin/js-fil.yml +++ b/config/locales/crowdin/js-fil.yml @@ -592,49 +592,6 @@ fil: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Naitalaga - responsible: Accountable - shared: Shared - watched: Tagapagmasid - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Ikaw ay nasa pahina lamang. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ fil: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Bagong idinagdag - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Ang idinagdag na wiking pahina - wiki_page_updated: Naka-update na wiking pahina - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Sigurado ka ba? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-fr.yml b/config/locales/crowdin/js-fr.yml index 28213a57b9c..ae89e04dc84 100644 --- a/config/locales/crowdin/js-fr.yml +++ b/config/locales/crowdin/js-fr.yml @@ -592,49 +592,6 @@ fr: settings: change_notification_settings: Vous pouvez modifier vos paramètres de notification pour vous assurer de ne jamais manquer une mise à jour importante. title: Paramètres de notifications - notify_me: M'alerter - reminders: - no_notification: Aucune notification - timeframes: - normal: - PT0S: le jour même - P1D: 1 jour avant - P3D: 3 jours avant - P7D: une semaine avant - overdue: - P1D: tous les jours - P3D: tous les 3 jours - P7D: toutes les semaines - reasons: - mentioned: - title: Mentionné - description: Recevoir une notification chaque fois que quelqu'un me mentionne n'importe où - assignee: Assigné à - responsible: Responsable - shared: Partagé - watched: Observateur - work_package_commented: Tous les nouveaux commentaires - work_package_created: Nouveaux lots de travaux - work_package_processed: Tous les changements de statut - work_package_prioritized: Tous les changements de priorité - work_package_scheduled: Tous les changements de date - global: - immediately: - title: Participant - description: Notifications pour toute activité sur les lots de travaux vous concernant (assigné, responsable ou observateur). - delayed: - title: Non participant - description: Notifications additionnelles lors d'activités sur tous les projets. - date_alerts: - title: Alertes de date - description: Notifications automatiques lorsque des dates importantes approchent pour les lots de travaux ouverts vous concernant (assigné, responsable ou observateur). - overdue: En cas de retard - project_specific: - title: Paramètres de notification spécifiques au projet - description: Ces paramètres spécifiques au projet remplacent les paramètres par défaut ci-dessus. - add: Ajouter un paramètre pour le projet - already_selected: Ce projet est déjà sélectionné - remove: Suppression des paramètres de projet pagination: no_other_page: Vous êtes sur la seule page. pages_skipped: Pages ignorées. @@ -658,39 +615,6 @@ fr: required_outside_context: 'Veuillez choisir un projet pour créer le lot de travaux et voir tous les attributs. Vous pouvez seulement sélectionner des projets ayant le type ci-dessus activé. ' - reminders: - settings: - daily: - add_time: Ajouter une heure - enable: Activer les rappels quotidiens par e-mail - explanation: Vous ne recevrez ces rappels que pour les notifications non lues et seulement aux heures que vous spécifiez. %{no_time_zone} - no_time_zone: Jusqu'à ce que vous configurez un fuseau horaire pour votre compte, les temps seront interprétés en UTC. - time_label: 'Temps %{counter} :' - title: Envoyez-moi des rappels quotidiens par e-mail pour les notifications non lues - workdays: - title: Recevoir des rappels par e-mail ces jours - immediate: - title: Envoyez-moi un rappel par e-mail - mentioned: Immédiatement quand quelqu'un me @mentionne - personal_reminder: Immédiatement lorsque je reçois un rappel personnel - alerts: - title: Alertes par e-mail pour les autres éléments (qui ne sont pas des lots de travaux) - explanation: 'Les notifications d''aujourd''hui sont limitées aux lots de travaux. Vous pouvez choisir de continuer à recevoir des alertes par e-mail pour ces événements jusqu''à ce qu''elles soient incluses dans les notifications: - - ' - news_added: Actualités ajoutées - news_commented: Commenter sur une actualité - document_added: Documents ajoutés - forum_messages: Nouveaux messages du forum - wiki_page_added: Page wiki ajoutée - wiki_page_updated: Page wiki mise à jour - membership_added: Adhésion ajoutée - membership_updated: Adhésion mise à jour - title: Rappels par e-mail - pause: - label: Mettre temporairement en pause les rappels quotidiens par e-mail - first_day: Premier jour - last_day: Dernier jour text_are_you_sure: Êtes-vous sûr ? text_are_you_sure_to_cancel: Vous avez des modifications non enregistrées sur cette page. Voulez-vous vraiment les supprimer ? breadcrumb: Fil d'Ariane diff --git a/config/locales/crowdin/js-he.yml b/config/locales/crowdin/js-he.yml index 71b534d073a..94172754d34 100644 --- a/config/locales/crowdin/js-he.yml +++ b/config/locales/crowdin/js-he.yml @@ -594,49 +594,6 @@ he: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: משויך אל - responsible: Accountable - shared: Shared - watched: צופה - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -660,39 +617,6 @@ he: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: האם הינך בטוח? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-hi.yml b/config/locales/crowdin/js-hi.yml index c27c925e6e5..5b169f5f9c5 100644 --- a/config/locales/crowdin/js-hi.yml +++ b/config/locales/crowdin/js-hi.yml @@ -592,49 +592,6 @@ hi: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: अनुदिष्ट - responsible: जवाबदेह - shared: Shared - watched: वॉचर - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ hi: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: विकी पृष्ठ अद्यतित - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-hr.yml b/config/locales/crowdin/js-hr.yml index ac3ad1133a1..ad9cefa6dba 100644 --- a/config/locales/crowdin/js-hr.yml +++ b/config/locales/crowdin/js-hr.yml @@ -593,49 +593,6 @@ hr: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Opunomoćeno - responsible: Accountable - shared: Shared - watched: Nadglednik - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -659,39 +616,6 @@ hr: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Vijest dodana - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki stranica dodana - wiki_page_updated: Wiki stranica ažurirana - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Da li ste sigurni? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-hu.yml b/config/locales/crowdin/js-hu.yml index 628eef119fd..47e5525831c 100644 --- a/config/locales/crowdin/js-hu.yml +++ b/config/locales/crowdin/js-hu.yml @@ -626,49 +626,6 @@ hu: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Értesítési beállítások - notify_me: Értesítést kérek - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Megemlített - description: Értesítés fogadása minden alkalommal, amikor bárki bárhol megemlít - assignee: Megbízott - responsible: Felelős - shared: Megosztva - watched: Megfigyelő - work_package_commented: Új megjegyzések - work_package_created: Új munkacsomagok - work_package_processed: Összes státusz változás - work_package_prioritized: Összes prioritás változás - work_package_scheduled: Összes dátum változás - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Projekt-specifikus értesítések beállításai - description: These project-specific settings override default settings above. - add: Beállítás hozzáadása a projekthez - already_selected: A projekt már ki lett választva - remove: Projekt beállítások eltávolítása pagination: no_other_page: Csak egyetlen oldalon tartózkodik. pages_skipped: Pages skipped. @@ -692,47 +649,6 @@ hu: required_outside_context: 'Kérjük, válasszon egy projektet a munkacsomag létrehozásához az összes attribútum megtekintéséhez. Csak olyan projekteket választhat ki, amelyek aktiválták a fenti típust. ' - reminders: - settings: - daily: - add_time: 'Idő hozzáadása - - ' - enable: 'Napi e-mail emlékeztetők engedélyezése - - ' - explanation: 'Ezeket az emlékeztetőket csak az olvasatlan értesítésekről kapja, és csak az Ön által megadott órákban. %{no_time_zone} - - ' - no_time_zone: Amíg a fiókjához nincs időzóna beállítva, addig az időpontok UTC szerint értendők. - time_label: 'Idő%{counter}:' - title: 'Napi e-mail emlékeztető küldése az olvasatlan értesítésekről - - ' - workdays: - title: Email emlékeztetők fogadása ezeken a napokon - immediate: - title: Kérek email emlékeztetőt! - mentioned: Amint valaki @megemlít engem - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Hírek hozzáadva - news_commented: Megjegyzés egy hír elemhez - document_added: Dokumentum hozzáadva - forum_messages: Új fórum üzenetek - wiki_page_added: Wiki oldal hozzáadva - wiki_page_updated: Wiki oldal frissítve - membership_added: Felhasználó hozzáadva - membership_updated: Felhasználó frissítve - title: E-mail emlékeztetők - pause: - label: Napi email emlékeztetők ideiglenes szüneteltetése - first_day: Első nap - last_day: Utolsó nap text_are_you_sure: Biztos vagy benne? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-id.yml b/config/locales/crowdin/js-id.yml index a20613bbfef..425dda19169 100644 --- a/config/locales/crowdin/js-id.yml +++ b/config/locales/crowdin/js-id.yml @@ -591,49 +591,6 @@ id: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Pelimpahan - responsible: Akuntabel - shared: Shared - watched: Pemantau - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -657,39 +614,6 @@ id: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News ditambahkan - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Halaman Wiki ditambahkan - wiki_page_updated: Halaman Wiki diupdate - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Apakah anda yakin? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-it.yml b/config/locales/crowdin/js-it.yml index 3521398ec66..803d3ddf105 100644 --- a/config/locales/crowdin/js-it.yml +++ b/config/locales/crowdin/js-it.yml @@ -592,49 +592,6 @@ it: settings: change_notification_settings: Puoi modificare le tue impostazioni di notifica per assicurarti di non perderti mai un aggiornamento importante. title: Impostazioni notifiche - notify_me: Avvisami - reminders: - no_notification: Nessuna notifica - timeframes: - normal: - PT0S: stesso giorno - P1D: 1 giorno prima - P3D: 3 giorni prima - P7D: una settimana prima - overdue: - P1D: ogni giorno - P3D: ogni 3 giorni - P7D: ogni settimana - reasons: - mentioned: - title: Menzionato - description: Ricevi una notifica ogni volta che qualcuno ti menziona ovunque - assignee: Assegnatario - responsible: Responsabile - shared: Condiviso - watched: Osservatore - work_package_commented: Tutti i nuovi commenti - work_package_created: Nuove macro-attività - work_package_processed: Tutte le modifiche di stato - work_package_prioritized: Tutte le modifiche prioritarie - work_package_scheduled: Tutte le modifiche della data - global: - immediately: - title: Partecipazione - description: Notifiche per tutte le attività nelle macro-attività in cui sei coinvolto (assegnatario, responsabile od osservatore) - delayed: - title: Non partecipante - description: Notifiche aggiuntive sulle attività in tutti i progetti - date_alerts: - title: Avvisi data - description: Notifiche automatiche quando si avvicinano date importanti per le macro-attività aperte in cui sei coinvolto (assegnatario, responsabile od osservatore) - overdue: Alla scadenza - project_specific: - title: Impostazioni di notifica specifiche del progetto - description: Queste impostazioni specifiche del progetto sostituiscono le impostazioni predefinite sopra. - add: Aggiungi impostazione per il progetto - already_selected: Questo progetto è già selezionato - remove: Rimuovi impostazioni progetto pagination: no_other_page: Sei nella pagina unica. pages_skipped: Pagine saltate. @@ -658,39 +615,6 @@ it: required_outside_context: 'Scegli un progetto per creare una macro-attività e vederne tutti gli attributi. Puoi selezionare solo progetti che hanno il tipo sopra indicato attivato. ' - reminders: - settings: - daily: - add_time: Aggiungi tempo - enable: Abilita promemoria email giornalieri - explanation: Riceverai questi promemoria solo per le notifiche non lette e solo nelle ore specificate. %{no_time_zone} - no_time_zone: Fino a quando non configuri un fuso orario per il tuo account, gli orari verranno interpretati in UTC. - time_label: 'Tempo %{counter}:' - title: Inviami promemoria email giornalieri per le notifiche non lette - workdays: - title: Ricevi promemoria email in questi giorni - immediate: - title: Inviami un promemoria email - mentioned: Subito quando qualcuno mi @menziona - personal_reminder: Immediatamente quando ricevo un promemoria personale - alerts: - title: Avvisi email per altri elementi (che non sono macro-attività) - explanation: 'Le notifiche oggi sono limitate ai pacchetti di lavoro. Puoi scegliere di continuare a ricevere avvisi email per questi eventi finché non vengono inclusi nelle notifiche: - - ' - news_added: Notizie aggiunte - news_commented: Commento su un articolo di notizie - document_added: Documenti aggiunti - forum_messages: Nuovi messaggi del forum - wiki_page_added: Pagina wiki aggiunta - wiki_page_updated: Pagina wiki aggiornata - membership_added: Iscrizione aggiunta - membership_updated: Iscrizione aggiornata - title: Promemoria email - pause: - label: Sospendi temporaneamente i promemoria email giornalieri - first_day: 'Primo giorno ' - last_day: Ultimo giorno text_are_you_sure: Sei sicuro/a? text_are_you_sure_to_cancel: Ci sono modifiche non salvate in questa pagina. Vuoi davvero eliminarle? breadcrumb: Percorso di navigazione diff --git a/config/locales/crowdin/js-ja.yml b/config/locales/crowdin/js-ja.yml index a28fa088b5a..6a67a4bdf79 100644 --- a/config/locales/crowdin/js-ja.yml +++ b/config/locales/crowdin/js-ja.yml @@ -34,12 +34,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: @@ -71,7 +71,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: 確認 @@ -79,7 +79,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: ウォッチャーを削除 @@ -101,7 +101,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: その他の操作 @@ -111,7 +111,7 @@ ja: button_uncheck_all: 全てを選択解除 button_update: 更新 button_export-atom: Atomをダウンロード - button_generate_pdf: PDFを生成 + button_generate_pdf: PDF作成 button_create: 作成 card: add_new: 新規カード追加 @@ -145,8 +145,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}' @@ -159,7 +159,7 @@ ja: attribute_reference: macro_help_tooltip: このテキストセグメントはマクロによって動的にレンダリングされています。 not_found: 要求されたリソースが見つかりませんでした - nested_macro: このマクロは %{model} %{id} を再帰的に参照しています。 + nested_macro: このマクロは %{model} %{id}を再帰的に参照している。 invalid_attribute: 選択した属性 '%{name}' は存在しません。 child_pages: button: 子ページへのリンク @@ -216,10 +216,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: 営業日とみなす曜日を変更すると、このサイト内のすべてのプロジェクトのすべてのワークパッケージの開始日と終了日に影響を与える可能性があります。 @@ -301,14 +301,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} @@ -321,7 +321,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: すべてのプロジェクト @@ -432,7 +432,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: すべての属性を表示 @@ -470,8 +470,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: アップロードした人 @@ -502,7 +502,7 @@ ja: label_version_plural: バージョン label_view_has_changed: このビューには未保存の変更があります。 クリックすると保存します。 help_texts: - show_modal: ヘルプテキストを表示 + show_modal: ヘルプテキストを表示する onboarding: buttons: skip: スキップ @@ -510,7 +510,7 @@ ja: got_it: 了承 steps: help_menu: ヘルプ(?)メニューは、その他のヘルプリソースを提供します。ここでは、ユーザーガイド、役立つハウツービデオなどを見つけることができます。
OpenProjectでの作業をお楽しみください! - members: 新しい メンバー をプロジェクトに招待します。 + members: 新しいメンバーをプロジェクトに招待する。 quick_add_button: ヘッダーナビゲーションにあるプラス(+)アイコンをクリックして、新規プロジェクトを作成したり、同僚を招待したりできます。 sidebar_arrow: プロジェクトのメインメニューに戻るには、左上の矢印を使います。 welcome: 3分間のイントロダクションツアーで、最も重要な機能を学びましょう。
最後までステップを完了することをお勧めします。ツアーはいつでも再開できます。 @@ -591,57 +591,14 @@ ja: settings: change_notification_settings: 通知設定を変更することで、重要なアップデートを見逃すことはありません。 title: 通知設定 - notify_me: 通知する - reminders: - no_notification: 通知なし - timeframes: - normal: - PT0S: 同じ日 - P1D: 1 日前 - P3D: 3 日前 - P7D: 週間前 - overdue: - P1D: 毎日 - P3D: 3日ごと - P7D: 毎週 - reasons: - mentioned: - title: メンションされた - description: メンションされたときに通知を受け取る - assignee: 担当者 - responsible: 責任者 - shared: 共有 - watched: ウォッチャー - work_package_commented: すべての新着コメント - work_package_created: 新しいワークパッケージ - work_package_processed: すべてのステータス変更 - work_package_prioritized: すべての優先度の変更 - work_package_scheduled: すべての日付の変更 - global: - immediately: - title: 参加 - description: 自分が関与しているワークパッケージのすべてのアクティビティに関する通知(アサイニー、アカウンタブル、ウォッチャー)。 - delayed: - title: 不参加 - description: すべてのプロジェクトでのアクティビティの追加通知。 - date_alerts: - title: 日付アラート - description: あなたが関与している(アサイニー、アカウンタブル、ウォッチャー)オープンワークパッケージの重要な日付が近づくと自動通知。 - overdue: 期限を過ぎた場合 - project_specific: - title: プロジェクト固有の通知設定 - description: これらのプロジェクト固有の設定は、上記のデフォルト設定を上書きする。 - add: プロジェクトの設定を追加する - 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: ここにタイトルを入力します @@ -651,45 +608,12 @@ ja: project: autocompleter: label: プロジェクト名の入力補完 - click_to_switch_to_project: 'プロジェクト: %{projectname}' + click_to_switch_to_project: プロジェクト: %{projectname} context: プロジェクトのコンテキスト not_available: プロジェクトなし required_outside_context: 'ワークパッケージを作成するプロジェクトを選択して、すべての属性を確認してください。 上記で有効になっているタイプのプロジェクトのみ選択できます。 ' - reminders: - settings: - daily: - add_time: 時間を追加 - enable: 毎日のEメールリマインダーを有効にする - explanation: このリマインダーは、未読の通知に対してのみ、指定した時間帯にのみ届きます。 %{no_time_zone} - no_time_zone: アカウントにタイムゾーンを設定するまでは、時間はUTCで解釈されます。 - time_label: 時間 %{counter}: - title: 未読の通知を毎日メールで通知する - workdays: - title: これらの日にリマインダーメールを受け取る - immediate: - title: 電子メールのリマインダーを送信 - mentioned: "@mentionするとすぐに" - personal_reminder: 個人的なリマインダーを受け取ったら直ちに - alerts: - title: その他の項目(ワークパッケージではないもの)に対する電子メールアラート - explanation: '本日の通知はワークパッケージに限定されています。これらのイベントが通知に含まれるようになるまで、Eメールアラートを受信し続けることを選択できます: - - ' - news_added: ニュースが追加されました。 - news_commented: ニュースへのコメント - document_added: 追加された書類 - forum_messages: 新しいフォーラムメッセージ - wiki_page_added: Wikiページが追加されました。 - wiki_page_updated: Wikiページが更新されました。 - membership_added: メンバーシップが追加されました - membership_updated: メンバーシップ更新 - title: 電子メールによるリマインダー - pause: - label: 毎日のEメールリマインダーを一時停止する - first_day: 初日 - last_day: 最終日 text_are_you_sure: よろしいですか? text_are_you_sure_to_cancel: このページには未保存の変更があります。本当に破棄しますか? breadcrumb: パンくず @@ -1176,7 +1100,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-ka.yml b/config/locales/crowdin/js-ka.yml index c0e2885ad6d..e063b4895eb 100644 --- a/config/locales/crowdin/js-ka.yml +++ b/config/locales/crowdin/js-ka.yml @@ -592,49 +592,6 @@ ka: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: შემატყობინე - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: იგივე დღე - P1D: 1 დღით ადრე - P3D: 3 დღით ადრე - P7D: 1 კვირით ადრე - overdue: - P1D: ყოველდღე - P3D: ყოველ 3 დღეში - P7D: ყოველკვირა - reasons: - mentioned: - title: მოხსენიებულები - description: Receive a notification every time someone mentions me anywhere - assignee: დამსაქმებელი - responsible: ანგარიშვალდებული - shared: გაზიარებული - watched: მეთვალყურე - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: მონაწილეობა - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: განგაშების თარიღი - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: როცა ვადა გადაცილდება - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ ka: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: დროის დამატება - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: სიახლე დამატებულია - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: პირველი დღე - last_day: ბოლო დღე text_are_you_sure: დარწმუნებული ბრძანდებით? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-kk.yml b/config/locales/crowdin/js-kk.yml index 67ca4f89984..f6e337650a8 100644 --- a/config/locales/crowdin/js-kk.yml +++ b/config/locales/crowdin/js-kk.yml @@ -592,49 +592,6 @@ kk: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ kk: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ko.yml b/config/locales/crowdin/js-ko.yml index ed4186c89c7..61a1a16bb82 100644 --- a/config/locales/crowdin/js-ko.yml +++ b/config/locales/crowdin/js-ko.yml @@ -591,49 +591,6 @@ ko: settings: change_notification_settings: 중요 업데이트를 놓치지 않도록 알림 설정을 수정할 수 있습니다. title: 알림 설정 - notify_me: 알림 받기 - reminders: - no_notification: 알림 없음 - timeframes: - normal: - PT0S: 같은 날 - P1D: 1일 전 - P3D: 3일 전 - P7D: 1주 전 - overdue: - P1D: 매일 - P3D: 3일마다 - P7D: 매주 - reasons: - mentioned: - title: 멘션됨 - description: 어디에서든 누군가가 나를 멘션할 때마다 알림 받기 - assignee: 담당자 - responsible: 책임자 - shared: 공유됨 - watched: 주시자 - work_package_commented: 모든 새로운 코멘트 - work_package_created: 새 작업 패키지 - work_package_processed: 모든 상태 변경 사항 - work_package_prioritized: 모든 우선 순위 변경 사항 - work_package_scheduled: 모든 날짜 변경 사항 - global: - immediately: - title: 참여 - description: 귀하가 참여하고 있는 작업 패키지의 모든 활동에 대한 알림(담당자, 책임자 또는 주시자). - delayed: - title: 참여하지 않음 - description: 모든 프로젝트의 활동에 대한 추가 알림. - date_alerts: - title: 날짜 경보 - description: 귀하가 참여하고 있는 오픈 작업 패키지에 대한 중요 날짜가 다가올 때 자동 알림(담당자, 책임자 또는 주시자). - overdue: 기한이 지난 경우 - project_specific: - title: 프로젝트별 알림 설정 - description: 이러한 프로젝트별 설정은 위의 기본 설정을 재정의합니다. - add: 프로젝트에 대한 설정 추가 - already_selected: 이 프로젝트는 이미 선택되었습니다. - remove: 프로젝트 설정 제거 pagination: no_other_page: 유일한 페이지에 있습니다. pages_skipped: 페이지를 건너뛰었습니다. @@ -657,39 +614,6 @@ ko: required_outside_context: '모든 특성을 보려면 작업 패키지를 만들 프로젝트를 선택하십시오. 위 유형이 활성화된 프로젝트만 선택할 수 있습니다. ' - reminders: - settings: - daily: - add_time: 시간 추가 - enable: 일일 이메일 미리 알림 사용 - explanation: 읽지 않은 알림에 대해서만 그리고 지정된 시간에만, 이러한 미리 알림이 전송됩니다. %{no_time_zone} - no_time_zone: 계정의 시간대를 구성할 때까지 시간이 UTC로 표시됩니다. - time_label: '시간 %{counter}:' - title: 읽지 않은 알림에 대해 내게 일일 이메일 미리 알림 보내기 - workdays: - title: 해당 요일에 이메일 미리 알림 받기 - immediate: - title: 내게 이메일 미리 알림 보내기 - mentioned: 누군가 나를 @멘션할 때 즉시 - personal_reminder: 개인 미리 알림을 받는 즉시 - alerts: - title: 기타 항목에 대한 이메일 알림(작업 패키지 외) - explanation: '오늘 알림은 작업 패키지로 제한됩니다. 알림에 포함될 때까지 이러한 이벤트에 대한 이메일 알림을 계속 받도록 선택할 수 있습니다. - - ' - news_added: 뉴스 추가됨 - news_commented: 뉴스 항목의 코멘트 - document_added: 문서 추가됨 - forum_messages: 새 포럼 메시지 - wiki_page_added: 위키 페이지 추가됨 - wiki_page_updated: 위키 페이지 업데이트됨 - membership_added: 멤버십 추가됨 - membership_updated: 멤버십 업데이트됨 - title: 이메일 미리 알림 - pause: - label: 임시로 일일 이메일 미리 알림 일시 중지 - first_day: 첫날 - last_day: 마지막 날 text_are_you_sure: 계속하시겠습니까? text_are_you_sure_to_cancel: 이 페이지에 저장되지 않은 변경 사항이 있습니다. 변경 사항을 취소하시겠습니까? breadcrumb: 이동 경로 diff --git a/config/locales/crowdin/js-lt.yml b/config/locales/crowdin/js-lt.yml index c154110b0b1..4f1ec026645 100644 --- a/config/locales/crowdin/js-lt.yml +++ b/config/locales/crowdin/js-lt.yml @@ -594,49 +594,6 @@ lt: settings: change_notification_settings: Jūs galite keisti savo pranešimų nustatymus, kad užtikrintumėte, jog niekada nepraleisite svarbaus atnaujinimo. title: Pranešimų nustatymai - notify_me: Pranešti man - reminders: - no_notification: Jokio pranešimo - timeframes: - normal: - PT0S: tą pačią dieną - P1D: prieš 1 dieną - P3D: prieš 3 dienas - P7D: prieš savaitę - overdue: - P1D: kiekvieną dieną - P3D: kas 3 dienas - P7D: kiekvieną savaitę - reasons: - mentioned: - title: Paminėtas - description: Gauti pranešimą kiekvieną kartą, kai kas nors mane bet kur pamini - assignee: Paskirtas - responsible: Atsakingas - shared: Bendrinta - watched: Stebėtojas - work_package_commented: Visi nauji komentarai - work_package_created: Nauji darbų paketai - work_package_processed: Visi būsenų pakeitimai - work_package_prioritized: Visi prioritetų pakeitimai - work_package_scheduled: Visi datų pakeitimai - global: - immediately: - title: Dalyvaujate - description: Pranešimai apie visus veiksmus darbo paketuose, su kuriais jūs susiję (priskirtasis, atsakingas ar stebėtojas). - delayed: - title: Nedalyvaujate - description: Papildomi pranešimai apie veiksmus visuose projektuose. - date_alerts: - title: Datos įspėjimai - description: Automatiniai pranešimai apie artėjančias svarbias atvirų darbo paketų, su kuriais jūs susiję (paskirtasis, atsakingas ar stebėtojas), datas. - overdue: Kai pavėluota - project_specific: - title: Konkrečių projektų pranešimų nustatymai - description: Šie konkrečių projektų nustatymai permuša aukščiau esančius numatytuosius nustatymus. - add: Pridėti nustatymą projektui - already_selected: Šis projektas jau pažymėtas - remove: Išimti projekto nustatymus pagination: no_other_page: Jūs esate vieninteliame puslapyje. pages_skipped: Pages skipped. @@ -660,39 +617,6 @@ lt: required_outside_context: 'Prašau pasirinkti projektą, kuriame turi būti sukurtas darbo paketas, kad matytumėte visus atributus. Jūs galite pasirinkti tik tuos projektus, kurie turi aukščiau nurodytą tipą. ' - reminders: - settings: - daily: - add_time: Pridėti laiką - enable: Įjungti kasdienius el.pašto priminimus - explanation: Jūs gausite šiuos priminimus tik neskaitytiems pranešimams ir tik jūsų nurodytomis valandomis. %{no_time_zone} - no_time_zone: Iki jūs sukonfigūruosite jūsų paskyros laiko juostą, šie laikai bus interpretuojami kaip UTC. - time_label: 'Laikas %{counter}:' - title: Siųskite man kasdienius el.pašto priminimus apie neskaitytus pranešimus - workdays: - title: Gauti e-pašto priminimus šiomis dienomis - immediate: - title: Siųsti man e-pašto priminimą - mentioned: Iš karto, kai kažkas @pamini mane - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: E-pašto pranešimai kitiems (ne darbo paketų) elementams - explanation: 'Šiandien pranešimai galimi tik darbų paketams. Jūs galite pasirinkti gauti įspėjimus laiškais apie šiuos įvykius, kol jie dar įtraukiami į pranešimus: - - ' - news_added: Pridėta naujiena - news_commented: Komentuoti naujienų elementą - document_added: Dokumentai pridėti - forum_messages: Nauji forumo pranešimai - wiki_page_added: Wiki puslapis pridėtas - wiki_page_updated: Wiki puslapis atnaujintas - membership_added: Narystė pridėta - membership_updated: Narystė atnaujinta - title: El.pašto priminimai - pause: - label: Laikinai stabdyti kasdieninius e-pašto priminimus - first_day: Pirma diena - last_day: Paskutinė diena text_are_you_sure: Ar esate įsitikinę? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Susijusi informacija diff --git a/config/locales/crowdin/js-lv.yml b/config/locales/crowdin/js-lv.yml index b828313761b..8c1b38f0da7 100644 --- a/config/locales/crowdin/js-lv.yml +++ b/config/locales/crowdin/js-lv.yml @@ -593,49 +593,6 @@ lv: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Pašreizējais atbildīgais - responsible: Accountable - shared: Shared - watched: Sekotājs - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Jūs esat vienīgajā lapā. pages_skipped: Pages skipped. @@ -659,39 +616,6 @@ lv: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Vai esat pārliecināts? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-mn.yml b/config/locales/crowdin/js-mn.yml index f4495277453..1d32a5387f7 100644 --- a/config/locales/crowdin/js-mn.yml +++ b/config/locales/crowdin/js-mn.yml @@ -592,49 +592,6 @@ mn: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Даалгагч - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ mn: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ms.yml b/config/locales/crowdin/js-ms.yml index 008af2c06be..f62bdbb513c 100644 --- a/config/locales/crowdin/js-ms.yml +++ b/config/locales/crowdin/js-ms.yml @@ -591,49 +591,6 @@ ms: settings: change_notification_settings: Anda boleh mengubah suai tetapan pemberitahuan anda untuk memastikan anda tidak terlepas tarikh yang penting. title: Tetapan pemberitahuan - notify_me: Maklumkan saya - reminders: - no_notification: Tiada pemberitahuan - timeframes: - normal: - PT0S: hari yang sama - P1D: 1 hari sebelum - P3D: 3 hari sebelum - P7D: 1 minggu sebelum - overdue: - P1D: setiap hari - P3D: setiap 3 hari - P7D: setiap minggu - reasons: - mentioned: - title: Disebutkan - description: Terima pemberitahuan setiap kali sesiapa menyebut saya di mana saja - assignee: Penerima tugasan - responsible: Bertanggungjawab - shared: Dikongsi - watched: Pemerhati - work_package_commented: Semua komen baharu - work_package_created: Pakej kerja baharu - work_package_processed: Semua perubahan status - work_package_prioritized: Semua perubahan utama - work_package_scheduled: Semua perubahan tarikh - global: - immediately: - title: Menyertai - description: Pemberitahuan untuk semua aktiviti dalam pakej kerja yang anda terlibat (penerima tugasan, bertanggungjawab, atau pemerhati). - delayed: - title: Tidak menyertai - description: Pemberitahuan tambahan untuk aktiviti dalam semua projek. - date_alerts: - title: Peringatan tarikh - description: Pemberitahuan secara automatik apabila tarikh penting menghampiri untuk pakej kerja terbuka yang anda terlibat (penerima tugasan, bertanggungjawab atau pemerhati). - overdue: Apabila tertunggak - project_specific: - title: Tetapan pemberitahuan khusus projek - description: Tetapan pemberitahuan khusus projek mengatasi tetapan default diatas. - add: Tambah tetapan untuk projek - already_selected: Projek ini telah dipilih - remove: Keluarkan tetapan projek pagination: no_other_page: Anda berada di satu-satunya halaman. pages_skipped: Pages skipped. @@ -657,39 +614,6 @@ ms: required_outside_context: 'Sila pilih projek untuk mencipta pakej kerja untuk melihat semua atribut. Anda hanya boleh memilih projek yang mempunyai jenis di atas yang telah diaktifkan. ' - reminders: - settings: - daily: - add_time: Tambah masa - enable: Benarkan peringatan e-mel harian - explanation: Anda akan menerima peringatan hanya untuk pemberitahuan yang belum dibaca dan hanya pada jam yang anda tentukan. %{no_time_zone} - no_time_zone: Sehingga anda konfigurasi zon masa untuk akaun anda, masa akan ditafsirkan dalam UTC. - time_label: 'Masa %{counter}:' - title: Hantar saya peringatan e-mel harian untuk pemberitahuan yang belum dibaca - workdays: - title: Terima peringatan e-mel pada hari-hari ini - immediate: - title: Hantar saya peringatan e-mel - mentioned: Serta merta apabila sesiapa @menyebut saya - personal_reminder: Serta-merta apabila saya menerima peringatan peribadi - alerts: - title: Peringatan e-mel untuk item lain (yang bukan pakej kerja) - explanation: 'Pemberitahuan hari ini adalah terhad untuk pakej kerja. Anda boleh memilih untuk terus menerima makluman e-mel untuk kejadian ini sehingga mereka disertakan dalam pemberitahuan: - - ' - news_added: Berita yang ditambah - news_commented: Komen pada item berita - document_added: Dokumen ditambah - forum_messages: Mesej forum baharu - wiki_page_added: Halaman wiki ditambah - wiki_page_updated: Halaman wiki dikemas kini - membership_added: Keahlian ditambah - membership_updated: Keahlian dikemas kini - title: Peringatan e-mel - pause: - label: Hentikan seketika peringatan e-mel harian - first_day: Hari pertama - last_day: Hari terakhir text_are_you_sure: Adakah anda pasti? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ne.yml b/config/locales/crowdin/js-ne.yml index b8671dac6fb..aa40404ad3a 100644 --- a/config/locales/crowdin/js-ne.yml +++ b/config/locales/crowdin/js-ne.yml @@ -592,49 +592,6 @@ ne: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ ne: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-nl.yml b/config/locales/crowdin/js-nl.yml index d5660e9b5b3..b44c1f54786 100644 --- a/config/locales/crowdin/js-nl.yml +++ b/config/locales/crowdin/js-nl.yml @@ -592,49 +592,6 @@ nl: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Instellingen voor meldingen - notify_me: Waarschuw mij - reminders: - no_notification: Geen melding - timeframes: - normal: - PT0S: dezelfde dag - P1D: 1 dag ervoor - P3D: 3 dagen ervoor - P7D: een week ervoor - overdue: - P1D: elke dag - P3D: elke 3 dagen - P7D: elke week - reasons: - mentioned: - title: Genoemd - description: Ontvang een melding elke keer dat iemand mij ergens noemt - assignee: Toegewezene - responsible: Verantwoording afleggen - shared: Gedeeld - watched: Kijker - work_package_commented: Alle nieuwe reacties - work_package_created: Nieuwe werkpakketten - work_package_processed: Alle statuswijzigingen - work_package_prioritized: Alle prioriteitswijzigingen - work_package_scheduled: 'Alle datum wijzigingen ' - global: - immediately: - title: Deelnemend - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Niet-deelnemend - description: Extra meldingen voor activiteiten in alle projecten. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: Wanneer achterstallig - project_specific: - title: Project-specifieke meldingsinstellingen - description: These project-specific settings override default settings above. - add: Instelling voor project toevoegen - already_selected: Dit project is al geselecteerd - remove: Projectinstellingen verwijderen pagination: no_other_page: U bent op de enige pagina. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ nl: required_outside_context: 'Kies een project om het werkpakket in te creëren om alle attributen te zien. U kunt alleen projecten selecteren waarvan het type hierboven is geactiveerd. ' - reminders: - settings: - daily: - add_time: Tijd toevoegen - enable: Dagelijkse e-mailherinneringen inschakelen - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Tijd %{counter}:' - title: Stuur me dagelijkse herinneringen voor ongelezen meldingen - workdays: - title: Ontvang herinneringen per e-mail op deze dagen - immediate: - title: Stuur me een e-mail herinnering - mentioned: Onmiddellijk wanneer iemand @vermeldt - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: E-mail meldingen voor andere items (die geen werkpakketten zijn) - explanation: 'Meldingen vandaag zijn beperkt tot werkpakketten. U kunt ervoor kiezen om e-mailmeldingen voor deze gebeurtenissen te blijven ontvangen totdat ze zijn opgenomen in meldingen: - - ' - news_added: Nieuws toegevoegd - news_commented: Reageer op een nieuwsitem - document_added: Documenten toegevoegd - forum_messages: Nieuwe forumberichten - wiki_page_added: Wiki-pagina toegevoegd - wiki_page_updated: Wiki-pagina bijgewerkt - membership_added: Lidmaatschap toegevoegd - membership_updated: Lidmaatschap bijgewerkt - title: E-mail herinneringen - pause: - label: Temporarily pause daily email reminders - first_day: Eerste dag - last_day: Laatste dag text_are_you_sure: Weet u het zeker? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-no.yml b/config/locales/crowdin/js-no.yml index ac48d95ba53..b21f75f8284 100644 --- a/config/locales/crowdin/js-no.yml +++ b/config/locales/crowdin/js-no.yml @@ -109,7 +109,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 @@ -592,49 +592,6 @@ settings: change_notification_settings: Du kan endre varslingsinnstillinger for å sikre at du aldri går glipp av en viktig oppdatering. title: Varslingsinnstillinger - notify_me: Gi meg beskjed - reminders: - no_notification: Ingen varsling - timeframes: - normal: - PT0S: samme dag - P1D: 1 dag før - P3D: 3 dager før - P7D: en uke før - overdue: - P1D: hver dag - P3D: hver 3. dag - P7D: hver uke - reasons: - mentioned: - title: Nevnt - description: Motta en melding hver gang noen nevner meg hvor som helst - assignee: Deltaker - responsible: Ansvarlig - shared: Delt - watched: Overvåker - work_package_commented: Alle nye kommentarer - work_package_created: Nye arbeidspakker - work_package_processed: Alle statusendringer - work_package_prioritized: Alle endringer i prioritet - work_package_scheduled: Alle dato endringer - global: - immediately: - title: Deltar - description: Varsler for alle aktiviteter i arbeidspakker du er involvert i (oppdragstaker, ansvarlig eller overvåker). - delayed: - title: Ikke-deltakende - description: Melding om ytterligere aktiviteter i alle prosjekter. - date_alerts: - title: Dato varsler - description: Automatiske varsler når viktige datoer nærmer seg for åpne arbeidspakker du er med i (oppdragstaker, ansvarlig eller overvåker). - overdue: Når forfalt - project_specific: - title: Prosjekt-spesifikke varslingsinnstillinger - description: Disse prosjektspesifikke innstillingene overstyrer standardinnstillingene ovenfor. - add: Legg til innstilling for prosjekt - already_selected: Dette prosjektet er allerede valgt - remove: Fjern prosjektinnstillinger pagination: no_other_page: Du er på den eneste siden. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ required_outside_context: 'Velg et prosjekt for å opprette arbeidspakken og se alle egenskaper. Du kan bare velge prosjekter som har typen ovenfor aktivert. ' - reminders: - settings: - daily: - add_time: Legg til tid - enable: Aktiver daglige e-postpåminnelser - explanation: Du får bare disse påminnelsene for uleste varslinger og bare på tider du angir. %{no_time_zone} - no_time_zone: Fram til du konfigurerer en tidssone for kontoen din, vil tidene bli tolket til å være i UTC. - time_label: 'Tid %{counter}:' - title: Send meg daglige e-postpåminnelser for uleste varslinger - workdays: - title: Motta e-postpåminnelser for disse dagene - immediate: - title: Send meg en påminnelse - mentioned: Umiddelbart hvis noen @nevner meg - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Epost varsler for andre produkter (som ikke er arbeidspakker) - explanation: 'Varsler i dag er begrenset til arbeidspakker. Du kan velge å fortsette å motta e-postvarsler for disse hendelsene inntil de er inkludert i varsler: - - ' - news_added: Lagt til nyhet - news_commented: Kommenter en nyhet - document_added: Dokumenter lagt til - forum_messages: Nye forummeldinger - wiki_page_added: Wiki-side lagt til - wiki_page_updated: Wiki-side oppdatert - membership_added: Medlemskap lagt til - membership_updated: Medlemskapet er oppdatert - title: E-postpåminnelser - pause: - label: pause daglige e-postpåminnelser midlertidig - first_day: 'Første dag ' - last_day: Siste dag text_are_you_sure: Er du sikker? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Brødsmule diff --git a/config/locales/crowdin/js-pl.yml b/config/locales/crowdin/js-pl.yml index a2ddce6bc77..969df3d372d 100644 --- a/config/locales/crowdin/js-pl.yml +++ b/config/locales/crowdin/js-pl.yml @@ -594,49 +594,6 @@ pl: settings: change_notification_settings: Możesz zmodyfikować ustawienia powiadomień, aby upewnić się, że nigdy nie przegapisz ważnej aktualizacji. title: Ustawienia powiadomień - notify_me: Powiadom mnie - reminders: - no_notification: Bez powiadomienia - timeframes: - normal: - PT0S: tego samego dnia - P1D: 1 dzień przed - P3D: 3 dni przed - P7D: 1 tydzień przed - overdue: - P1D: codziennie - P3D: co 3 dni - P7D: co tydzień - reasons: - mentioned: - title: Wzmianka - description: Otrzymuj powiadomienie za każdym razem, gdy pojawi się o tobie wzmianka gdziekolwiek - assignee: Przypisany do - responsible: Osoba odpowiedzialna - shared: Udostępniono - watched: Obserwator - work_package_commented: Wszystkie nowe komentarze - work_package_created: Nowe pakiety robocze - work_package_processed: Wszystkie zmiany statusu - work_package_prioritized: Wszystkie zmiany priorytetów - work_package_scheduled: Wszystkie zmiany daty - global: - immediately: - title: Uczestnictwo - description: Powiadomienia dotyczące wszystkich działań w pakietach roboczych, do których Cię zaangażowano (jako osobę przypisaną, odpowiedzialną lub obserwator) - delayed: - title: Brak uczestnictwa - description: Dodatkowe powiadomienia dotyczące działań we wszystkich projektach - date_alerts: - title: Alerty dotyczące dat - description: 'Automatyczne powiadomienia, gdy zbliżają się ważne daty otwartych pakietów roboczych, do których Cię zaangażowano (jako osobę: przypisaną, odpowiedzialną lub obserwatora)' - overdue: Po upływie terminu - project_specific: - title: Ustawienia powiadomień dla danego projektu - description: Te ustawienia dla danego projektu zastępują powyższe ustawienia domyślne. - add: Dodaj ustawienie dla projektu - already_selected: Ten projekt jest już wybrany - remove: Usuń ustawienia projektu pagination: no_other_page: Jesteś na jedynej stronie. pages_skipped: Pominięte strony. @@ -660,39 +617,6 @@ pl: required_outside_context: 'Wybierz projekt, w którym chcesz utworzyć pakiet roboczy, aby zobaczyć wszystkie atrybuty. Możesz wybrać tylko projekty, które mają aktywowany powyższy typ. ' - reminders: - settings: - daily: - add_time: Dodaj czas - enable: Włącz codzienne przypomnienia e-mail - explanation: Otrzymasz te przypomnienia tylko dla nieprzeczytanych powiadomień i tylko w określonych przez Ciebie godzinach. %{no_time_zone} - no_time_zone: Dopóki nie skonfigurujesz strefy czasowej dla swojego konta, czas będzie interpretowany jako UTC. - time_label: 'Czas %{counter}:' - title: Wyślij mi codzienne przypomnienia e-mail dla nieprzeczytanych powiadomień - workdays: - title: Otrzymuj przypomnienia e-mail w tych dniach - immediate: - title: Wyślij mi przypomnienie e-mail - mentioned: Natychmiast, gdy pojawi się @wzmianka o mnie - personal_reminder: Natychmiast po otrzymaniu osobistego przypomnienia - alerts: - title: Powiadomienia e-mail dla innych elementów (które nie są pakietami roboczymi) - explanation: 'Dzisiaj powiadomienia są ograniczone do pakietów roboczych. Możesz nadal otrzymywać powiadomienia e-mail dla tych zdarzeń, dopóki nie zostaną one zawarte w powiadomieniach: - - ' - news_added: Wiadomość dodana - news_commented: Komentarz do nowości - document_added: Dodanie dokumentu - forum_messages: Nowe wiadomości na forum - wiki_page_added: Dodano stronę wiki - wiki_page_updated: Zaktualizowano stronę wiki - membership_added: Dodanie członkostwa - membership_updated: Aktualizacja członkostwa - title: Przypomnienia e-mail - pause: - label: Tymczasowo wstrzymuj codzienne przypomnienia e-mail - first_day: Pierwszy dzień - last_day: Ostatni dzień text_are_you_sure: Jesteś pewny? text_are_you_sure_to_cancel: Na tej stronie znajdują się niezapisane zmiany. Czy na pewno chcesz je odrzucić? breadcrumb: Ścieżka diff --git a/config/locales/crowdin/js-pt-BR.yml b/config/locales/crowdin/js-pt-BR.yml index b0659471aa9..3adbc3bbed4 100644 --- a/config/locales/crowdin/js-pt-BR.yml +++ b/config/locales/crowdin/js-pt-BR.yml @@ -592,49 +592,6 @@ pt-BR: settings: change_notification_settings: Você pode modificar suas configurações de notificação para se certificar de nunca perder nenhuma atualização importante. title: Configurações de notificação - notify_me: Notifique-me - reminders: - no_notification: Sem notificação - timeframes: - normal: - PT0S: mesmo dia - P1D: 1 dia antes - P3D: 3 dias antes - P7D: uma semana antes - overdue: - P1D: todo dia - P3D: a cada 3 dias - P7D: Toda semana - reasons: - mentioned: - title: Mencionado - description: Receber uma notificação sempre que alguém me mencionar - assignee: Cessionário - responsible: Responsável - shared: Compartilhado - watched: Observador - work_package_commented: Todos os novos comentários - work_package_created: Novos pacotes de trabalho - work_package_processed: Todas as mudanças de situação - work_package_prioritized: Todas as mudanças de prioridade - work_package_scheduled: Todas as alterações de data - global: - immediately: - title: Participando - description: Notificações para todas as atividades nos pacotes de trabalho que você está envolvido (cessionário, responsável ou observador). - delayed: - title: Não participando - description: Notificações adicionais para atividades em todos os projetos - date_alerts: - title: Alertas de data - description: Notificações automáticas quando datas importantes estiverem se aproximando para pacotes de trabalho abertos que você esteja envolvido (cessionário, responsável ou observador). - overdue: Quando vencido - project_specific: - title: Configurações de notificação específicas de projetos - description: Essas configurações específicas de projeto substituem as configurações padrão acima - add: Adicionar configuração para o projeto - already_selected: Este projeto já está selecionado - remove: Remover configurações de projeto pagination: no_other_page: Você está na página única. pages_skipped: Páginas puladas. @@ -658,39 +615,6 @@ pt-BR: required_outside_context: 'Por favor, escolha o projeto onde vai criar o pacote de trabalho para visualizar todos os atributos. Você pode selecionar somente projetos que possuam o tipo acima ativado. ' - reminders: - settings: - daily: - add_time: Adicionar tempo - enable: 'Ativar lembretes diários por e-mail ' - explanation: Você receberá estes lembretes somente para as notificações não lidas e apenas nas horas especificadas por você. %{no_time_zone} - no_time_zone: Até que um fuso horário seja configurado em sua conta, os horários serão interpretados como UTC. - time_label: 'Tempo %{counter}:' - title: Envie-me lembretes diários por e-mail para as notificações não lidas - workdays: - title: Receber lembretes por e-mail nestes dias - immediate: - title: Envie-me um lembrete por e-mail - mentioned: Imediatamente quando alguém me @mencionar - personal_reminder: Imediatamente quando eu receber um lembrete pessoal - alerts: - title: Alertas por e-mail para outros itens (qua não sejam pacotes de trabalho) - explanation: 'As notificações hoje estão limitadas a pacotes de trabalho. Você pode optar por continuar a receber alertas por e-mail para estes eventos até que eles sejam incluídos nas notificações: - - ' - news_added: Notícia adicionada - news_commented: Comentar em um item de notícia - document_added: Documentos adicionados - forum_messages: Novas mensagens de fórum - wiki_page_added: Página wiki adicionada - wiki_page_updated: Página wiki atualizada - membership_added: Associação adicionada - membership_updated: Associação atualizada - title: Lembretes por e-mail - pause: - label: 'Pausar temporariamente lembretes por e-mail diários ' - first_day: Primeiro dia - last_day: Último dia text_are_you_sure: Você tem certeza? text_are_you_sure_to_cancel: Você tem alterações não salvas nesta página. Tem certeza de que deseja descartá-las? breadcrumb: Trilha de navegação diff --git a/config/locales/crowdin/js-pt-PT.yml b/config/locales/crowdin/js-pt-PT.yml index 889f67bd505..be9aaa527d5 100644 --- a/config/locales/crowdin/js-pt-PT.yml +++ b/config/locales/crowdin/js-pt-PT.yml @@ -592,49 +592,6 @@ pt-PT: settings: change_notification_settings: Pode modificar as suas definições de notificações para garantir que nunca perde uma atualização importante. title: Definições das notificações - notify_me: Notificar-me - reminders: - no_notification: Sem notificações - timeframes: - normal: - PT0S: no mesmo dia - P1D: 1 dia antes - P3D: 3 dias antes - P7D: uma semana antes - overdue: - P1D: todos os dias - P3D: a cada 3 dias - P7D: todas as semanas - reasons: - mentioned: - title: Mencionou - description: Receber uma notificação sempre que alguém me mencionar em qualquer lugar - assignee: Responsável - responsible: Responsável - shared: Partilhado - watched: Observador - work_package_commented: Todos os novos comentários - work_package_created: Novos pacotes de trabalho - work_package_processed: Todas as mudanças de estado - work_package_prioritized: Todas as mudanças de prioridade - work_package_scheduled: Todas as alterações de data - global: - immediately: - title: Participante - description: Notificações para todas as atividades nos pacotes de trabalho em que está envolvido (responsável ou observador). - delayed: - title: Não participante - description: Notificações adicionais para atividades em todos os projetos. - date_alerts: - title: Alertas de data - description: Notificações automáticas ao aproximarem-se datas importantes para pacotes de trabalho abertos em que esteja envolvido (responsável ou observador). - overdue: Quando vencido - project_specific: - title: Configurações de notificação específicas do projeto - description: Estas configurações específicas do projeto substituem as configurações padrão acima. - add: Adicionar configuração ao projeto - already_selected: Este projeto já está selecionado - remove: Remover definições do projeto pagination: no_other_page: Mais nenhuma página a apresentar. pages_skipped: Páginas saltadas. @@ -658,39 +615,6 @@ pt-PT: required_outside_context: 'Por favor escolha um projeto onde criar o pacote de trabalho de forma a ver todos os atributos. Só pode selecionar projetos com o tipo acima ativado. ' - reminders: - settings: - daily: - add_time: Adicionar hora - enable: Ativar lembretes diários por e-mail - explanation: Vai receber estes lembretes apenas para notificações não lidas e apenas às horas que especificar. %{no_time_zone} - no_time_zone: Até que configure um fuso horário para a sua conta, os horários serão interpretados como estando em UTC. - time_label: 'Hora %{counter}:' - title: Envie-me lembretes diários de e-mail para notificações não lidas - workdays: - title: Receba lembretes por e-mail nestes dias - immediate: - title: Enviar-me um lembrete por e-mail - mentioned: Imediatamente quando alguém me @mencionar - personal_reminder: Imediatamente quando recebo um lembrete pessoal - alerts: - title: Alertas de e-mail para outros itens (que não são pacotes de trabalho) - explanation: 'As notificações hoje estão limitadas a pacotes de trabalho. Pode optar por continuar a receber alertas de e-mail para estes eventos até que eles sejam incluídos nas notificações: - - ' - news_added: Notícia adicionada - news_commented: Comentar numa notícia - document_added: Documentos adicionados - forum_messages: Novas mensagens do fórum - wiki_page_added: Página wiki adicionada - wiki_page_updated: Página wiki atualizada - membership_added: Adesão adicionada - membership_updated: Adesão atualizada - title: Lembretes por e-mail - pause: - label: Pausar temporariamente lembretes de e-mail diários - first_day: Primeiro dia - last_day: Último dia text_are_you_sure: Tem a certeza? text_are_you_sure_to_cancel: Tem alterações não guardadas nesta página. Tem a certeza de que as quer eliminar? breadcrumb: Estrutural diff --git a/config/locales/crowdin/js-ro.yml b/config/locales/crowdin/js-ro.yml index 13eb514d9b7..cf9d15d6703 100644 --- a/config/locales/crowdin/js-ro.yml +++ b/config/locales/crowdin/js-ro.yml @@ -109,7 +109,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ă @@ -593,49 +593,6 @@ ro: settings: change_notification_settings: Poți modifica setările de notificare pentru a te asigura că nu pierzi niciodată o actualizare importantă. title: Setări notificare - notify_me: Notifică-mă - reminders: - no_notification: Nicio notificare - timeframes: - normal: - PT0S: în aceeași zi - P1D: cu 1 zi înainte - P3D: cu 3 zile înainte - P7D: " cu o săptămână înainte " - overdue: - P1D: zilnic - P3D: la fiecare 3 zile - P7D: săptămânal - reasons: - mentioned: - title: Menţionat - description: Primesc o notificare de fiecare dată când cineva mă menționează oriunde - assignee: Executant - responsible: Responsabil - shared: Partajat - watched: Observator - work_package_commented: Toate comentariile noi - work_package_created: Pachete de lucru noi - work_package_processed: Toate modificările de stare - work_package_prioritized: Toate modificările de prioritate - work_package_scheduled: Toate modificările dății - global: - immediately: - title: Participant - description: Notificări pentru toate activitățile din pachetele de lucru în care ești implicat (desemnat, responsabil sau observator). - delayed: - title: Unde nu participi - description: Notificări suplimentare pentru activitățile din toate proiectele. - date_alerts: - title: Alerte dăți - description: Notificări automate atunci când se apropie dăți importante pentru pachetele de lucru deschise în care ești implicat (desemnat, responsabil sau observator). - overdue: În caz de întârziere - project_specific: - title: Setări notificare specifice proiectului - description: Aceste setări specifice proiectului prevalează asupra setărilor implicite de mai sus. - add: Adaugă o setare pentru proiect - already_selected: Acest proiect este deja selectat - remove: Elimină setările proiectului pagination: no_other_page: Ești pe singura pagină. pages_skipped: Pagini sărite. @@ -659,39 +616,6 @@ ro: required_outside_context: 'Te rog să alegi un proiect în care să creezi pachetul de lucru pentru a vedea toate atributele. Poți selecta numai proiectele care au tipul de mai sus activat. ' - reminders: - settings: - daily: - add_time: Adaugă timp - enable: Activează reamintirile zilnice prin e-mail - explanation: Vei primi aceste reamintiri numai pentru notificările necitite și numai la câteva ore specificate. %{no_time_zone} - no_time_zone: Până când configurezi un fus orar pentru contul tău, orele vor fi interpretate ca fiind în UTC. - time_label: 'Timp %{counter}:' - title: Trimite-mi reamintiri zilnice prin e-mail pentru notificările necitite - workdays: - title: Primește reamintiri prin e-mail în aceste zile - immediate: - title: Trimite-mi un e-mail de reamintire - mentioned: Imediat când cineva mă @menționează - personal_reminder: Imediat când primesc reamintiri personale - alerts: - title: Alerte prin e-mail pentru alte elemente (care nu sunt pachete de lucru) - explanation: 'Astăzi, notificările se limitează la pachetele de lucru. Puteți alege să continuați să primiți alerte prin e-mail pentru aceste evenimente până când acestea vor fi incluse în notificări: - - ' - news_added: Noutăți adăugate - news_commented: Comentariu la o știre - document_added: Documente adăugate - forum_messages: Mesaje noi pe forum - wiki_page_added: Pagină wiki adăugată - wiki_page_updated: Pagină wiki actualizată - membership_added: Adaugă membri - membership_updated: Membrii actualizați - title: Reamintiri e-mail - pause: - label: Întrerupe temporar reamintirile zilnice prin e-mail - first_day: Prima zi - last_day: Ultima zi text_are_you_sure: Ești sigur? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index bdcb0bb6467..5f7d8352065 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -109,7 +109,7 @@ ru: button_save: Сохранить button_settings: Настройки button_uncheck_all: Снять все отметки - button_update: Обновить + button_update: Обновление button_export-atom: Скачать Atom button_generate_pdf: Создать PDF button_create: Создать @@ -596,49 +596,6 @@ ru: settings: change_notification_settings: Измените настройки уведомлений , чтобы не пропустить важное обновление. title: Настройки уведомлений - notify_me: Уведомлять меня - reminders: - no_notification: Нет уведомления - timeframes: - normal: - PT0S: тот же день - P1D: за 1 день до - P3D: 3 дня до - P7D: за неделю до - overdue: - P1D: каждый день - P3D: каждые 3 дня - P7D: каждую неделю - reasons: - mentioned: - title: Упомянутый - description: Получать уведомления каждый раз, когда кто-то упоминает меня в любом месте - assignee: Назначенный - responsible: Ответственный - shared: Общий доступ - watched: Наблюдатель - work_package_commented: Все новые комментарии - work_package_created: Новые пакеты работ - work_package_processed: Все изменения статуса - work_package_prioritized: Все приоритетные изменения - work_package_scheduled: Все изменения даты - global: - immediately: - title: Участие - description: Уведомления обо всех действиях в пакетах работ, в которых вы участвуете (назначение, подотчетность или наблюдение). - delayed: - title: Неучастие - description: Дополнительные уведомления для деятельности во всех проектах. - date_alerts: - title: Дата оповещения - description: Автоматическое уведомление, когда приближаются важные даты открытия пакетов работ, в которых вы участвуете (исполнитель, ответственный или наблюдатель). - overdue: Когда просрочено - project_specific: - title: Конкретные настройки уведомлений проекта - description: Эти настройки специфического проекта переопределяют параметры по умолчанию выше. - add: Добавить настройку для проекта - already_selected: Этот проект уже выбран - remove: Удалить настройки проекта pagination: no_other_page: Вы находитесь на единственной странице. pages_skipped: Страницы пропущены. @@ -662,39 +619,6 @@ ru: required_outside_context: 'Пожалуйста, выберите проект для создания пакета работ для просмотра всех атрибутов. Вы можете выбрать только проекты, которые имеют вышеуказанный тип. ' - reminders: - settings: - daily: - add_time: Добавить время - enable: Включить ежедневные напоминания по электронной почте - explanation: Вы будете получать эти напоминания только для непрочитанных уведомлений и только в указанные вами часы. %{no_time_zone} - no_time_zone: До уточнения часового пояса для вашего аккаунта время будет в UTC. - time_label: 'Время %{counter}:' - title: Посылать мне ежедневные напоминания по электронной почте для непрочитанных уведомлений - workdays: - title: Получать напоминания по электронной почте в эти дни - immediate: - title: Отправить мне напоминание по электронной почте - mentioned: Сразу же когда кто-то @упоминает меня - personal_reminder: Немедленно, когда я получаю персональное напоминание - alerts: - title: Уведомления по электронной почте для других элементов (которые не являются пакетами работ) - explanation: 'Уведомления сегодня ограничены пакетами работ. Вы можете продолжать получать уведомления по электронной почте об этих событиях до тех пор, пока они не будут включены в уведомления: - - ' - news_added: Новость добавлена - news_commented: Комментарий к новости - document_added: Добавлены документы - forum_messages: Новые сообщения форума - wiki_page_added: Wiki страница добавлена - wiki_page_updated: Wiki страница обновлена - membership_added: Членство добавлено - membership_updated: Членство обновлено - title: Почтовые напоминания - pause: - label: Временно приостановить ежедневные напоминания по электронной почте - first_day: Первый день - last_day: Последний день text_are_you_sure: Уверены? text_are_you_sure_to_cancel: У Вас есть несохраненные изменения на этой странице. Вы уверены, что хотите их отменить? breadcrumb: Навигационная цепочка diff --git a/config/locales/crowdin/js-rw.yml b/config/locales/crowdin/js-rw.yml index fbb287eec30..2d8bf298611 100644 --- a/config/locales/crowdin/js-rw.yml +++ b/config/locales/crowdin/js-rw.yml @@ -592,49 +592,6 @@ rw: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ rw: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-si.yml b/config/locales/crowdin/js-si.yml index cd0caa7c2b1..7e1031c8154 100644 --- a/config/locales/crowdin/js-si.yml +++ b/config/locales/crowdin/js-si.yml @@ -592,49 +592,6 @@ si: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: අස්ගිනී - responsible: වගවීම - shared: Shared - watched: මුරකරු - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: ඔබ සිටින්නේ එකම පිටුවේ ය. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ si: required_outside_context: 'කරුණාකර සියලු ගුණාංග බැලීමට වැඩ පැකේජය නිර්මාණය කිරීම සඳහා ව්යාපෘතියක් තෝරන්න. ඔබට තෝරා ගත හැක්කේ ඉහත ආකාරයේ සක්රිය කර ඇති ව්යාපෘති පමණි. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: පුවත් එකතු - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: විකි පිටුව එකතු කරන ලදි - wiki_page_updated: විකි පිටුව යාවත්කාලීන කරන ලදි - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: ඔබට විශ්වාසද? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-sk.yml b/config/locales/crowdin/js-sk.yml index af58c4b132c..fbe4209b160 100644 --- a/config/locales/crowdin/js-sk.yml +++ b/config/locales/crowdin/js-sk.yml @@ -594,49 +594,6 @@ sk: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Priradené - responsible: Zodpovedný - shared: Shared - watched: Pozorovateľ - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Ste na jedinej stránke. pages_skipped: Pages skipped. @@ -660,39 +617,6 @@ sk: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Novinky pridané - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wikistránka bola pridaná - wiki_page_updated: Wikistránka bola aktualizovaná - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Ste si istí? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-sl.yml b/config/locales/crowdin/js-sl.yml index 6c243c3fdd7..2da7cae7a22 100644 --- a/config/locales/crowdin/js-sl.yml +++ b/config/locales/crowdin/js-sl.yml @@ -596,49 +596,6 @@ sl: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Nastavitve obvestil - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Omenjen - description: Receive a notification every time someone mentions me anywhere - assignee: Prevzemnik - responsible: Odgovorni - shared: Shared - watched: Opazovalec - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: 'Ste na edini strani. ' pages_skipped: Pages skipped. @@ -662,39 +619,6 @@ sl: required_outside_context: 'Prosimo, izberite projekt, v katerem boste ustvarili delovni paket, če si želite ogledati vse atribute. Izberete lahko samo projekte, ki so aktivirani zgoraj. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Dodane novice - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki stran dodana - wiki_page_updated: Wiki stran posodobljena - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Ste prepričani? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-sr.yml b/config/locales/crowdin/js-sr.yml index 6c3197f8805..8337a759061 100644 --- a/config/locales/crowdin/js-sr.yml +++ b/config/locales/crowdin/js-sr.yml @@ -593,49 +593,6 @@ sr: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Zadužen - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -659,39 +616,6 @@ sr: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-sv.yml b/config/locales/crowdin/js-sv.yml index 8c256a434c4..9a9f5943fcd 100644 --- a/config/locales/crowdin/js-sv.yml +++ b/config/locales/crowdin/js-sv.yml @@ -592,49 +592,6 @@ sv: settings: change_notification_settings: Du kan ändra dina aviseringsinställningar för att säkerställa att du aldrig missar en viktig uppdatering. title: Inställningar för aviseringar - notify_me: Meddela mig - reminders: - no_notification: Ingen avisering - timeframes: - normal: - PT0S: samma dag - P1D: 1 dag innan - P3D: 3 dagar innan - P7D: en vecka innan - overdue: - P1D: varje dag - P3D: var 3:e dag - P7D: varje vecka - reasons: - mentioned: - title: Omnämnd - description: Få ett meddelande varje gång någon nämner mig var som helst - assignee: Tilldelad till - responsible: Huvudansvarig - shared: Delad - watched: Bevakare - work_package_commented: Alla nya kommentarer - work_package_created: Nya arbetspaket - work_package_processed: Alla statusändringar - work_package_prioritized: Alla prioritetsändringar - work_package_scheduled: Alla datumändringar - global: - immediately: - title: Deltar - description: Aviseringar för alla aktiviteter i arbetspaket som du är involverad i (tilldelad ansvarig, ansvarig eller bevakare). - delayed: - title: Icke-deltagande - description: Ytterligare aviseringar för aktiviteter i alla projekt. - date_alerts: - title: Datumnotiser - description: Automatiska aviseringar när viktiga datum närmar sig för öppna arbetspaket som du är involverad i (tilldelad ansvarig, ansvarig eller bevakare). - overdue: När försenad - project_specific: - title: Projekt-specifika aviseringsinställningar - description: Dessa projektspecifika inställningar åsidosätter standardinställningarna ovan. - add: Lägg till inställning för projekt - already_selected: Detta projekt är redan valt - remove: Ta bort projektinställningar pagination: no_other_page: Du befinner dig på den enda sidan. pages_skipped: Sidor överhoppade. @@ -658,39 +615,6 @@ sv: required_outside_context: 'Välj ett projekt för att skapa arbetspaketet för att se alla attribut. Du kan bara välja projekt som har typen ovan aktiverad. ' - reminders: - settings: - daily: - add_time: Lägg till tid - enable: Aktivera dagliga e-postpåminnelser - explanation: Du kommer att få dessa påminnelser endast för olästa aviseringar och endast vid timmar du anger. %{no_time_zone} - no_time_zone: Tills du konfigurerar en tidszon för ditt konto kommer tiderna att tolkas som att vara i UTC. - time_label: 'Tid %{counter}:' - title: Skicka dagligen e-postpåminnelser för olästa aviseringar - workdays: - title: Få påminnelser via e-post dessa dagar - immediate: - title: Skicka mig en påminnelse via e-post - mentioned: Omedelbart när någon @nämner mig - personal_reminder: Omedelbart när jag får en personlig påminnelse - alerts: - title: E-postmeddelanden för andra artiklar (som inte är arbetspaket) - explanation: 'Meddelanden i dag är begränsade till arbetspaket. Du kan välja att fortsätta ta emot e-postmeddelanden för dessa händelser tills de ingår i meddelanden: - - ' - news_added: Nyheter tillagda - news_commented: Kommentar till en nyhet - document_added: Dokument tillagt - forum_messages: Nya forummeddelanden - wiki_page_added: Wiki-sidan tillagd - wiki_page_updated: Wiki-sidan uppdaterad - membership_added: Medlemskap tillagt - membership_updated: Medlemskap uppdaterat - title: E-postpåminnelser - pause: - label: Pausa tillfälligt dagliga e-postpåminnelser - first_day: Första dagen - last_day: Senaste dagen text_are_you_sure: Är du säker? text_are_you_sure_to_cancel: Du har osparade ändringar på denna sida. Är du säker på att du vill kasta dem? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-th.yml b/config/locales/crowdin/js-th.yml index 8209937a2d5..f424fff1053 100644 --- a/config/locales/crowdin/js-th.yml +++ b/config/locales/crowdin/js-th.yml @@ -591,49 +591,6 @@ th: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: ผู้ได้รับมอบหมาย - responsible: Accountable - shared: Shared - watched: ผู้ดูข้อมูล - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -657,39 +614,6 @@ th: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: เพิ่มข่าวแล้ว - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: เพิ่มหน้าวิกิแล้ว - wiki_page_updated: ปรับปรุงหน้าวิกิแล้ว - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: คุณแน่ใจหรือไม่ ? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-tr.yml b/config/locales/crowdin/js-tr.yml index 8b417636476..05c962abbfd 100644 --- a/config/locales/crowdin/js-tr.yml +++ b/config/locales/crowdin/js-tr.yml @@ -592,49 +592,6 @@ tr: settings: change_notification_settings: Önemli bir güncellemeyi asla kaçırmamak için bildirim ayarlarınızı değiştirebilirsiniz. title: Bildirim ayarları - notify_me: Beni dürt - reminders: - no_notification: Bildirim yok - timeframes: - normal: - PT0S: aynı gün - P1D: 1 gün önce - P3D: 3 gün önce - P7D: 1 hafta önce - overdue: - P1D: her gün - P3D: Her 3 günde bir - P7D: her hafta - reasons: - mentioned: - title: Bahsedilen - description: Birisi herhangi bir yerde benden her bahsettiğinde bir bildirim alın - assignee: Atanan - responsible: Sorumlu - shared: Paylaşıldı - watched: Takip edilen - work_package_commented: Tüm yeni yorumlar - work_package_created: Yeni iş paketleri - work_package_processed: Tüm durum değişiklikleri - work_package_prioritized: Tüm öncelik değişiklikleri - work_package_scheduled: Tüm tarih değişiklikleri - global: - immediately: - title: Katılım - description: Dahil olduğunuz (vekil, sorumlu veya gözlemci) iş paketlerindeki tüm faaliyetler için bildirimler. - delayed: - title: Katılmayan - description: Tüm projelerdeki faaliyetler için ek bildirimler. - date_alerts: - title: Tarih uyarısı - description: Dahil olduğunuz (vekil, sorumlu veya gözlemci) açık iş paketleri için önemli tarihler yaklaştığında otomatik bildirimler. - overdue: Süresi geçtiğinde - project_specific: - title: Projeye özel bildirim ayarları - description: Bu projeye özgü ayarlar, yukarıdaki varsayılan ayarları geçersiz kılar. - add: Proje için ayar ekle - already_selected: Bu proje zaten seçildi - remove: Proje ayarlarını kaldır pagination: no_other_page: Geçerli tek sayfadasınız. pages_skipped: Sayfalar atlandı. @@ -658,39 +615,6 @@ tr: required_outside_context: 'Lütfen tüm özellikleri görmek için çalışma paketini oluşturacak bir proje seçin. Yalnızca yukarıdaki türden etkin olan projeleri seçebilirsiniz. ' - reminders: - settings: - daily: - add_time: Zaman ekle - enable: Günlük email hatırlatıcılarını etkinleştir - explanation: Bu hatırlatıcıları yalnızca okunmamış bildirimler için ve yalnızca sizin belirlediğiniz saatlerde alacaksınız. %{no_time_zone} - no_time_zone: Hesabınız için bir saat dilimi yapılandırana kadar, saatler UTC olarak yorumlanacaktır. - time_label: 'Zaman %{counter}:' - title: Okunmamış bildirimler için bana günlük e-posta hatırlatıcıları gönder - workdays: - title: Bu günlerde e-posta hatırlatıcıları alın - immediate: - title: Bana email hatırlatıcısı gönder - mentioned: Birisi benden @bahsettiğinde hemen - personal_reminder: Kişisel bir hatırlatma aldığımda hemen - alerts: - title: Diğer öğeler için e-posta uyarıları (iş paketleri olmayanlar) - explanation: 'Bildirimler bugün iş paketleri ile sınırlıdır. Bildirimlere eklenene kadar bu etkinlikler için e-posta uyarıları almaya devam etmeyi seçebilirsiniz: - - ' - news_added: Haber eklendiğinde - news_commented: Bir habere yorum yapıldığında - document_added: Belgeler eklendi - forum_messages: Yeni forum iletileri yazıldığında - wiki_page_added: Wiki sayfası eklendiğinde - wiki_page_updated: Wiki sayfası güncellendiğinde - membership_added: Üyelik eklendiğinde - membership_updated: Üyelik güncellendiğinde - title: Email hatırlatıcıları - pause: - label: Email hatırlatıcılarını geçici olarak durdur - first_day: İlk gün - last_day: Son gün text_are_you_sure: Emin misiniz? text_are_you_sure_to_cancel: Bu sayfada kaydedilmemiş değişiklikleriniz var. Bunları atmak istediğinizden emin misiniz? breadcrumb: Gezinti Menüsü diff --git a/config/locales/crowdin/js-uk.yml b/config/locales/crowdin/js-uk.yml index 704242f5274..1fdce90438c 100644 --- a/config/locales/crowdin/js-uk.yml +++ b/config/locales/crowdin/js-uk.yml @@ -594,49 +594,6 @@ uk: settings: change_notification_settings: Ви можете змінити налаштування сповіщень, щоб ніколи не пропускати важливі оновлення. title: Налаштування сповіщень - notify_me: Сповіщати мене - reminders: - no_notification: Немає сповіщень - timeframes: - normal: - PT0S: того ж дня - P1D: за 1 день - P3D: за 3 дні - P7D: за тиждень - overdue: - P1D: щодня - P3D: кожні 3 дні - P7D: щотижня - reasons: - mentioned: - title: Згадано - description: Отримувати сповіщення щоразу, коли хтось згадує мене де завгодно - assignee: Виконавець - responsible: Відповідальний - shared: Спільні - watched: Спостерігач - work_package_commented: Усі нові коментарі - work_package_created: Нові пакети робіт - work_package_processed: Усі зміни статусу - work_package_prioritized: Усі зміни пріоритету - work_package_scheduled: Усі зміни дат - global: - immediately: - title: Бере участь - description: Сповіщення про всі дії з пакетами робіт, з якими ви пов’язані (виконавець, відповідальний або спостерігач). - delayed: - title: Не бере участі - description: Додаткові сповіщення про дії в усіх проєктах. - date_alerts: - title: Оповіщення про дати - description: Автоматичні сповіщення в разі наближення важливих дат для відкритих пакетів робіт, з якими ви пов’язані (виконавець, відповідальний або спостерігач). - overdue: Якщо прострочено - project_specific: - title: Параметри сповіщень проєкту - description: Ці налаштування сповіщень проєкту перевизначають типові налаштування вище. - add: Додати налаштування для проєкту - already_selected: Цей проєкт уже вибрано - remove: Вилучити налаштування проєкту pagination: no_other_page: Ви знаходитесь на єдиній сторінці. pages_skipped: Сторінки пропущено. @@ -660,39 +617,6 @@ uk: required_outside_context: 'Виберіть проект, щоб створити робочий пакет, щоб побачити всі атрибути. Ви можете вибирати лише проекти, які мають вказаний вище тип активації. ' - reminders: - settings: - daily: - add_time: Додати час - enable: Увімкнути щоденні нагадування електронною поштою - explanation: Ви отримуватимете ці нагадування лише для непрочитаних сповіщень і тільки в указані години. %{no_time_zone} - no_time_zone: Доки ви не налаштуєте часовий пояс для свого облікового запису, вважатиметься, що ваш час указано за стандартом UTC. - time_label: 'Час %{counter}:' - title: Надсилати мені щоденні нагадування електронною поштою про непрочитані сповіщення - workdays: - title: Отримувати нагадування електронною поштою в ці дні - immediate: - title: Надсилати мені нагадування електронною поштою - mentioned: Негайно, коли хтось згадує @мене - personal_reminder: Одразу після отримання особистого нагадування - alerts: - title: Сповіщення електронною поштою для інших елементів (які не є пакетами робіт) - explanation: 'Сьогодні надходитимуть лише сповіщення про пакети робіт. Ви можете й надалі отримувати сповіщення електронною поштою для цих подій, якщо їх не буде включено в сповіщення: - - ' - news_added: Новину додано - news_commented: Коментар щодо новини - document_added: Документи додано - forum_messages: Нові повідомлення форуму - wiki_page_added: Додано сторінку Wiki - wiki_page_updated: Сторінка Wiki оновлена - membership_added: Членство додано - membership_updated: Членство додано - title: Нагадування електронною поштою - pause: - label: Тимчасово призупинити щоденні нагадування електронною поштою - first_day: Перший день - last_day: Останній день text_are_you_sure: Ви впевненені? text_are_you_sure_to_cancel: На цій сторінці є незбережені зміни. Ви дійсно хочете їх скасувати? breadcrumb: Ланцюжок навігації diff --git a/config/locales/crowdin/js-uz.yml b/config/locales/crowdin/js-uz.yml index ab77db4e91e..f4fb16888e3 100644 --- a/config/locales/crowdin/js-uz.yml +++ b/config/locales/crowdin/js-uz.yml @@ -592,49 +592,6 @@ uz: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +615,6 @@ uz: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb diff --git a/config/locales/crowdin/js-vi.yml b/config/locales/crowdin/js-vi.yml index 1e8f3b6ea59..a615d3a8509 100644 --- a/config/locales/crowdin/js-vi.yml +++ b/config/locales/crowdin/js-vi.yml @@ -109,7 +109,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 @@ -591,49 +591,6 @@ vi: settings: change_notification_settings: Bạn có thể thay đổi cài đặt thông báo của mình để đảm bảo không bỏ lỡ cập nhật quan trọng nào. title: Cài đặt thông báo - notify_me: Thông báo cho tôi - reminders: - no_notification: Không có thông báo - timeframes: - normal: - PT0S: cùng ngày - P1D: 1 ngày trước - P3D: 3 ngày trước - P7D: một tuần trước - overdue: - P1D: mỗi ngày - P3D: cứ sau 3 ngày - P7D: mỗi tuần - reasons: - mentioned: - title: Được đề cập - description: Nhận thông báo mỗi khi ai đó nhắc đến tôi ở bất cứ đâu - assignee: Người được chuyển nhượng - responsible: chịu trách nhiệm - shared: đã chia sẻ - watched: người theo dõi - work_package_commented: Tất cả bình luận mới - work_package_created: Gói công việc mới - work_package_processed: Mọi thay đổi trạng thái - work_package_prioritized: Tất cả các thay đổi ưu tiên - work_package_scheduled: Tất cả các thay đổi ngày - global: - immediately: - title: Tham gia - description: Thông báo cho tất cả các hoạt động trong gói công việc mà bạn tham gia (người được giao, người chịu trách nhiệm hoặc người theo dõi). - delayed: - title: Không tham gia - description: Thông báo bổ sung cho các hoạt động trong tất cả các dự án. - date_alerts: - title: Thông báo ngày - description: Thông báo tự động khi các ngày quan trọng đang đến gần đối với các gói công việc đang mở mà bạn tham gia (người được giao, người chịu trách nhiệm hoặc người theo dõi). - overdue: Khi quá hạn - project_specific: - title: Cài đặt thông báo dành riêng cho dự án - description: Các cài đặt dành riêng cho dự án này sẽ ghi đè các cài đặt mặc định ở trên. - add: Thêm cài đặt cho dự án - already_selected: Dự án này đã được chọn - remove: Xóa cài đặt dự án pagination: no_other_page: Bạn đang trên trang duy nhất. pages_skipped: Các trang bị bỏ qua. @@ -657,39 +614,6 @@ vi: required_outside_context: 'Vui lòng chọn một dự án để tạo gói công việc để xem tất cả các thuộc tính. Bạn chỉ có thể chọn các dự án đã kích hoạt loại trên. ' - reminders: - settings: - daily: - add_time: Thêm thời gian - enable: Bật lời nhắc email hàng ngày - explanation: Bạn sẽ chỉ nhận được những lời nhắc này đối với những thông báo chưa đọc và chỉ vào những giờ bạn chỉ định. %{no_time_zone} - no_time_zone: Cho đến khi bạn định cấu hình múi giờ cho tài khoản của mình, thời gian sẽ được hiểu là theo UTC. - time_label: 'Thời gian %{counter}:' - title: Gửi cho tôi lời nhắc email hàng ngày về các thông báo chưa đọc - workdays: - title: Nhận lời nhắc qua email vào những ngày này - immediate: - title: Gửi cho tôi lời nhắc qua email - mentioned: Ngay lập tức khi ai đó @đề cập đến tôi - personal_reminder: Ngay lập tức khi tôi nhận được lời nhắc cá nhân - alerts: - title: Thông báo qua email cho các mục khác (không phải là gói công việc) - explanation: 'Thông báo ngày hôm nay được giới hạn trong các gói công việc. Bạn có thể chọn tiếp tục nhận thông báo qua email cho những sự kiện này cho đến khi chúng được đưa vào thông báo: - - ' - news_added: Đã thêm tin tức - news_commented: Bình luận về một tin tức - document_added: Đã thêm tài liệu - forum_messages: Thông báo diễn đàn mới - wiki_page_added: Đã thêm trang Wiki - wiki_page_updated: Trang Wiki được cập nhật - membership_added: Đã thêm thành viên - membership_updated: Đã cập nhật tư cách thành viên - title: Lời nhắc qua email - pause: - label: Tạm dừng nhắc nhở email hàng ngày - first_day: Ngày đầu tiên - last_day: Ngày cuối cùng text_are_you_sure: Bạn có chắc không? text_are_you_sure_to_cancel: Bạn có những thay đổi chưa được lưu trên trang này. Bạn có chắc chắn muốn loại bỏ chúng không? breadcrumb: vụn bánh mì diff --git a/config/locales/crowdin/js-zh-CN.yml b/config/locales/crowdin/js-zh-CN.yml index 2d408dfa75b..887e1165909 100644 --- a/config/locales/crowdin/js-zh-CN.yml +++ b/config/locales/crowdin/js-zh-CN.yml @@ -591,51 +591,6 @@ zh-CN: settings: change_notification_settings: 您可以修改您的通知设置,以确保您不会错过重要的更新。 title: 通知设置 - notify_me: 通知我 - reminders: - no_notification: 无通知 - timeframes: - normal: - PT0S: 当天 - P1D: 提前 1 天 - P3D: 提前 3 天 - P7D: 提前一周 - overdue: - P1D: 每天 - P3D: 每 3 天 - P7D: 每周 - reasons: - mentioned: - title: 被提及 - description: 每当有人在任何位置提及我时都接收通知 - assignee: 受理人 - responsible: 负责人 - shared: 共享 - watched: 关注者 - work_package_commented: 所有新评论 - work_package_created: 新工作包 - work_package_processed: 所有状态更改 - work_package_prioritized: 所有优先级更改 - work_package_scheduled: 所有日期更改 - global: - immediately: - title: 参与 - description: 对于你参与的所有工作包(受理人、负责人或关注者),可以设置通知以接收所有活动的提醒。 - delayed: - title: 不参与 - description: 在所有项目中通知您所有活动。 - date_alerts: - title: 日期提醒 - description: 当你参与的开放工作包(作为受理人、负责人或关注者)的重要日期临近时,自动发送通知。 - overdue: 逾期时 - project_specific: - title: 项目特定通知设置 - description: '这些特定于项目的设置会覆盖上面的默认设置。 - - ' - add: 为项目添加设置 - already_selected: 此项目已经选定 - remove: 移除项目设置 pagination: no_other_page: 您位于唯一页面上。 pages_skipped: 跳过的页面。 @@ -659,39 +614,6 @@ zh-CN: required_outside_context: '请选择要创建工作包的项目以查看所有属性。 您只能选择已激活上述类型的项目。 ' - reminders: - settings: - daily: - add_time: 添加时间 - enable: 启用每日电子邮件提醒 - explanation: 只有存在未读通知时,您才会在指定的时间收到这些提醒。%{no_time_zone} - no_time_zone: 除非您为帐户配置时区,否则将以 UTC 显示时间。 - time_label: 时间 %{counter}: - title: 向我发送针对未读通知的每日电子邮件提醒 - workdays: - title: 在如下日期接收电子邮件提醒 - immediate: - title: 向我发送电子邮件提醒 - mentioned: 当有人@提及我时立即提醒 - personal_reminder: 收到个人提醒后立即发送 - alerts: - title: 其他项目(非工作包)的电子邮件提醒 - explanation: '今日提醒仅限于工作包。您可以选择继续接收这些事件的电子邮件提醒,直到它们被包含在通知中。 - - ' - news_added: 添加新闻 - news_commented: 评论新闻 - document_added: 添加文档 - forum_messages: 新的论坛消息 - wiki_page_added: 添加维基页面 - wiki_page_updated: 更新维基页面 - membership_added: 添加成员 - membership_updated: 更新成员 - title: 电子邮件提醒 - pause: - label: 临时暂停每日电子邮件提醒 - first_day: 第一天 - last_day: 最后一天 text_are_you_sure: 是否确定? text_are_you_sure_to_cancel: 您在此页面上有未保存的更改。您确定要放弃这些更改吗? breadcrumb: 面包屑导航 diff --git a/config/locales/crowdin/js-zh-TW.yml b/config/locales/crowdin/js-zh-TW.yml index 20f77ec0357..50e8a541dd6 100644 --- a/config/locales/crowdin/js-zh-TW.yml +++ b/config/locales/crowdin/js-zh-TW.yml @@ -591,51 +591,6 @@ zh-TW: settings: change_notification_settings: 您可以修改 通知設定,以確保您不會錯過重要更新。 title: 通知設定 - notify_me: 通知我 - reminders: - no_notification: 无通知 - timeframes: - normal: - PT0S: 當日 - P1D: 一天前 - P3D: 3 天前 - P7D: 1 週前 - overdue: - P1D: 每天 - P3D: 每 3 天 - P7D: 每週 - reasons: - mentioned: - title: 被提及 - description: 每當有人提及我時都收到通知 - assignee: 執行者 - responsible: 負責人 - shared: 參與 - watched: '監看者 - - ' - work_package_commented: 有新留言 - work_package_created: 新增工作套件 - work_package_processed: 所有狀態改變 - work_package_prioritized: 優先順序已變更 - work_package_scheduled: 所有日期變更 - global: - immediately: - title: 參與中 - description: 你參與的工作套件中所有活動的通知(執行者、負責人或監看者)。 - delayed: - title: 未參與之工作套件 - description: 額外通知 - date_alerts: - title: 日期提醒 - description: 當「執行者、負責人或監看者」之開啟的工作套件,其重要日期臨近時,自動發出通知。 - overdue: 逾期 - project_specific: - title: 特定專案通知設定 - description: 特別專案通知設定將覆蓋上列標準設定。 - add: 新增設定 - already_selected: 已選取此專案 - remove: 移除專案設定 pagination: no_other_page: 您位於唯一頁面上。 pages_skipped: 跳過的頁面。 @@ -659,39 +614,6 @@ zh-TW: required_outside_context: '請選擇專案以便新增工作套件以及檢視屬性。您只能選擇啟用中的專案。 ' - reminders: - settings: - daily: - add_time: 加入時間 - enable: 啟用每日電子郵件提醒 - explanation: 只有當未讀取通知時,您才會在指定的時間收到這些提醒。%{no_time_zone} - no_time_zone: 除非您為帳戶配置時區,否則將以 UTC 顯示時間。 - time_label: '時間 %{counter}:' - title: 每日將尚未讀取之通知,寄提醒郵件給我 - workdays: - title: 在以下日期收到電子郵件提醒 - immediate: - title: 寄發提醒郵件給我 - mentioned: 當有人使用「@帳號」提到我,系統立即發信通知我 - personal_reminder: 當我收到個人提醒時立即通知 - alerts: - title: 其他非工作套件之提醒 - explanation: '目前的通知僅限於工作套件。您可以選擇在這些事件納入通知系統之前,繼續透過電子郵件接收提醒: - - ' - news_added: 有最新消息 - news_commented: 最新消息有留言 - document_added: 文件已新增 - forum_messages: 有新的討論區訊息 - wiki_page_added: 新增維基頁面 - wiki_page_updated: 維基頁面有更新 - membership_added: 成員加入 - membership_updated: 成員更新 - title: 電子郵件提醒 - pause: - label: 暫時暫停每日電子郵件提醒 - first_day: 第一天 - last_day: 最後一天 text_are_you_sure: 是否確定? text_are_you_sure_to_cancel: 您在此頁面上有未儲存的變更。您確定要捨棄它們嗎? breadcrumb: 導覽軌跡 diff --git a/config/locales/crowdin/ka.yml b/config/locales/crowdin/ka.yml index 67cbfcc0c23..f9713425952 100644 --- a/config/locales/crowdin/ka.yml +++ b/config/locales/crowdin/ka.yml @@ -114,7 +114,7 @@ ka: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ka: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ka: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ ka: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ ka: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ ka: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ ka: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ ka: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: ჩვენება სადამდე attachment: @@ -3391,6 +3398,11 @@ ka: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ ka: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: დამსაქმებელი @@ -3484,6 +3562,7 @@ ka: invalid_filter: Invalid notification filter label_accessibility: წვდომადობა label_account: ანგარიში + label_actions: ქმედებები label_active: აქტიური label_activate_user: მომხმარებლის გააქტიურება label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ ka: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: უახლესი label_ical_access_key_revoke: გაუქმება + label_integrations: Integrations label_add_column: Add column label_applied_status: გადატარებულია სტატუსი label_archive_project: Archive project @@ -3774,7 +3854,7 @@ ka: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: საკვანძო სიტყვები label_language_based: Based on user's language label_last_activity: ბოლო აქტივობა @@ -5030,12 +5110,12 @@ ka: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/kk.yml b/config/locales/crowdin/kk.yml index c7aef34428b..9e5320f6d44 100644 --- a/config/locales/crowdin/kk.yml +++ b/config/locales/crowdin/kk.yml @@ -114,7 +114,7 @@ kk: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ kk: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ kk: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ kk: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ kk: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ kk: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ kk: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ kk: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ kk: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ kk: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ kk: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ kk: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ kk: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ kk: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index dc14bb7c0ef..83fe6b2e830 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -114,7 +114,7 @@ ko: import: title: 가져오기 jira: - title: Jira 가져오기 + title: Jira Migrator description: 이 도구를 사용하여 Jira 인스턴스에서 데이터를 가져옵니다. 여러 Jira 호스트를 구성하고 가져오기 실행 각각에서 가져올 항목을 선택할 수 있습니다. errors: cannot_delete_with_imports: 기존 가져오기가 있는 Jira 호스트는 삭제할 수 없습니다 @@ -125,8 +125,8 @@ ko: title: Jira 구성 new: 새 구성 banner: - title: 제한된 가져오기 - description: 이 가져오기 도구는 현재 베타 버전이며 프로젝트, 이슈(이름, 제목, 설명, 첨부 파일), 사용자(이름, 이메일, 프로젝트 멤버십), 상태 및 유형과 같은 기본 데이터만 가져올 수 있습니다. 워크플로, 사용자 지정 필드, 이슈 관계 또는 권한은 가져올 수 없습니다. Jira Server/Data Center 버전 10.x 및 11.x만 현재 지원합니다. 클라우드 인스턴스는 현재 지원되지 않습니다. + title: 제한된 가져오기 기능 + description: 이 Jira Migrator는 현재 베타 버전이며 프로젝트, 이슈(이름, 제목, 설명, 첨부 파일), 사용자(이름, 이메일, 프로젝트 멤버십), 상태 및 유형과 같은 기본 데이터만 가져올 수 있습니다. 워크플로, 사용자 지정 필드, 이슈 관계 또는 권한은 가져올 수 없습니다. Jira Server/Data Center 버전 10.x 및 11.x만 현재 지원됩니다. 클라우드 인스턴스는 현재 지원되지 않습니다. form: fields: name: 이름 @@ -162,7 +162,7 @@ ko: run: title: 가져오기 실행 history: 기록 - remove_error: Jira 가져오기는 실행되는 동안 제거할 수 없습니다 + remove_error: 실행되는 동안에는 Jira 가져오기 실행을 제거할 수 없습니다 import_blocked_error: 다른 Jira 가져오기 실행이 현재 진행 중이거나 검토를 기다리는 중입니다. 새 가져오기를 시작하기 전에 완료하거나 되돌리세요. project_identifier_taken: '이미 사용된 식별자가 있는 프로젝트를 가져오려고 합니다: %{taken_identifier}. Jira에서 프로젝트 식별자를 업데이트한 후에 ''다시 시도''를 클릭하세요.' blank: @@ -381,19 +381,23 @@ ko: notification_text_default: "

안녕하세요,

새 프로젝트가 생성되었습니다: projectValue:name

감사합니다.

\n" work_packages_identifier: page_header: - description: 기본 숫자 작업 패키지 ID 또는 작업 패키지 ID 앞에 프로젝트 식별자를 붙이는 프로젝트별 ID 중에서 선택합니다. + description: 클래식 숫자 작업 패키지 ID 또는 작업 패키지 ID 앞에 프로젝트 식별자를 붙이는 프로젝트별 시맨틱 ID 중에서 선택합니다. banner: - existing_identifiers_notice: "%{project_count}개 프로젝트의 기존 식별자가 프로젝트 기반 영숫자 식별자에 대한 요구 사항을 충족하지 않습니다. OpenProject는 아래 예와 같이 해당 식별자가 유효하도록 자동으로 업데이트할 수 있습니다. '자동 수정 및 저장'을 클릭하여 이러한 방식으로 모든 프로젝트의 식별자를 업데이트하고 프로젝트 기반 영숫자 식별자를 활성화하세요.\n" + existing_identifiers_notice: "%{project_count}개 프로젝트의 기존 식별자가 프로젝트 기반 시맨틱 식별자에 대한 요구 사항을 충족하지 않습니다. OpenProject는 아래 예와 같이 해당 식별자가 유효하도록 자동으로 업데이트할 수 있습니다. '자동 수정 및 저장'을 클릭하여 이러한 방식으로 모든 프로젝트의 식별자를 업데이트하고 프로젝트 기반 시맨틱 식별자를 활성화하세요.\n" box_header: label_project: 프로젝트 label_previous_identifier: 이전 식별자 label_autofixed_suggestion: 향후 식별자 label_example_work_package_id: 작업 패키지 ID 예 autofix_preview: - error_too_long: 5자 미만이어야 합니다 + error_too_long: 10자 이하여야 함 + error_numerical: 완전히 숫자로만 지정할 수 없음 + error_starts_with_number: 숫자로 시작할 수 없음 error_special_characters: 특수 문자는 허용되지 않음 + error_not_fully_uppercased: 대문자여야 함 error_in_use: 이미 다른 프로젝트의 활성 핸들로 사용 중입니다 error_reserved: 다른 프로젝트의 핸들 기록에 의해 예약되었습니다 + error_unknown: 수동 검토가 필요합니다 remaining_projects: other: "... %{count}개의 추가 프로젝트" button_autofix: 자동 수정 및 저장 @@ -407,7 +411,7 @@ ko: checkbox_label: 이렇게 하면 모든 작업 패키지 ID가 영구적으로 변경됨을 이해합니다 success_banner: 작업 패키지 식별자 형식을 업데이트했습니다. in_progress: - banner_message: 프로젝트 기반 영숫자 식별자로 프로젝트 식별자를 현재 업데이트하는 중입니다. 시간이 다소 걸릴 수 있습니다. + banner_message: 프로젝트 기반 시맨틱 식별자로 프로젝트 식별자를 현재 업데이트하는 중입니다. 시간이 다소 걸릴 수 있습니다. workflows: tabs: default_transitions: 기본 전환 @@ -1516,6 +1520,9 @@ ko: dependencies: 종속성 activerecord: attributes: + work_package_semantic_alias: + identifier: 식별자 + work_package: 작업 패키지 jira_import: projects: 프로젝트 import/jira: @@ -1524,7 +1531,7 @@ ko: personal_access_token: 개인 액세스 토큰 import/jira_open_project_reference: jira: Jira - jira_import: Jira 가져오기 + jira_import: Jira Migrator announcements: show_until: 표시 기한 attachment: @@ -3348,6 +3355,11 @@ ko: quick_add: label: 추가… my_account: + notifications_and_email: + title: 알림 및 이메일 + tabs: + notifications: 알림 설정 + email_reminders: 이메일 미리 알림 access_tokens: description: 공급자 토큰은 OpenProject에서 발급하며, 다른 애플리케이션이 액세스하도록 허용합니다. 클라이언트 토큰은 다른 애플리케이션에서 발급하며, OpenProject가 액세스하도록 허용합니다. no_results: @@ -3410,6 +3422,72 @@ ko: disabled_text: 관리자가 RSS 토큰을 활성화하지 않았습니다. 이 기능을 사용하려면 관리자에게 문의하세요. storages: unknown_storage: 알 수 없는 저장소 + email_reminders: + immediate_reminders: + title: 이메일 미리 알림 보내기 + mentioned: 내가 멘션되면 알림 보내기 + personal_reminder: 개인 미리 알림 보내기 + daily_reminders: + title: 읽지 않은 알림에 대한 일일 이메일 미리 알림 보내기 + caption: 읽지 않은 알림에 대해서만 그리고 사용자가 지정한 시간에만 이러한 미리 알림이 전송됩니다. 계정의 표준 시간대를 구성할 때까지, 시간은 UTC로 적용됩니다. + enabled: 일일 이메일 미리 알림 활성화 + add_time: 시간 추가 + remove_time: 시간 제거 + time_slot_label: 미리 알림 시간(UTC) + workdays: + title: 해당 요일에 이메일 미리 알림 받기 + submit_button: 미리 알림 요일 업데이트 + pause_reminders: + title: 이메일 알림 일시 중지 + enabled: 임시로 일일 이메일 미리 알림 일시 중지 + date_range: 일시 중지 기간 + email_alerts: + title: 작업 패키지가 아닌 기타 항목에 대한 이메일 알림 + news_added: 뉴스 추가됨 + news_commented: 뉴스 항목의 코멘트 + document_added: 문서 추가됨 + forum_messages: 포럼 메시지 게시됨 + wiki_page_added: 위키 페이지 추가됨 + wiki_page_updated: 위키 페이지 업데이트됨 + membership_added: 멤버십 추가됨 + membership_updated: 멤버십 업데이트됨 + submit_button: 알림 업데이트 + notifications: + participating: + title: 참여 + submit_button: 기본 설정 업데이트 + mentioned: 멘션됨 + watched: 지켜보는 중 + assignee: 담당자 + responsible: 담당 + shared: 나와 공유됨 + date_alerts: + title: 날짜 알림 + submit_button: 날짜 알림 업데이트 + start_date: 시작 날짜 + due_date: 완료 날짜 + overdue: 기한 지남 + times: + same_day: 같은 날 + one_day_before: 1일 전 + three_days_before: 3일 전 + seven_days_before: 7일 전 + one_day_after: 1일 후 + three_days_after: 3일 후 + seven_days_after: 7일 후 + non_participating: + title: 참여하지 않음 + submit_button: 기본 설정 업데이트 + work_package_created: 새 작업 패키지 + work_package_commented: 모든 새로운 코멘트 + work_package_processed: 모든 상태 변경 사항 + work_package_prioritized: 모든 우선 순위 변경 사항 + work_package_scheduled: 모든 날짜 변경 사항 + project_specific_settings: + title: 프로젝트별 알림 설정 + add_button: 프로젝트별 알림 추가 + dialog_title: 프로젝트별 알림 추가 + list_header: 특정 알림이 있는 프로젝트 notifications: reasons: assigned: 담당자 @@ -3441,6 +3519,7 @@ ko: invalid_filter: 잘못된 알림 필터 label_accessibility: 접근성 label_account: 계정 + label_actions: 작업 label_active: 활성 label_activate_user: 사용자 활성화 label_active_in_new_projects: 새로운 프로젝트에서 활성 @@ -3481,6 +3560,7 @@ ko: label_ical_access_key_generation_hint: 캘린더 구독 시 자동으로 생성됩니다. label_ical_access_key_latest: 최신 label_ical_access_key_revoke: 취소 + label_integrations: 통합 label_add_column: 열 추가 label_applied_status: 적용된 상태 label_archive_project: 프로젝트 아카이브하기 @@ -3731,7 +3811,7 @@ ko: label_external_links: 외부 링크 label_locale: 언어 및 지역 label_jump_to_a_project: 프로젝트로 이동... - label_jira_import: Jira 가져오기 + label_jira_import: Jira Migrator label_keyword_plural: 키워드 label_language_based: 사용자 언어에 따름 label_last_activity: 마지막 활동 @@ -4977,12 +5057,12 @@ ko: setting_welcome_text: 환영 블록 텍스트 setting_welcome_title: 환영 블록 제목 setting_welcome_on_homescreen: 홈 화면에 환영 블록 표시 - setting_work_packages_identifier_numeric: 인스턴스 전체의 숫자 시퀀스(기본값) - setting_work_packages_identifier_numeric_caption: '모든 작업 패키지는 1로 시작하는 순차 번호가 있으며, 새로운 패키지마다 순차 번호가 증가합니다. 이 번호는 해당 인스턴스 내에서 고유하므로 작업 패키지가 프로젝트 간에 이동되더라도 동일하게 유지됩니다. + setting_work_packages_identifier_classic: 인스턴스 전체의 숫자 시퀀스(기본값) + setting_work_packages_identifier_classic_caption: '모든 작업 패키지는 1로 시작하는 순차 번호가 있으며, 새로운 패키지마다 순차 번호가 증가합니다. 이 번호는 해당 인스턴스 내에서 고유하므로 작업 패키지가 프로젝트 간에 이동되더라도 동일하게 유지됩니다. ' - setting_work_packages_identifier_alphanumeric: 프로젝트 기반 영숫자 식별자 - setting_work_packages_identifier_alphanumeric_caption: '모든 프로젝트에는 작업 패키지 ID 앞에 붙는 고유 식별자가 있습니다. 작업 패키지가 다른 프로젝트로 이동된 경우 새 식별자가 생성되지만 이전 식별자도 계속 작동됩니다. + setting_work_packages_identifier_semantic: 프로젝트 기반 시맨틱 식별자 + setting_work_packages_identifier_semantic_caption: '모든 프로젝트에는 작업 패키지 ID 앞에 붙는 고유 식별자가 있습니다. 작업 패키지가 다른 프로젝트로 이동된 경우 새 식별자가 생성되지만 이전 식별자도 계속 작동됩니다. ' setting_work_package_list_default_highlighting_mode: 기본 강조 표시 모드 diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index 87167a5ac92..77d7dd90c65 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -114,7 +114,7 @@ lt: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ lt: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ lt: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +399,9 @@ lt: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +410,14 @@ lt: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -430,7 +434,7 @@ lt: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1571,6 +1575,9 @@ lt: dependencies: Priklausomybės activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1579,7 +1586,7 @@ lt: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Rodyti iki attachment: @@ -3508,6 +3515,11 @@ lt: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3570,6 +3582,72 @@ lt: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Nežinoma saugykla + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Paskirtas @@ -3601,6 +3679,7 @@ lt: invalid_filter: Invalid notification filter label_accessibility: Prieinamumas label_account: Paskyra + label_actions: Veiksmai label_active: Aktyvus label_activate_user: Aktyvus vartotojas label_active_in_new_projects: Aktyvus naujuose projektuose @@ -3641,6 +3720,7 @@ lt: label_ical_access_key_generation_hint: Automatiškai sukurta prenumeruojant kalendorių. label_ical_access_key_latest: vėliausias label_ical_access_key_revoke: Atšaukti + label_integrations: Integrations label_add_column: Add column label_applied_status: Taikomoji būsena label_archive_project: Archyvuoti projektą @@ -3891,7 +3971,7 @@ lt: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Peršokti į projektą... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Raktažodžiai label_language_based: Paremta vartotojo kalba label_last_activity: Paskutinė veikla @@ -5153,12 +5233,12 @@ lt: setting_welcome_text: Pasisveikinimo bloko tekstas setting_welcome_title: Pasisveikinimo bloko pavadinimas setting_welcome_on_homescreen: Rodyti pasisveikinimo bloką namų lange - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Numatytasis paryškinimo būdas diff --git a/config/locales/crowdin/lv.yml b/config/locales/crowdin/lv.yml index f9cbd4abdeb..ec895ee0b9c 100644 --- a/config/locales/crowdin/lv.yml +++ b/config/locales/crowdin/lv.yml @@ -114,7 +114,7 @@ lv: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ lv: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ lv: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +393,9 @@ lv: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +404,14 @@ lv: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: zero: "... %{count} more projects" one: "... 1 more project" @@ -423,7 +427,7 @@ lv: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1554,6 +1558,9 @@ lv: dependencies: Saistītie projekti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1569,7 @@ lv: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3450,6 +3457,11 @@ lv: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3512,6 +3524,72 @@ lv: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Pašreizējais atbildīgais @@ -3543,6 +3621,7 @@ lv: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Konts + label_actions: Actions label_active: Aktīvs label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3583,6 +3662,7 @@ lv: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3833,7 +3913,7 @@ lv: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Pēdējā aktivitāte @@ -5094,12 +5174,12 @@ lv: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/mn.yml b/config/locales/crowdin/mn.yml index d6b5e7ab996..f3e6408d694 100644 --- a/config/locales/crowdin/mn.yml +++ b/config/locales/crowdin/mn.yml @@ -114,7 +114,7 @@ mn: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ mn: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ mn: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ mn: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ mn: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ mn: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ mn: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ mn: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ mn: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ mn: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Даалгагч @@ -3484,6 +3562,7 @@ mn: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ mn: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ mn: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ mn: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ms.yml b/config/locales/crowdin/ms.yml index e7b986d491f..0394be4e87b 100644 --- a/config/locales/crowdin/ms.yml +++ b/config/locales/crowdin/ms.yml @@ -114,7 +114,7 @@ ms: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ms: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ms: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +381,9 @@ ms: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ ms: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ ms: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1516,6 +1520,9 @@ ms: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1524,7 +1531,7 @@ ms: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Paparkan sehingga attachment: @@ -3340,6 +3347,11 @@ ms: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3402,6 +3414,72 @@ ms: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Storan tidak dikenal pasti + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Penerima tugasan @@ -3433,6 +3511,7 @@ ms: invalid_filter: Invalid notification filter label_accessibility: Kebolehcapaian label_account: Akaun + label_actions: Tindakan label_active: Aktif label_activate_user: Aktifkan pengguna label_active_in_new_projects: Aktifkan dalam projek baharu @@ -3473,6 +3552,7 @@ ms: label_ical_access_key_generation_hint: Dijanakan secara automatik apabila melanggan kalendar. label_ical_access_key_latest: terkini label_ical_access_key_revoke: Batalkan + label_integrations: Integrations label_add_column: Tambah lajur label_applied_status: Status yang dilaksanakan label_archive_project: Arkib projek @@ -3725,7 +3805,7 @@ ms: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Lompat ke projek... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Kata kunci label_language_based: Berdasarkan bahasa pengguna label_last_activity: Aktiviti terakhir @@ -4976,12 +5056,12 @@ ms: setting_welcome_text: Teks blok selamat datang setting_welcome_title: Tajuk blok selamat datang setting_welcome_on_homescreen: Paparkan blok selamat datang di skrin utama - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Mod penyorotan default diff --git a/config/locales/crowdin/ne.yml b/config/locales/crowdin/ne.yml index d97f76fdf04..a14becd2f5b 100644 --- a/config/locales/crowdin/ne.yml +++ b/config/locales/crowdin/ne.yml @@ -114,7 +114,7 @@ ne: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ne: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ne: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ ne: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ ne: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ ne: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ ne: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ ne: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ ne: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ ne: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ ne: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: खाता + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ ne: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: रद्द गर्नुहोस् + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ ne: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: शब्दकुञ्जी label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ ne: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index e2640f523fb..14ff493d3c4 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -114,7 +114,7 @@ nl: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ nl: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ nl: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ nl: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ nl: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ nl: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1534,6 +1538,9 @@ nl: dependencies: Afhankelijkheden activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1542,7 +1549,7 @@ nl: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Toon tot attachment: @@ -3387,6 +3394,11 @@ nl: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3449,6 +3461,72 @@ nl: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Onbekende opslag + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Toegewezene @@ -3480,6 +3558,7 @@ nl: invalid_filter: Invalid notification filter label_accessibility: Toegankelijkheid label_account: Account + label_actions: Acties label_active: Actief label_activate_user: Gebruiker activeren label_active_in_new_projects: Actief in nieuwe projecten @@ -3520,6 +3599,7 @@ nl: label_ical_access_key_generation_hint: Automatisch gegenereerd bij het abonneren op een kalender. label_ical_access_key_latest: laatste label_ical_access_key_revoke: Intrekken + label_integrations: Integrations label_add_column: Add column label_applied_status: Toegepaste status label_archive_project: Project archiveren @@ -3770,7 +3850,7 @@ nl: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Ga naar een project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Trefwoorden label_language_based: Op basis van de taal van gebruiker label_last_activity: Laatste activiteit @@ -5016,12 +5096,12 @@ nl: setting_welcome_text: Welkom blok tekst setting_welcome_title: Welkom blok titel setting_welcome_on_homescreen: Toon het Welkom blok op thuisscherm - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standaardmodus markeren diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index d38d57f689f..8ebca314569 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -114,7 +114,7 @@ import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1534,6 +1538,9 @@ dependencies: Avhengigheter activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1542,7 +1549,7 @@ personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Vise til attachment: @@ -3389,6 +3396,11 @@ quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3451,6 +3463,72 @@ disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Ukjent lagring + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Deltaker @@ -3482,6 +3560,7 @@ invalid_filter: Invalid notification filter label_accessibility: Tilgjengelighet label_account: Konto + label_actions: Handlinger label_active: Aktiv label_activate_user: Aktiver bruker label_active_in_new_projects: Aktiv i nye prosjekter @@ -3522,6 +3601,7 @@ label_ical_access_key_generation_hint: Automatisk generert ved abonnering på en kalender. label_ical_access_key_latest: siste label_ical_access_key_revoke: Tilbakekall + label_integrations: Integrations label_add_column: Add column label_applied_status: Tildelt status label_archive_project: Arkiver prosjekt @@ -3772,7 +3852,7 @@ label_external_links: External links label_locale: Language and region label_jump_to_a_project: Gå til et prosjekt... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Nøkkelord label_language_based: Basert på brukerens språk label_last_activity: Siste aktivitet @@ -5028,12 +5108,12 @@ setting_welcome_text: Velkommen blokktekst setting_welcome_title: Velkomst blokk tittel setting_welcome_on_homescreen: Vis velkomstblokk på hjemskjermen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standard utheving modus diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index 22913448bf3..fbae2cc8eae 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -114,7 +114,7 @@ pl: import: title: Import jira: - title: Import Jira + title: Migrator Jira description: To narzędzie służy do importowania danych z wystąpienia usługi Jira. Można skonfigurować wiele hostów usługi Jira i wybrać, co ma być importowane w każdym przebiegu importu. errors: cannot_delete_with_imports: Nie można usunąć hosta Jira z istniejącym importem @@ -125,8 +125,8 @@ pl: title: Konfiguracja usługi Jira new: Nowa konfiguracja banner: - title: Ograniczony import - description: 'To narzędzie importu jest obecnie w wersji beta i może importować tylko podstawowe dane: projekty, zgłoszenia problemów (nazwa, tytuł, opis, załączniki), użytkowników (nazwa, adres e-mail, członkostwo w projekcie), statusy i typy. Nie może importować przepływów pracy, pól niestandardowych, relacji między problemami ani uprawnień. Obecnie obsługujemy tylko serwery / centra danych Jira w wersjach 10.x i 11.x. Wystąpienia w chmurze nie są obecnie obsługiwane.' + title: Ograniczone możliwości importu + description: 'Ten migrator Jira jest obecnie w wersji beta i może importować tylko podstawowe dane: projekty, zgłoszenia problemów (nazwa, tytuł, opis, załączniki), użytkowników (nazwa, adres e-mail, członkostwo w projekcie), statusy i typy. Nie może importować przepływów pracy, pól niestandardowych, relacji między problemami ani uprawnień. Obecnie obsługujemy tylko serwery / centra danych Jira w wersjach 10.x i 11.x. Wystąpienia w chmurze nie są obecnie obsługiwane.' form: fields: name: Nazwa @@ -399,9 +399,9 @@ pl: notification_text_default: "

Witaj,

Utworzono nowy projekt: projectValue:name

Dziękujemy

\n" work_packages_identifier: page_header: - description: Wybierz między podstawowymi identyfikatorami liczbowymi pakietów roboczych a identyfikatorami specyficznymi dla projektu, które dodają identyfikator projektu jako prefiks do identyfikatora pakietu roboczego. + description: Wybierz pomiędzy klasycznymi numerycznymi identyfikatorami pakietów roboczych albo semantycznymi identyfikatorami specyficznymi dla projektu, które dodają identyfikator projektu do identyfikatora pakietu roboczego. banner: - existing_identifiers_notice: 'Istniejące identyfikatory %{project_count} projektów nie spełniają wymagań dotyczących identyfikatorów alfanumerycznych opartych na projektach. OpenProject może automatycznie zaktualizować je tak, aby były prawidłowe, jak w poniższych przykładach. Kliknij przycisk „Automatyczne napraw i zapisz”, aby zaktualizować identyfikatory wszystkich projektów w ten sposób i włączyć identyfikatory alfanumeryczne oparte na projektach. + existing_identifiers_notice: 'Istniejące identyfikatory %{project_count} projektów nie spełniają wymagań dotyczących identyfikatorów semantycznych opartych na projektach. OpenProject może automatycznie zaktualizować je tak, aby były prawidłowe, jak w poniższych przykładach. Kliknij przycisk „Automatyczne napraw i zapisz”, aby zaktualizować identyfikatory wszystkich projektów w ten sposób i włączyć identyfikatory semantyczne oparte na projektach. ' box_header: @@ -410,10 +410,14 @@ pl: label_autofixed_suggestion: Przyszły identyfikator label_example_work_package_id: Przykładowy identyfikator pakietu roboczego autofix_preview: - error_too_long: Musi składać się z mniej niż 5 znaków + error_too_long: Musi składać się z nie więcej niż 10 znaków + error_numerical: Nie może być czysto liczbowy + error_starts_with_number: Nie może zaczynać się od cyfry error_special_characters: Znaki specjalne są niedozwolone + error_not_fully_uppercased: Musi być pisany wielkimi literami error_in_use: Już używany jako aktywne dojście innego projektu error_reserved: Zarezerwowany przez historię dojść innego projektu + error_unknown: Wymaga ręcznego sprawdzenia remaining_projects: one: "...jeszcze 1 projekt" few: "...jeszcze %{count} projekty" @@ -430,7 +434,7 @@ pl: checkbox_label: Rozumiem, że spowoduje to trwałą zmianę wszystkich identyfikatorów pakietów roboczych success_banner: Pomyślnie zaktualizowano format identyfikatora pakietu roboczego. in_progress: - banner_message: Identyfikatory projektów są obecnie zmieniane na identyfikatory alfanumeryczne oparte na projektach. Może to zająć trochę czasu. + banner_message: Identyfikatory projektów są obecnie zmieniane na identyfikatory semantyczne oparte na projektach. Może to zająć trochę czasu. workflows: tabs: default_transitions: Przejścia domyślne @@ -1568,6 +1572,9 @@ pl: dependencies: Zależności activerecord: attributes: + work_package_semantic_alias: + identifier: Identyfikator + work_package: Pakiet roboczy jira_import: projects: Projekty import/jira: @@ -1576,7 +1583,7 @@ pl: personal_access_token: Osobisty token dostępu import/jira_open_project_reference: jira: Jira - jira_import: Import Jira + jira_import: Migrator Jira announcements: show_until: Wyświetlaj do attachment: @@ -3501,6 +3508,11 @@ pl: quick_add: label: Dodaj… my_account: + notifications_and_email: + title: Powiadomienia i wiadomości e-mail + tabs: + notifications: Ustawienia powiadomień + email_reminders: Przypomnienia e-mail access_tokens: description: Tokeny dostawcy są wydawane przez OpenProject, co umożliwia dostęp do nich innym aplikacjom. Tokeny klienta są wydawane przez inne aplikacje, co umożliwia dostęp do nich oprogramowaniu OpenProject. no_results: @@ -3563,6 +3575,72 @@ pl: disabled_text: Tokeny RSS nie zostały włączone przez administratora. Aby użyć tej funkcji, skontaktuj się z administratorem. storages: unknown_storage: Nieznany magazyn + email_reminders: + immediate_reminders: + title: Wyślij mi przypomnienie e-mail + mentioned: Powiadamiaj mnie, gdy ktoś mnie wspomni + personal_reminder: Powiadamiaj mnie o osobistych przypomnieniach + daily_reminders: + title: Wyślij mi codzienne przypomnienia e-mail dla nieprzeczytanych powiadomień + caption: Będziesz otrzymywać te przypomnienia tylko w przypadku nieprzeczytanych powiadomień i tylko w określonych przez Ciebie godzinach. Dopóki nie skonfigurujesz strefy czasowej dla swojego konta, godziny będą interpretowane jako UTC. + enabled: Włącz codzienne przypomnienia e-mail + add_time: Dodaj godzinę + remove_time: Usuń godzinę + time_slot_label: Czas przypomnienia (UTC) + workdays: + title: Otrzymuj przypomnienia e-mail w tych dniach + submit_button: Zaktualizuj dni przypomnienia + pause_reminders: + title: Wstrzymaj powiadomienia e-mail + enabled: Tymczasowo wstrzymuj codzienne przypomnienia e-mail + date_range: Okres wstrzymania + email_alerts: + title: Powiadomienia e-mail dotyczące innych elementów, które nie są pakietami roboczymi + news_added: Wiadomość dodana + news_commented: Komentarz do nowości + document_added: Dodano dokument + forum_messages: Wiadomość na forum + wiki_page_added: Dodano stronę wiki + wiki_page_updated: Zaktualizowano stronę wiki + membership_added: Dodanie członkostwa + membership_updated: Aktualizacja członkostwa + submit_button: Zaktualizuj alerty + notifications: + participating: + title: Uczestnictwo + submit_button: Zaktualizuj preferencje + mentioned: Wzmianka + watched: Obserwowane + assignee: Przypisana osoba + responsible: Osoba odpowiedzialna + shared: Udostępnione dla mnie + date_alerts: + title: Alerty dotyczące dat + submit_button: Zaktualizuj alerty dotyczące dat + start_date: Data rozpoczęcia + due_date: Data zakończenia + overdue: Przekroczony termin + times: + same_day: Tego samego dnia + one_day_before: 1 dzień przed + three_days_before: 3 dni przed + seven_days_before: 7 dni przed + one_day_after: 1 dzień po + three_days_after: 3 dni po + seven_days_after: 7 dni po + non_participating: + title: Brak uczestnictwa + submit_button: Zaktualizuj preferencje + work_package_created: Nowe pakiety robocze + work_package_commented: Wszystkie nowe komentarze + work_package_processed: Wszystkie zmiany statusu + work_package_prioritized: Wszystkie zmiany priorytetów + work_package_scheduled: Wszystkie zmiany daty + project_specific_settings: + title: Ustawienia powiadomień dla danego projektu + add_button: Dodaj powiadomienia specyficzne dla projektu + dialog_title: Dodaj powiadomienia specyficzne dla projektu + list_header: Projekty z określonymi powiadomieniami notifications: reasons: assigned: Przypisana osoba @@ -3594,6 +3672,7 @@ pl: invalid_filter: Nieprawidłowy filtr powiadomień label_accessibility: Dostępność label_account: Konto + label_actions: Działania label_active: Aktywne label_activate_user: Aktywuj użytkownika label_active_in_new_projects: Aktywne w nowych projektach @@ -3634,6 +3713,7 @@ pl: label_ical_access_key_generation_hint: Automatycznie wygenerowano podczas subskrypcji kalendarza. label_ical_access_key_latest: najnowszy label_ical_access_key_revoke: Odwołaj + label_integrations: Integracje label_add_column: Dodaj kolumnę label_applied_status: Nadaj status label_archive_project: Archiwum projektów @@ -3884,7 +3964,7 @@ pl: label_external_links: Linki zewnętrzne label_locale: Język i region label_jump_to_a_project: Skok do projektu... - label_jira_import: Import Jira + label_jira_import: Migrator Jira label_keyword_plural: Słowa kluczowe label_language_based: Na podstawie języka użytkownika label_last_activity: Ostatnia aktywność @@ -5142,12 +5222,12 @@ pl: setting_welcome_text: Tekst bloku powitalnego setting_welcome_title: Tytuł bloku powitalnego setting_welcome_on_homescreen: Wyświetl wiadomość powitalną na ekranie głównym - setting_work_packages_identifier_numeric: Sekwencja liczbowa dla całego wystąpienia (domyślnie) - setting_work_packages_identifier_numeric_caption: 'Każdy pakiet roboczy otrzymuje numer kolejny zaczynający się od 1 i zwiększany z każdym nowym pakietem. Numery są unikalne w ramach tego wystąpienia, więc pozostają takie same, nawet jeśli pakiety robocze są przenoszone między projektami. + setting_work_packages_identifier_classic: Sekwencja liczbowa dla całego wystąpienia (domyślnie) + setting_work_packages_identifier_classic_caption: 'Każdy pakiet roboczy otrzymuje numer kolejny zaczynający się od 1 i zwiększany z każdym nowym pakietem. Numery są unikalne w ramach tego wystąpienia, więc pozostają takie same, nawet jeśli pakiety robocze są przenoszone między projektami. ' - setting_work_packages_identifier_alphanumeric: Identyfikatory alfanumeryczne oparte na projektach - setting_work_packages_identifier_alphanumeric_caption: 'Każdy projekt ma unikalny identyfikator, który jest poprzedzony identyfikatorem pakietu roboczego. Jeśli pakiet roboczy zostanie przeniesiony do innego projektu, generowany jest nowy identyfikator, ale stary nadal działa. + setting_work_packages_identifier_semantic: Identyfikatory semantyczne oparte na projektach + setting_work_packages_identifier_semantic_caption: 'Każdy projekt ma unikalny identyfikator, który jest poprzedzony identyfikatorem pakietu roboczego. Jeśli pakiet roboczy zostanie przeniesiony do innego projektu, generowany jest nowy identyfikator, ale stary nadal działa. ' setting_work_package_list_default_highlighting_mode: Domyślny tryb wyróżniania diff --git a/config/locales/crowdin/pt-BR.yml b/config/locales/crowdin/pt-BR.yml index 871ae418944..71cba25ed29 100644 --- a/config/locales/crowdin/pt-BR.yml +++ b/config/locales/crowdin/pt-BR.yml @@ -114,7 +114,7 @@ pt-BR: import: title: Importar jira: - title: Importação do Jira + title: Migrador do Jira description: Use esta ferramenta para importar dados da sua instância do Jira. Você pode configurar vários hosts do Jira e escolher o que importar em cada execução de importação. errors: cannot_delete_with_imports: Não é possível excluir o host do Jira enquanto houver importações existentes @@ -125,8 +125,8 @@ pt-BR: title: Configuração do Jira new: Nova configuração banner: - title: Importação limitada - description: 'Esta ferramenta de importação está atualmente em versão beta e só consegue importar dados básicos: projetos, tarefas (nome, título, descrição, anexos), usuários (nome, e-mail, participação em projetos), status e tipos. Não é possível importar fluxos de trabalho, campos personalizados, relações entre tarefas ou permissões. No momento, só damos suporte às versões 10.x e 11.x do Jira Server/Data Center. Instâncias Cloud não possuem suporte.' + title: Capacidades de importação limitadas + description: 'Este Migrador do Jira está atualmente em versão beta e só consegue importar dados básicos: projetos, chamados (nome, título, descrição, anexos), usuários (nome, e-mail, participação em projetos), status e tipos. Ele não consegue importar fluxos de trabalho, campos personalizados, relações entre chamados ou permissões. No momento, só são suportadas as versões Jira Server/Data Center 10.x e 11.x. Instâncias na nuvem (Cloud) não são compatíveis atualmente.' form: fields: name: Nome @@ -387,9 +387,9 @@ pt-BR: notification_text_default: "

Olá,

Um novo projeto foi criado: projectValue:name

Obrigado

\n" work_packages_identifier: page_header: - description: Escolha entre IDs de pacotes de trabalho numéricos básicos ou IDs específicos do projeto, que adicionam o identificador do projeto ao ID do pacote de trabalho. + description: Escolha entre IDs de pacotes de trabalho numéricos clássicos ou IDs semânticos específicos do projeto, que adicionam o identificador do projeto ao ID do pacote de trabalho. banner: - existing_identifiers_notice: 'Os identificadores existentes de %{project_count} projetos não atendem aos requisitos para identificadores alfanuméricos baseados em projeto. O OpenProject pode atualizá-los automaticamente para que fiquem válidos, como nos exemplos abaixo. Clique em “Autocorrigir e salvar” para atualizar os identificadores de todos os projetos dessa forma e ativar os Ids alfanuméricos baseados em projeto. + existing_identifiers_notice: 'Os identificadores existentes de %{project_count} projetos não atendem aos requisitos para Ids semânticos baseados em projeto. O OpenProject pode atualizá-los automaticamente para que fiquem válidos, como nos exemplos abaixo. Clique em “Autocorrigir e salvar” para atualizar os identificadores de todos os projetos dessa forma e ativar os Ids semânticos baseados em projeto. ' box_header: @@ -398,10 +398,14 @@ pt-BR: label_autofixed_suggestion: Identificador futuro label_example_work_package_id: Exemplo de ID de pacote de trabalho autofix_preview: - error_too_long: Deve ter menos de 5 caracteres + error_too_long: Deve conter no máximo 10 caracteres + error_numerical: Não pode ser exclusivamente numérico + error_starts_with_number: Não pode começar com um número error_special_characters: Caracteres especiais não são permitidos + error_not_fully_uppercased: Deve estar em maiúsculas error_in_use: Já está em uso como identificador ativo de outro projeto error_reserved: Reservado pelo histórico de identificadores de outro projeto + error_unknown: Requer revisão manual remaining_projects: one: "… mais 1 projeto" other: "… mais %{count} projetos" @@ -416,7 +420,7 @@ pt-BR: checkbox_label: Estou ciente de que isso alterará permanentemente todos os IDs dos pacotes de trabalho success_banner: Formato de identificador de pacote de trabalho atualizado com sucesso. in_progress: - banner_message: Os identificadores dos projetos estão sendo atualizados para identificadores alfanuméricos baseados em projeto. Isso pode levar algum tempo. + banner_message: Os identificadores dos projetos estão sendo atualizados para identificadores semânticos baseados em projeto. Isso pode levar algum tempo. workflows: tabs: default_transitions: Transições padrão @@ -1533,6 +1537,9 @@ pt-BR: dependencies: Dependências activerecord: attributes: + work_package_semantic_alias: + identifier: Identificador + work_package: Pacote de trabalho jira_import: projects: Projetos import/jira: @@ -1541,7 +1548,7 @@ pt-BR: personal_access_token: Token de acesso de pessoal import/jira_open_project_reference: jira: Jira - jira_import: Importação do Jira + jira_import: Migrador do Jira announcements: show_until: Exibir até attachment: @@ -3388,6 +3395,11 @@ pt-BR: quick_add: label: Adicionar… my_account: + notifications_and_email: + title: Notificação e e-mail + tabs: + notifications: Configurações de notificação + email_reminders: Lembretes por e-mail access_tokens: description: Tokens de provedor são emitidos pelo OpenProject, permitindo que outros aplicativos tenham acesso a ele. Tokens de cliente são emitidos por outros aplicativos, permitindo que o OpenProject tenha acesso a eles. no_results: @@ -3450,6 +3462,72 @@ pt-BR: disabled_text: Tokens RSS não estão habilitados pelo administrador. Entre em contato com ele para usar este recurso. storages: unknown_storage: Armazenamento desconhecido + email_reminders: + immediate_reminders: + title: Envie-me um lembrete por e-mail + mentioned: Avise-me quando eu for mencionado + personal_reminder: Notificar-me sobre lembretes pessoais + daily_reminders: + title: Enviar lembretes diários por e-mail para notificações não lidas + caption: Você receberá esses lembretes apenas para notificações não lidas e somente nos horários que você especificar. Até que você configure um fuso horário para sua conta, os horários serão interpretados como UTC. + enabled: Ativar lembretes diários por e-mail + add_time: Adicionar horário + remove_time: Remover horário + time_slot_label: Horário do lembrete (UTC) + workdays: + title: Receber lembretes por e-mail nestes dias + submit_button: Atualizar dias do lembrete + pause_reminders: + title: Pausar notificações por e-mail + enabled: Pausar temporariamente os lembretes diários por e-mail + date_range: Período de pausa + email_alerts: + title: Alertas por e-mail para outros itens que não sejam pacotes de trabalho + news_added: Notícia adicionada + news_commented: Comentar em uma notícia + document_added: Documento adicionado + forum_messages: Mensagem publicada no fórum + wiki_page_added: Página wiki adicionada + wiki_page_updated: Página wiki atualizada + membership_added: Associação adicionada + membership_updated: Associação atualizada + submit_button: Atualizar alertas + notifications: + participating: + title: Participando + submit_button: Atualizar preferências + mentioned: Mencionado + watched: Assistindo + assignee: Encarregado + responsible: Responsável + shared: Compartilhados comigo + date_alerts: + title: Alertas de data + submit_button: Atualizar alertas de data + start_date: Data de início + due_date: Data de conclusão + overdue: Atrasado + times: + same_day: No mesmo dia + one_day_before: 1 dia antes + three_days_before: 3 dias antes + seven_days_before: 7 dias antes + one_day_after: 1 dia depois + three_days_after: 3 dias depois + seven_days_after: 7 dias depois + non_participating: + title: Não participando + submit_button: Atualizar preferências + work_package_created: Novos pacotes de trabalho + work_package_commented: Todos os novos comentários + work_package_processed: Todas as mudanças de status + work_package_prioritized: Todas as mudanças de prioridade + work_package_scheduled: Todas as mudanças de data + project_specific_settings: + title: Configurações de notificação específicas de projetos + add_button: Adicionar notificações específicas do projeto + dialog_title: Adicionar notificações específicas do projeto + list_header: Projetos com notificações específicas notifications: reasons: assigned: Cessionário @@ -3481,6 +3559,7 @@ pt-BR: invalid_filter: Filtro de notificação inválido label_accessibility: Acessibilidade label_account: Conta + label_actions: Ações label_active: Ativo label_activate_user: Ativar usuário label_active_in_new_projects: Ativo em novos projetos @@ -3521,6 +3600,7 @@ pt-BR: label_ical_access_key_generation_hint: Gerado automaticamente ao assinar um calendário. label_ical_access_key_latest: último label_ical_access_key_revoke: Anular + label_integrations: Integrações label_add_column: Adicionar coluna label_applied_status: Situação aplicada label_archive_project: Arquivar projeto @@ -3771,7 +3851,7 @@ pt-BR: label_external_links: Links externos label_locale: Idioma e região label_jump_to_a_project: Saltar para um projeto... - label_jira_import: Importação do Jira + label_jira_import: Migrador do Jira label_keyword_plural: Palavras-chave label_language_based: Com base no idioma do usuário label_last_activity: Última atividade @@ -5018,12 +5098,12 @@ pt-BR: setting_welcome_text: Texto do bloco de boas-vindas setting_welcome_title: Título do bloco de boas-vindas setting_welcome_on_homescreen: Exibir bloco de boas-vindas na tela inicial - setting_work_packages_identifier_numeric: Sequência numérica global da instância (padrão) - setting_work_packages_identifier_numeric_caption: 'Cada pacote de trabalho recebe um número sequencial começando em 1, que é aumentado a cada novo item. Os números são únicos nesta instância, portanto permanecem os mesmos mesmo que os pacotes de trabalho sejam movidos entre projetos. + setting_work_packages_identifier_classic: Sequência numérica global da instância (padrão) + setting_work_packages_identifier_classic_caption: 'Cada pacote de trabalho recebe um número sequencial começando em 1, que é aumentado a cada novo item. Os números são únicos nesta instância, portanto permanecem os mesmos mesmo que os pacotes de trabalho sejam movidos entre projetos. ' - setting_work_packages_identifier_alphanumeric: Identificadores alfanuméricos baseados em projeto - setting_work_packages_identifier_alphanumeric_caption: 'Cada projeto possui um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for movido para outro projeto, um novo identificador é gerado, mas o antigo continua funcionando. + setting_work_packages_identifier_semantic: Identificadores semânticos baseados em projeto + setting_work_packages_identifier_semantic_caption: 'Cada projeto possui um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for movido para outro projeto, um novo identificador é gerado, mas o antigo continua funcionando. ' setting_work_package_list_default_highlighting_mode: Modo de destaque padrão diff --git a/config/locales/crowdin/pt-PT.yml b/config/locales/crowdin/pt-PT.yml index e55f5bb7117..f20648000c5 100644 --- a/config/locales/crowdin/pt-PT.yml +++ b/config/locales/crowdin/pt-PT.yml @@ -114,7 +114,7 @@ pt-PT: import: title: Importar jira: - title: Importação Jira + title: Jira Migrator description: Utilize esta ferramenta para importar dados da sua instância do Jira. Pode configurar vários anfitriões do Jira, e escolher o que importar em cada execução de importação. errors: cannot_delete_with_imports: Não é possível eliminar o anfitrião do Jira com importações existentes @@ -125,8 +125,8 @@ pt-PT: title: Configuração do Jira new: Nova configuração banner: - title: Importação limitada - description: 'Esta ferramenta de importação está em fase beta, e só consegue importar dados básicos: projetos, problemas (nome, título, descrição, anexos), utilizadores (nome, e-mail, associação ao projeto), estados e tipos. Não pode importar fluxos de trabalho, campos personalizados, relações de problemas ou permissões. Neste momento, só temos suporte para o servidor/centro de dados Jira nas versões 10.x e 11.x. As instâncias na nuvem não são suportadas agora.' + title: Capacidades de importação limitadas + description: 'Jira Migrator está em fase beta, e só consegue importar dados básicos: projetos, problemas (nome, título, descrição, anexos), utilizadores (nome, e-mail, associação ao projeto), estados e tipos. Não pode importar fluxos de trabalho, campos personalizados, relações de problemas ou permissões. Neste momento, só temos suporte para o servidor/centro de dados Jira nas versões 10.x e 11.x. As instâncias na nuvem não são suportadas agora.' form: fields: name: Nome @@ -162,7 +162,7 @@ pt-PT: run: title: Importar execução history: Histórico - remove_error: Uma importação do Jira não pode ser removida enquanto está a ser executada + remove_error: Uma execução de importação do Jira não pode ser eliminada enquanto estiver em execução import_blocked_error: Está em curso ou a aguardar revisão outra importação do Jira. Conclua-a ou reverta-a antes de iniciar uma nova importação. project_identifier_taken: 'Está a tentar importar um projeto com um identificador já utilizado: %{taken_identifier}. Atualize o identificador do projeto no Jira e clique em Tentar novamente.' blank: @@ -387,9 +387,9 @@ pt-PT: notification_text_default: "

Olá,

Foi criado um novo projeto: projectValue:name

Obrigado

\n" work_packages_identifier: page_header: - description: Escolha entre IDs numéricos básicos de pacotes de trabalho ou IDs específicos do projeto que acrescentam o identificador do projeto ao ID do pacote de trabalho. + description: Escolha entre IDs de pacotes de trabalho numéricos clássicos ou específicos de projetos semânticos, que acrescentam o identificador do projeto ao ID do pacote de trabalho. banner: - existing_identifiers_notice: 'Os identificadores existentes para os projec~tos %{project_count} não cumprem os requisitos para identificadores alfanuméricos baseados em projetos. O OpenProject pode atualizá-los automaticamente para que sejam válidos, como nos exemplos abaixo. Clique em "Corrigir automaticamente e guardar" para atualizar os identificadores de todos os projetos desta forma, e ativar os identificadores alfanuméricos baseados em projetos. + existing_identifiers_notice: 'Os identificadores existentes para os projec~tos %{project_count} não cumprem os requisitos para identificadores semânticos baseados em projetos. O OpenProject pode atualizá-los automaticamente para que sejam válidos, como nos exemplos abaixo. Clique em "Corrigir automaticamente e guardar" para atualizar os identificadores de todos os projetos desta forma, e ativar os identificadores semânticos baseados em projetos. ' box_header: @@ -398,10 +398,14 @@ pt-PT: label_autofixed_suggestion: Identificador de futuro label_example_work_package_id: Exemplo de ID de pacote de trabalho autofix_preview: - error_too_long: Tem de ter menos de 5 caracteres + error_too_long: Tem de ter 10 caracteres ou menos + error_numerical: Não pode ser puramente numérico + error_starts_with_number: Não pode começar com um número error_special_characters: Caracteres especiais não são permitidos + error_not_fully_uppercased: Deve estar em maiúsculas error_in_use: Já está a ser utilizado como identificador ativo de outro projeto error_reserved: Reservado pelo histórico do identificador de outro projeto + error_unknown: Precisa de revisão manual remaining_projects: one: "... mais 1 projeto" other: "... mais %{count} projetos" @@ -416,7 +420,7 @@ pt-PT: checkbox_label: Compreendo que isto irá alterar permanentemente todos os IDs dos pacotes de trabalho success_banner: Formato de identificador de pacote de trabalho atualizado com sucesso. in_progress: - banner_message: Os identificadores de projetos estão a ser atualizados para identificadores alfanuméricos baseados em projetos. Este processo pode demorar algum tempo. + banner_message: Os identificadores de projetos estão a ser atualizados para identificadores semânticos baseados em projetos. Este processo pode demorar algum tempo. workflows: tabs: default_transitions: Transições padrão @@ -1534,6 +1538,9 @@ pt-PT: dependencies: Dependências activerecord: attributes: + work_package_semantic_alias: + identifier: Identificador + work_package: Pacote de trabalho jira_import: projects: Projetos import/jira: @@ -1542,7 +1549,7 @@ pt-PT: personal_access_token: Token de acesso pessoal import/jira_open_project_reference: jira: Jira - jira_import: Importação Jira + jira_import: Jira Migrator announcements: show_until: Exibir até attachment: @@ -3389,6 +3396,11 @@ pt-PT: quick_add: label: Adicionar… my_account: + notifications_and_email: + title: Notificação e e-mail + tabs: + notifications: Definições de notificações + email_reminders: Lembretes por e-mail access_tokens: description: Os tokens de fornecedor são emitidos pelo OpenProject e permitem o acesso de outras aplicações. Os tokens de cliente são emitidos por outras aplicações e permitem que o OpenProject lhes aceda. no_results: @@ -3451,6 +3463,72 @@ pt-PT: disabled_text: Os tokens RSS não são ativados pelo administrador. Contacte o seu administrador para utilizar esta funcionalidade. storages: unknown_storage: Armazenamento desconhecido + email_reminders: + immediate_reminders: + title: Enviar-me um lembrete por e-mail + mentioned: Notificar-me quando eu for mencionado + personal_reminder: Notificar-me para receber lembretes pessoais + daily_reminders: + title: Enviar-me lembretes diários de e-mail para notificações não lidas + caption: Receberá estes lembretes apenas para notificações não lidas e apenas nas horas que especificar. Até configurar um fuso horário para a sua conta, as horas serão interpretadas em UTC. + enabled: Ativar lembretes diários por e-mail + add_time: Adicionar hora + remove_time: Remover hora + time_slot_label: Hora do lembrete (UTC) + workdays: + title: Receber lembretes por e-mail nestes dias + submit_button: Atualizar dias de lembrete + pause_reminders: + title: Pausar notificações por e-mail + enabled: Pausar temporariamente lembretes de e-mail diários + date_range: Período de pausa + email_alerts: + title: Alertas de e-mail para outros elementos que não pacotes de trabalho + news_added: Notícia adicionada + news_commented: Comentar numa notícia + document_added: Documento adicionado + forum_messages: Mensagem do fórum publicada + wiki_page_added: Página wiki adicionada + wiki_page_updated: Página wiki atualizada + membership_added: Adesão adicionada + membership_updated: Adesão atualizada + submit_button: Atualizar alertas + notifications: + participating: + title: Participante + submit_button: Atualizar preferências + mentioned: Mencionou + watched: A ver + assignee: Encarregado + responsible: Responsável + shared: Partilhado comigo + date_alerts: + title: Alertas de data + submit_button: Atualizar alertas de data + start_date: Data de início + due_date: Data de término + overdue: Em atraso + times: + same_day: No mesmo dia + one_day_before: 1 dia antes + three_days_before: 3 dias antes + seven_days_before: 7 dias antes + one_day_after: 1 dia depois + three_days_after: 3 dias depois + seven_days_after: 7 dias depois + non_participating: + title: Não participante + submit_button: Atualizar preferências + work_package_created: Novos pacotes de trabalho + work_package_commented: Todos os novos comentários + work_package_processed: Todas as mudanças de estado + work_package_prioritized: Todas as mudanças de prioridade + work_package_scheduled: Todas as alterações de datas + project_specific_settings: + title: Definições de notificação específicas do projeto + add_button: Adicionar notificações específicas do projeto + dialog_title: Adicionar notificações específicas do projeto + list_header: Projetos com notificações específicas notifications: reasons: assigned: Pessoa atribuída @@ -3482,6 +3560,7 @@ pt-PT: invalid_filter: Filtro de notificação inválido label_accessibility: Accessibilidade label_account: Conta + label_actions: Ações label_active: Ativo label_activate_user: Ativar utilizador label_active_in_new_projects: Ativo em novos projetos @@ -3522,6 +3601,7 @@ pt-PT: label_ical_access_key_generation_hint: Gerado automaticamente ao subscrever um calendário. label_ical_access_key_latest: recente label_ical_access_key_revoke: Revogar + label_integrations: Integrações label_add_column: Adicionar coluna label_applied_status: Status aplicado label_archive_project: Arquivar projeto @@ -3772,7 +3852,7 @@ pt-PT: label_external_links: Links externos label_locale: Idioma e região label_jump_to_a_project: Saltar para um projeto... - label_jira_import: Importação Jira + label_jira_import: Jira Migrator label_keyword_plural: Palavras-chave label_language_based: Com base no idioma do utilizador label_last_activity: Última atividade @@ -5013,12 +5093,12 @@ pt-PT: setting_welcome_text: Bloco de texto de boas-vindas setting_welcome_title: Título de texto de boas-vindas setting_welcome_on_homescreen: Exibir o bloco de boas-vindas no ecrã inicial - setting_work_packages_identifier_numeric: Sequência numérica a nível da instância (padrão) - setting_work_packages_identifier_numeric_caption: 'Cada pacote de trabalho recebe um número sequencial que começa em 1 e aumenta com cada novo pacote. Os números são únicos nesta instância, pelo que permanecem os mesmos, mesmo que os pacotes de trabalho sejam transferidos entre projetos. + setting_work_packages_identifier_classic: Sequência numérica a nível da instância (padrão) + setting_work_packages_identifier_classic_caption: 'Cada pacote de trabalho recebe um número sequencial que começa em 1 e aumenta com cada novo pacote. Os números são únicos nesta instância, pelo que permanecem os mesmos, mesmo que os pacotes de trabalho sejam transferidos entre projetos. ' - setting_work_packages_identifier_alphanumeric: Identificadores alfanuméricos baseados em projetos - setting_work_packages_identifier_alphanumeric_caption: 'Cada projeto tem um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for transferido para outro projeto, é gerado um novo identificador, mas o antigo continua a funcionar. + setting_work_packages_identifier_semantic: Identificadores semânticos baseados em projetos + setting_work_packages_identifier_semantic_caption: 'Cada projeto tem um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for transferido para outro projeto, é gerado um novo identificador, mas o antigo continua a funcionar. ' setting_work_package_list_default_highlighting_mode: Modo de destaque padrão diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index f2e163d0e24..8afc1ad46af 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -114,7 +114,7 @@ ro: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ro: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ ro: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +393,9 @@ ro: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +404,14 @@ ro: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -423,7 +427,7 @@ ro: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1554,6 +1558,9 @@ ro: dependencies: Dependenţe activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1569,7 @@ ro: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Afişare până la attachment: @@ -3450,6 +3457,11 @@ ro: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3512,6 +3524,72 @@ ro: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Executant @@ -3543,6 +3621,7 @@ ro: invalid_filter: Filtru de notificare invalid label_accessibility: Accesibilitate label_account: Cont + label_actions: Acțiuni label_active: Activ label_activate_user: Activați utilizatorul label_active_in_new_projects: Activ în proiecte noi @@ -3583,6 +3662,7 @@ ro: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revocă + label_integrations: Integrations label_add_column: Adaugă coloană label_applied_status: Stare aplicată label_archive_project: Arhivează proiect @@ -3726,7 +3806,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ă @@ -3783,7 +3863,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} @@ -3833,7 +3913,7 @@ ro: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Salt la un proiect... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Cuvinte cheie label_language_based: Bazat pe limba utilizatorului label_last_activity: Ultima activitate @@ -5092,12 +5172,12 @@ ro: setting_welcome_text: Text bloc "bun venit" setting_welcome_title: Titlu bloc "bun venit" setting_welcome_on_homescreen: Afişare bloc "bun venit" pe ecranul de start - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Mod de evidențiere implicit diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 20092e44195..0fa042d1e37 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -114,7 +114,7 @@ ru: import: title: Импорт jira: - title: Импорт из Jira + title: Мигратор Jira description: Используйте этот инструмент для импорта данных из Вашего экземпляра Jira. Вы можете настроить несколько узлов Jira и выбрать, что импортировать в каждом запуске импорта. errors: cannot_delete_with_imports: Невозможно удалить хост Jira с существующими импортами @@ -125,8 +125,8 @@ ru: title: Конфигурация Jira new: Новая конфигурация banner: - title: Ограниченный импорт - description: 'Этот инструмент импорта в настоящее время находится в стадии бета-тестирования и может импортировать только основные данные: проекты, проблемы (имя, название, описание, вложения), пользователей (имя, электронная почта, членство в проекте), статусы и типы. Он не может импортировать рабочие процессы, пользовательские поля, отношения между заданиями или разрешения. В настоящее время мы поддерживаем только Jira Server/Data Center версий 10.x и 11.x. Облачные экземпляры на данный момент не поддерживаются.' + title: Ограниченные возможности импорта + description: 'Этот мигратор Jira в настоящее время находится в бета-версии и может импортировать только основные данные: проекты, проблемы (имя, заголовок, описание, вложения), пользователей (имя, электронная почта, членство в проекте), статусы и типы. Он не может импортировать рабочие процессы, пользовательские поля, отношения между заданиями или разрешения. В настоящее время мы поддерживаем только Jira Server/Data Center версий 10.x и 11.x. Облачные экземпляры на данный момент не поддерживаются.' form: fields: name: Имя @@ -162,7 +162,7 @@ ru: run: title: Выполнение импорта history: История - remove_error: Импорт Jira нельзя удалить во время его выполнения + remove_error: Запуск импорта Jira не может быть удалён, пока он запущен import_blocked_error: Другая операция импорта Jira находится в процессе выполнения или ожидает рассмотрения. Пожалуйста, завершите или отмените его, прежде чем начинать новый импорт. project_identifier_taken: 'Вы пытаетесь импортировать проект с уже использованным идентификатором: %{taken_identifier}. Пожалуйста, обновите идентификатор проекта в Jira, затем нажмите «Повторить».' blank: @@ -399,9 +399,9 @@ ru: notification_text_default: "

Здравствуйте,

Был создан новый проект: projectValue:name

Спасибо

\n" work_packages_identifier: page_header: - description: Выберите между базовыми числовыми идентификаторами пакетов работ или специфическими для проекта, которые добавляют идентификатор проекта к идентификатору пакета работ. + description: Выберите между классическими числовыми идентификаторами пакета работ или семантическими, специфичными для проекта, которые добавляют идентификатор проекта к ID пакета работ. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Существующие идентификаторы для проектов %{project_count} не соответствуют требованиям, предъявляемым к семантическим идентификаторам. OpenProject может автоматически обновить их, чтобы они были действительными, как показано в примерах ниже. Щелкните на ''Исправить и сохранить'', чтобы обновить идентификаторы для всех проектов и включить семантические идентификаторы на основе проекта. ' box_header: @@ -410,10 +410,14 @@ ru: label_autofixed_suggestion: Следующий идентификатор label_example_work_package_id: Пример ID пакета работ autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Должно быть 10 символов или меньше + error_numerical: Не может быть чисто числовым + error_starts_with_number: Не может начинаться с цифры error_special_characters: Специальные символы не допускаются + error_not_fully_uppercased: Должно быть заглавными error_in_use: Уже используется в другом проекте error_reserved: Зарезервировано другим проектом + error_unknown: Требуется ручная проверка remaining_projects: one: "... еще 1 проект" few: "... %{count} more projects" @@ -430,7 +434,7 @@ ru: checkbox_label: Я понимаю, что это навсегда изменит все идентификаторы пакета работ success_banner: Формат идентификатора пакета работ успешно обновлен. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Идентификаторы проекта в настоящее время обновляются до семантических идентификаторов. Это может занять некоторое время. workflows: tabs: default_transitions: Переходы по умолчанию @@ -1575,6 +1579,9 @@ ru: dependencies: Связи activerecord: attributes: + work_package_semantic_alias: + identifier: Идентификатор + work_package: Пакет работ jira_import: projects: Проекты import/jira: @@ -1583,7 +1590,7 @@ ru: personal_access_token: Персональный токен доступа import/jira_open_project_reference: jira: Jira - jira_import: Импорт из Jira + jira_import: Мигратор Jira announcements: show_until: Отобразить до attachment: @@ -3514,6 +3521,11 @@ ru: quick_add: label: Добавить… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Настройки уведомлений + email_reminders: Почтовые напоминания access_tokens: description: Провайдерские токены выпускаются OpenProject, позволяя другим приложениям получать к ним доступ. Клиентские токены выпускаются другими приложениями, позволяя OpenProject получить к ним доступ. no_results: @@ -3576,6 +3588,72 @@ ru: disabled_text: Токены RSS не включены администратором. Пожалуйста, обратитесь к администратору, чтобы использовать эту функцию. storages: unknown_storage: Неизвестное хранилище + email_reminders: + immediate_reminders: + title: Отправить мне напоминание по электронной почте + mentioned: Сообщить мне, когда меня упомянут + personal_reminder: Уведомлять меня о личных напоминаниях + daily_reminders: + title: Отправлять мне ежедневные напоминания по электронной почте о непрочитанных уведомлениях + caption: Вы будете получать эти напоминания только для непрочитанных уведомлений и только в указанные Вами часы. Пока Вы не настроите часовой пояс для своей учетной записи, время будет интерпретироваться как UTC. + enabled: Включить ежедневные напоминания по электронной почте + add_time: Добавить время + remove_time: Удалить время + time_slot_label: Время напоминания (UTC) + workdays: + title: Получать напоминания по электронной почте в эти дни + submit_button: Обновить дни напоминаний + pause_reminders: + title: Приостановить уведомления по электронной почте + enabled: Временно приостановить ежедневные напоминания по электронной почте + date_range: Период приостановки + email_alerts: + title: Уведомления по электронной почте для других элементов (которые не являются пакетами работ) + news_added: Новость добавлена + news_commented: Комментарий к новости + document_added: Документ добавлен + forum_messages: Сообщение на форуме + wiki_page_added: Wiki-страница добавлена + wiki_page_updated: Wiki-страница обновлена + membership_added: Членство добавлено + membership_updated: Членство обновлено + submit_button: Обновить оповещения + notifications: + participating: + title: Участие + submit_button: Обновить настройки + mentioned: Упомянутый + watched: Отслеживает + assignee: Назначенный + responsible: Ответственный + shared: Доступные мне + date_alerts: + title: Дата оповещения + submit_button: Обновить дату оповещения + start_date: Дата начала + due_date: Дата окончания + overdue: Просрочено + times: + same_day: В тот же день + one_day_before: За 1 день до + three_days_before: За 3 дня до + seven_days_before: За 7 дней до + one_day_after: Через 1 день после + three_days_after: Через 3 дня после + seven_days_after: Через 7 дней после + non_participating: + title: Неучастие + submit_button: Обновить настройки + work_package_created: Новые пакеты работ + work_package_commented: Все новые комментарии + work_package_processed: Все изменения статуса + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Исполнитель @@ -3607,6 +3685,7 @@ ru: invalid_filter: Неверный фильтр уведомлений label_accessibility: Спец. возможности label_account: Учетная запись + label_actions: Действия label_active: Активен label_activate_user: Активировать пользователя label_active_in_new_projects: Активное участие в новых проектах @@ -3647,6 +3726,7 @@ ru: label_ical_access_key_generation_hint: Автоматически генерируется при подписке на календарь. label_ical_access_key_latest: последний label_ical_access_key_revoke: Отозвать + label_integrations: Integrations label_add_column: Добавить столбец label_applied_status: Прикладной статус label_archive_project: Архивировать проект @@ -3897,7 +3977,7 @@ ru: label_external_links: Внешние ссылки label_locale: Язык и регион label_jump_to_a_project: Перейти к проекту... - label_jira_import: Импорт из Jira + label_jira_import: Jira Migrator label_keyword_plural: Ключевые слова label_language_based: Основанный на языке пользователя label_last_activity: Последняя активность @@ -5149,12 +5229,12 @@ ru: setting_welcome_text: Текст приветствия setting_welcome_title: Заголовок приветствия setting_welcome_on_homescreen: Показывать приветствие на домашней странице - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Числовая последовательность для всего экземпляра (по умолчанию) + setting_work_packages_identifier_classic_caption: 'Каждый пакет работ получает порядковый номер, начинающийся с 1 и увеличивающийся с каждым новым пакетом. Номера уникальны в пределах данного экземпляра, поэтому они остаются неизменными, даже если пакеты работ перемещаются между проектами. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Семантические идентификаторы на основе проекта + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Способ выделения по умолчанию diff --git a/config/locales/crowdin/rw.yml b/config/locales/crowdin/rw.yml index 7711ffd5f64..9e66899ed88 100644 --- a/config/locales/crowdin/rw.yml +++ b/config/locales/crowdin/rw.yml @@ -114,7 +114,7 @@ rw: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ rw: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ rw: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ rw: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ rw: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ rw: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ rw: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ rw: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ rw: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ rw: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ rw: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ rw: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ rw: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ rw: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/si.yml b/config/locales/crowdin/si.yml index 9b00ffeec88..3f4d4774e59 100644 --- a/config/locales/crowdin/si.yml +++ b/config/locales/crowdin/si.yml @@ -114,7 +114,7 @@ si: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ si: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ si: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ si: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ si: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ si: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ si: dependencies: පරායත්තතා activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ si: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: ප්රදර්ශනය වන තුරු attachment: @@ -3391,6 +3398,11 @@ si: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ si: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: අස්ගිනී @@ -3484,6 +3562,7 @@ si: invalid_filter: Invalid notification filter label_accessibility: ප්රවේශ්යතාව label_account: ගිණුම + label_actions: ක්‍රියාමාර්ග label_active: ක්රියාකාරී label_activate_user: පරිශීලකයා සක්රිය කරන්න label_active_in_new_projects: නව ව්යාපෘතිවල ක්රියාකාරී වේ @@ -3524,6 +3603,7 @@ si: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: අවලංගු + label_integrations: Integrations label_add_column: Add column label_applied_status: ව්යවහාරික තත්ත්වය label_archive_project: සංරක්ෂිත ව්යාපෘතිය @@ -3774,7 +3854,7 @@ si: label_external_links: External links label_locale: Language and region label_jump_to_a_project: ව්යාපෘතියකට පනින්න... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: මූල පද label_language_based: පරිශීලකයාගේ භාෂාව මත පදනම්ව label_last_activity: අවසාන ක්රියාකාරකම් @@ -5030,12 +5110,12 @@ si: setting_welcome_text: බ්ලොක් පෙළ සාදරයෙන් පිළිගනිමු setting_welcome_title: වාරණ මාතෘකාව සාදරයෙන් පිළිගනිමු setting_welcome_on_homescreen: homescreen මත ප්රදර්ශනය පිළිගැනීමේ වාරණ - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: පෙරනිමි ඉස්මතු මාදිලිය diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index bf84e25d6c0..73c18c2c865 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -114,7 +114,7 @@ sk: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ sk: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ sk: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +399,9 @@ sk: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +410,14 @@ sk: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -430,7 +434,7 @@ sk: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1572,6 +1576,9 @@ sk: dependencies: Závislosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1580,7 +1587,7 @@ sk: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Zobrazovať, až kým attachment: @@ -3507,6 +3514,11 @@ sk: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3569,6 +3581,72 @@ sk: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Priradené @@ -3600,6 +3678,7 @@ sk: invalid_filter: Invalid notification filter label_accessibility: Prístupnosť label_account: Účet + label_actions: Akcie label_active: Aktívny label_activate_user: Aktívny užívateľ label_active_in_new_projects: Aktívny v nových projektoch @@ -3640,6 +3719,7 @@ sk: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Odvolať + label_integrations: Integrations label_add_column: Add column label_applied_status: Aplikovaný stav label_archive_project: Archivovať projekt @@ -3890,7 +3970,7 @@ sk: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Skok do projektu... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Kľúčové slová label_language_based: Založené na jazyku používateľa label_last_activity: Posledná aktivita @@ -5158,12 +5238,12 @@ sk: setting_welcome_text: Text pre uvítací blok setting_welcome_title: Názov uvítacieho bloku setting_welcome_on_homescreen: Zobraziť uvítací blok na úvodnej obrazovke - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Predvolený režim zvýraznenia diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index f066e4f040a..60db981553e 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -116,7 +116,7 @@ sl: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -127,8 +127,8 @@ sl: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -164,7 +164,7 @@ sl: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -401,9 +401,9 @@ sl: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -412,10 +412,14 @@ sl: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" two: "... %{count} more projects" @@ -432,7 +436,7 @@ sl: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1573,6 +1577,9 @@ sl: dependencies: Odvisnosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1581,7 +1588,7 @@ sl: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Prikaži do attachment: @@ -2866,8 +2873,8 @@ sl: - avgust - september - oktober - - november - - december + - November + - December order: - :leto - :mesec @@ -3520,6 +3527,11 @@ sl: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3582,6 +3594,72 @@ sl: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Prevzemnik @@ -3613,6 +3691,7 @@ sl: invalid_filter: Invalid notification filter label_accessibility: Dostopnost label_account: Račun + label_actions: Akcije label_active: Aktivno label_activate_user: Aktivirajte uporabnika label_active_in_new_projects: Aktivno v novih projektih @@ -3653,6 +3732,7 @@ sl: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Prekliči + label_integrations: Integrations label_add_column: Add column label_applied_status: Uveljavljeno stanje label_archive_project: Arhivirani projekti @@ -3903,7 +3983,7 @@ sl: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Skoči na projekt... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Ključne besede label_language_based: Glede na uporabnikov jezik label_last_activity: Zadnja aktivnost @@ -5181,12 +5261,12 @@ sl: setting_welcome_text: Dobrodošli blok besedila setting_welcome_title: Dobrodošli blok naslov setting_welcome_on_homescreen: Prikažite blok dobrodošlice na osnovnem zaslonu - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Privzeti način osvetlitve diff --git a/config/locales/crowdin/sr.yml b/config/locales/crowdin/sr.yml index 329a70a966d..52d8a9a495d 100644 --- a/config/locales/crowdin/sr.yml +++ b/config/locales/crowdin/sr.yml @@ -114,7 +114,7 @@ sr: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ sr: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ sr: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +393,9 @@ sr: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +404,14 @@ sr: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -423,7 +427,7 @@ sr: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1554,6 +1558,9 @@ sr: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1569,7 @@ sr: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3450,6 +3457,11 @@ sr: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3512,6 +3524,72 @@ sr: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Zadužen @@ -3543,6 +3621,7 @@ sr: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3583,6 +3662,7 @@ sr: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3833,7 +3913,7 @@ sr: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5094,12 +5174,12 @@ sr: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index a3b8b2602e8..e6356de41e6 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -114,7 +114,7 @@ sv: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ sv: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ sv: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ sv: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ sv: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ sv: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ sv: dependencies: Beroenden activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ sv: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Visa fram till attachment: @@ -3391,6 +3398,11 @@ sv: quick_add: label: Lägg till… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ sv: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Okänd lagring + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Tilldelad till @@ -3484,6 +3562,7 @@ sv: invalid_filter: Invalid notification filter label_accessibility: Tillgänglighet label_account: Konto + label_actions: Åtgärder label_active: Aktiv label_activate_user: Aktivera användare label_active_in_new_projects: Aktiv i nya projekt @@ -3524,6 +3603,7 @@ sv: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: senaste label_ical_access_key_revoke: Återkalla + label_integrations: Integrations label_add_column: Lägg till kolumn label_applied_status: Tillämpad status label_archive_project: Arkivera projekt @@ -3774,7 +3854,7 @@ sv: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Hoppa till projekt... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Nyckelord label_language_based: Baserat på användarens språk label_last_activity: Senaste aktivitet @@ -5018,12 +5098,12 @@ sv: setting_welcome_text: Textblock för välkomstmeddelande setting_welcome_title: Textblock för välkomsttitel setting_welcome_on_homescreen: Visa välkomstblocket på hemsidan - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standardläge för markering diff --git a/config/locales/crowdin/th.yml b/config/locales/crowdin/th.yml index a8f5aca3495..9f228725700 100644 --- a/config/locales/crowdin/th.yml +++ b/config/locales/crowdin/th.yml @@ -114,7 +114,7 @@ th: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ th: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ th: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +381,9 @@ th: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ th: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ th: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1518,6 +1522,9 @@ th: dependencies: ส่วนที่อ้างอิง activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1526,7 +1533,7 @@ th: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3332,6 +3339,11 @@ th: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3394,6 +3406,72 @@ th: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: ผู้ได้รับมอบหมาย @@ -3425,6 +3503,7 @@ th: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: บัญชี + label_actions: Actions label_active: ใช้งานอยู่ label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3465,6 +3544,7 @@ th: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: ใช้สถานะ label_archive_project: Archive project @@ -3715,7 +3795,7 @@ th: label_external_links: External links label_locale: Language and region label_jump_to_a_project: ไปยังโครงการ... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: ตามภาษาของผู้ใช้ label_last_activity: Last activity @@ -4966,12 +5046,12 @@ th: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index b490ab99604..8b719c6f6e7 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -114,7 +114,7 @@ tr: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ tr: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ tr: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ tr: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ tr: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ tr: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1537,6 +1541,9 @@ tr: dependencies: Bağımlılıklar activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1545,7 +1552,7 @@ tr: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Son yayın tarihi attachment: @@ -3396,6 +3403,11 @@ tr: quick_add: label: Ekle… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3458,6 +3470,72 @@ tr: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Bilinmeyen depolama + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Atanan @@ -3489,6 +3567,7 @@ tr: invalid_filter: Geçersiz bildirim filtresi label_accessibility: Erişilebilirlik label_account: Hesap + label_actions: Eylemler label_active: Aktif label_activate_user: Aktif kullanıcı label_active_in_new_projects: Yeni projelerde etkin @@ -3529,6 +3608,7 @@ tr: label_ical_access_key_generation_hint: Bir takvime abone olunduğunda otomatik olarak oluşturulur. label_ical_access_key_latest: en son label_ical_access_key_revoke: İptal et + label_integrations: Integrations label_add_column: Sütun ekle label_applied_status: Uygulanan statü label_archive_project: Projeyi arşivle @@ -3779,7 +3859,7 @@ tr: label_external_links: Harici Bağlantılar label_locale: Dil ve bölge label_jump_to_a_project: Projeye git... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Anahtar kelimeler label_language_based: Kullanıcının dili tabanlı label_last_activity: Son Aktivite @@ -5032,12 +5112,12 @@ tr: setting_welcome_text: Hoş geldiniz blok metini setting_welcome_title: Hoş geldiniz blok başlığı setting_welcome_on_homescreen: Hoşgeldiniz bloğunu ana ekranda göster - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Varsayılan vurgulama modu diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index ae2569fde7f..1b0c7fcef47 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -114,7 +114,7 @@ uk: import: title: Iмпорт jira: - title: Імпорт із Jira + title: Jira Migrator description: Використовуйте цей інструмент для імпорту даних зі свого екземпляра Jira. Ви можете налаштувати кілька хостів Jira й вибрати, що імпортувати в кожному циклі імпорту. errors: cannot_delete_with_imports: Не вдається видалити хост Jira з наявними процесами імпорту @@ -125,8 +125,8 @@ uk: title: Конфігурація Jira new: Нова конфігурація banner: - title: Обмежений імпорт - description: 'Цей інструмент зараз доступний лише як бета-версія і може імпортувати тільки основні дані: проєкти, задачі (назву, заголовок, опис, вкладення), користувачів (ім’я, електронну адресу, дані про участь у проєктах), статуси й типи. Він не може імпортувати робочі процеси, користувацькі поля, зв’язки між задачами чи дозволи. Зараз ми підтримуємо лише версії Jira Server / Data Center 10.x і 11.x. Хмарні екземпляри поки що не підтримуються.' + title: Обмежені можливості імпорту + description: 'Інструмент Jira Migrator зараз доступний лише як бета-версія і може імпортувати тільки основні дані: проєкти, задачі (назву, заголовок, опис, вкладення), користувачів (ім’я, електронну адресу, дані про участь у проєктах), статуси й типи. Він не може імпортувати робочі процеси, користувацькі поля, зв’язки між задачами чи дозволи. Зараз ми підтримуємо лише версії Jira Server / Data Center 10.x і 11.x. Хмарні екземпляри поки що не підтримуються.' form: fields: name: Назва @@ -162,7 +162,7 @@ uk: run: title: Цикл імпорту history: Історія - remove_error: Імпорт Jira не можна видалити, поки він виконується + remove_error: Цикл імпорту Jira не можна видалити, поки він виконується import_blocked_error: Зараз виконується або очікує на перевірку ще один цикл імпорту Jira. Завершіть або скасуйте його, якщо потрібно почати новий імпорт. project_identifier_taken: 'Ви намагаєтесь імпортувати проєкт з ідентифікатором, який уже використовується: %{taken_identifier}. Будь ласка, оновіть ідентифікатор проєкту в Jira, а потім натисніть на кнопку «Повторити спробу».' blank: @@ -399,9 +399,9 @@ uk: notification_text_default: "

Вітаємо,

Новий проєкт створено: projectValue:name

Дякуємо

\n" work_packages_identifier: page_header: - description: Виберіть звичайні числові ідентифікатори пакетів робіт або ідентифікатори із зазначенням проєкту (до ідентифікатора пакета робіт додається префікс з ідентифікатором проєкту). + description: Виберіть класичні числові ідентифікатори пакетів робіт або семантичні із зазначенням проєкту (до ідентифікатора пакета робіт додається префікс з ідентифікатором проєкту). banner: - existing_identifiers_notice: 'Наявні ідентифікатори для кількох проєктів (%{project_count}) не відповідають вимогам до буквено-цифрових ідентифікаторів із зазначенням проєктів. OpenProject може автоматично оновити їх, щоб вони стали дійсними, як показано в прикладах нижче. Натисніть кнопку «Виправити й зберегти», щоб оновити в такий спосіб ідентифікатори для всіх проєктів і ввімкнути буквено-цифрові ідентифікатори із зазначенням проєктів. + existing_identifiers_notice: 'Наявні ідентифікатори для кількох проєктів (%{project_count}) не відповідають вимогам до семантичних ідентифікаторів із зазначенням проєктів. OpenProject може автоматично оновити їх, щоб вони стали дійсними, як показано в прикладах нижче. Натисніть кнопку «Виправити й зберегти», щоб оновити в такий спосіб ідентифікатори для всіх проєктів і ввімкнути семантичні ідентифікатори із зазначенням проєктів. ' box_header: @@ -410,10 +410,14 @@ uk: label_autofixed_suggestion: Майбутній ідентифікатор label_example_work_package_id: Приклад ідентифікатора пакета робіт autofix_preview: - error_too_long: Має містити менше ніж 5 символів + error_too_long: Має містити щонайбільше 10 символів + error_numerical: Не може містити лише числа + error_starts_with_number: Не може починатись із числа error_special_characters: Не дозволяється використовувати спеціальні символи + error_not_fully_uppercased: Літери мають бути великими error_in_use: Уже використовується як активний дескриптор іншого проєкту error_reserved: Зарезервовано в історії дескрипторів іншого проєкту + error_unknown: Потребує ручної перевірки remaining_projects: one: "… ще 1 проєкт" few: "… ще %{count} проєкти" @@ -430,7 +434,7 @@ uk: checkbox_label: Я розумію, що ця дія назавжди змінить усі ідентифікатори пакетів робіт success_banner: Формат ідентифікаторів пакетів робіт успішно оновлено. in_progress: - banner_message: Ідентифікатори проєктів зараз замінюються на буквено-цифрові ідентифікатори із зазначенням проєктів. Це може зайняти деякий час. + banner_message: Ідентифікатори проєктів зараз замінюються на семантичні ідентифікатори із зазначенням проєктів. Це може зайняти деякий час. workflows: tabs: default_transitions: Стандартні переходи @@ -1567,6 +1571,9 @@ uk: dependencies: Залежності activerecord: attributes: + work_package_semantic_alias: + identifier: Ідентифікатор + work_package: Робочий пакет jira_import: projects: Проєкти import/jira: @@ -1575,7 +1582,7 @@ uk: personal_access_token: Персональний маркер доступу import/jira_open_project_reference: jira: Jira - jira_import: Імпорт із Jira + jira_import: Jira Migrator announcements: show_until: Показувати до attachment: @@ -3508,6 +3515,11 @@ uk: quick_add: label: Додати… my_account: + notifications_and_email: + title: Сповіщення й електронні листи + tabs: + notifications: Налаштування сповіщень + email_reminders: Нагадування електронною поштою access_tokens: description: Маркери постачальника послуг випускаються в OpenProject, що дає змогу іншим додаткам отримувати до них доступ. Клієнтські маркери випускаються іншими додатками, що дає змогу OpenProject отримати до них доступ. no_results: @@ -3570,6 +3582,72 @@ uk: disabled_text: Маркери API не ввімкнено адміністратором. Зверніться до нього, якщо вам потрібна ця функція. storages: unknown_storage: Невідоме сховище + email_reminders: + immediate_reminders: + title: Надсилати мені нагадування електронною поштою + mentioned: Повідомляти мене, коли мене згадують + personal_reminder: Повідомляти мене про персональні нагадування + daily_reminders: + title: Надсилати мені щоденні нагадування електронною поштою про непрочитані сповіщення + caption: Ви отримуватимете лише нагадування про непрочитані сповіщення, і вони надходитимуть тільки у вказаний вами час. Доки ви не налаштуєте для свого облікового запису часовий пояс, використовуватиметься UTC. + enabled: Увімкнути щоденні нагадування електронною поштою + add_time: Додати час + remove_time: Вилучити час + time_slot_label: Час нагадування (UTC) + workdays: + title: Отримувати нагадування електронною поштою в ці дні + submit_button: Оновити дні нагадувань + pause_reminders: + title: Призупинити сповіщення електронною поштою + enabled: Тимчасово призупинити щоденні нагадування, що надсилаються електронною поштою + date_range: Період призупинення + email_alerts: + title: Сповіщення електронною поштою для інших елементів, які не є пакетами робіт + news_added: Новину додано + news_commented: Коментар щодо новини + document_added: Документ додано + forum_messages: Повідомлення на форумі опубліковано + wiki_page_added: Wiki-сторінку додано + wiki_page_updated: Wiki-сторінку оновлено + membership_added: Членство додано + membership_updated: Членство оновлено + submit_button: Оновити сповіщення + notifications: + participating: + title: Бере участь + submit_button: Оновити налаштування + mentioned: Вас згадали + watched: Відстеження + assignee: Виконавець + responsible: Відповідальний + shared: Доступ надано мені + date_alerts: + title: Сповіщення про дати + submit_button: Оновити сповіщення про дати + start_date: Дата початку + due_date: Дата завершення + overdue: Прострочено + times: + same_day: Того самого дня + one_day_before: За 1 день + three_days_before: За 3 дні + seven_days_before: За 7 днів + one_day_after: Через 1 день + three_days_after: Через 3 дні + seven_days_after: Через 7 днів + non_participating: + title: Не бере участі + submit_button: Оновити налаштування + work_package_created: Нові пакети робіт + work_package_commented: Усі нові коментарі + work_package_processed: Усі зміни статусу + work_package_prioritized: Усі зміни пріоритету + work_package_scheduled: Усі зміни дат + project_specific_settings: + title: Налаштування сповіщень щодо проєктів + add_button: Додати сповіщення щодо проєктів + dialog_title: Додати сповіщення щодо проєктів + list_header: Проєкти зі спеціальними сповіщеннями notifications: reasons: assigned: Виконавець @@ -3601,6 +3679,7 @@ uk: invalid_filter: Недійсний фільтр сповіщень label_accessibility: Розробникам також потрібно оплачувати свої рахунки. З доступністю label_account: Обліковий запис + label_actions: Дії label_active: Активні label_activate_user: Активні користувачі label_active_in_new_projects: Активна участь в нових проектах @@ -3641,6 +3720,7 @@ uk: label_ical_access_key_generation_hint: Автоматично створено під час оформлення підписки на календар. label_ical_access_key_latest: останні label_ical_access_key_revoke: Анулювати + label_integrations: Інтеграції label_add_column: Додати стовпець label_applied_status: Застосовний статус label_archive_project: Архівний проект @@ -3869,7 +3949,7 @@ uk: label_index_by_title: Індекс за назвою label_information: Інформація label_information_plural: Інформація - label_installation_guides: Інструкції із встановлення + label_installation_guides: Інструкції зі встановлення label_integer: Ціле число label_interface: Інтерфейс label_internal: Власне @@ -3891,7 +3971,7 @@ uk: label_external_links: Зовнішні посилання label_locale: Мова й регіон label_jump_to_a_project: Перейти до проекту... - label_jira_import: Імпорт із Jira + label_jira_import: Jira Migrator label_keyword_plural: Ключові слова label_language_based: На основі мови користувача label_last_activity: Остання активність @@ -5156,12 +5236,12 @@ uk: setting_welcome_text: Текст блоку привітання setting_welcome_title: Заголовок вітального блоку setting_welcome_on_homescreen: Відображати вітальний блок на робочому столі - setting_work_packages_identifier_numeric: Числова послідовність для всього екземпляра (за умовчанням) - setting_work_packages_identifier_numeric_caption: 'Кожному пакету робіт присвоюється порядковий номер, який починається з одиниці й збільшується з кожним новим пакетом. Номери є унікальними в межах цього екземпляра, тому залишаються незмінними, навіть якщо пакети робіт переміщуються між проєктами. + setting_work_packages_identifier_classic: Числова послідовність для всього екземпляра (за умовчанням) + setting_work_packages_identifier_classic_caption: 'Кожному пакету робіт присвоюється порядковий номер, який починається з одиниці й збільшується з кожним новим пакетом. Номери є унікальними в межах цього екземпляра, тому залишаються незмінними, навіть якщо пакети робіт переміщуються між проєктами. ' - setting_work_packages_identifier_alphanumeric: Буквено-цифрові ідентифікатори із зазначенням проєктів - setting_work_packages_identifier_alphanumeric_caption: 'Кожен проєкт має унікальний ідентифікатор, який додається як префікс до ідентифікатора пакета робіт. Якщо пакет робіт переміщується в інший проєкт, генерується новий ідентифікатор, однак старий продовжує працювати. + setting_work_packages_identifier_semantic: Семантичні ідентифікатори із зазначенням проєктів + setting_work_packages_identifier_semantic_caption: 'Кожен проєкт має унікальний ідентифікатор, який додається як префікс до ідентифікатора пакета робіт. Якщо пакет робіт переміщується в інший проєкт, генерується новий ідентифікатор, однак старий продовжує працювати. ' setting_work_package_list_default_highlighting_mode: Режим виділення за умовчанням diff --git a/config/locales/crowdin/uz.yml b/config/locales/crowdin/uz.yml index a89d5fcb699..b2b0a6cf6bf 100644 --- a/config/locales/crowdin/uz.yml +++ b/config/locales/crowdin/uz.yml @@ -114,7 +114,7 @@ uz: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ uz: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ uz: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +387,9 @@ uz: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +398,14 @@ uz: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +420,7 @@ uz: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Default transitions @@ -1536,6 +1540,9 @@ uz: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1551,7 @@ uz: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Display until attachment: @@ -3391,6 +3398,11 @@ uz: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3465,72 @@ uz: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3562,7 @@ uz: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3603,7 @@ uz: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3854,7 @@ uz: label_external_links: External links label_locale: Language and region label_jump_to_a_project: Jump to a project... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Keywords label_language_based: Based on user's language label_last_activity: Last activity @@ -5030,12 +5110,12 @@ uz: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index 253835d638b..d7e7ef038b0 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -114,7 +114,7 @@ vi: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ vi: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ vi: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +381,9 @@ vi: notification_text_default: "

Xin chào,

Một dự án mới đã được tạo: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ vi: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ vi: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: Chuyển tiếp mặc định @@ -1520,6 +1524,9 @@ vi: dependencies: phụ thuộc activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1528,7 +1535,7 @@ vi: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Hiển thị cho đến khi attachment: @@ -2431,7 +2438,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 @@ -3334,6 +3341,11 @@ vi: quick_add: label: Thêm vào… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Mã thông báo của nhà cung cấp được OpenProject phát hành, cho phép các ứng dụng khác truy cập vào nó. Mã thông báo của khách hàng được phát hành bởi các ứng dụng khác, cho phép OpenProject truy cập chúng. no_results: @@ -3396,6 +3408,72 @@ vi: disabled_text: Mã thông báo RSS không được quản trị viên kích hoạt. Vui lòng liên hệ với quản trị viên của bạn để sử dụng tính năng này. storages: unknown_storage: Bộ nhớ không xác định + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Người được chuyển nhượng @@ -3427,6 +3505,7 @@ vi: invalid_filter: Bộ lọc thông báo không hợp lệ label_accessibility: Trợ năng label_account: tài khoản + label_actions: hành động label_active: Đang hoạt động label_activate_user: Người dùng kích họat label_active_in_new_projects: Tích cực trong các dự án mới @@ -3467,6 +3546,7 @@ vi: label_ical_access_key_generation_hint: Tự động được tạo khi đăng ký lịch. label_ical_access_key_latest: mới nhất label_ical_access_key_revoke: Thu hồi + label_integrations: Integrations label_add_column: Thêm cột label_applied_status: Tình trạng áp dụng label_archive_project: Lưu trữ dự án @@ -3717,7 +3797,7 @@ vi: label_external_links: Liên kết ngoài label_locale: Ngôn ngữ và khu vực label_jump_to_a_project: Chuyển đến một dự án... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: Từ khóa label_language_based: Dựa trên ngôn ngữ của người dùng label_last_activity: Hoạt động cuối cùng @@ -4039,7 +4119,7 @@ vi: label_used_by: Được dùng bởi label_used_by_types: Được sử dụng bởi các loại label_used_in_projects: Được sử dụng trong các dự án - label_user: người dùng + label_user: Người dùng label_user_and_permission: Người dùng và quyền label_user_named: Người dùng %{name} label_user_activity_html: "%{value}'s activity" @@ -4976,12 +5056,12 @@ vi: setting_welcome_text: Văn bản chặn chào mừng setting_welcome_title: Tiêu đề khối chào mừng setting_welcome_on_homescreen: Hiển thị khối chào mừng trên màn hình chính - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Chế độ đánh dấu mặc định diff --git a/config/locales/crowdin/zh-CN.seeders.yml b/config/locales/crowdin/zh-CN.seeders.yml index 30e63f026bc..212a0e3692c 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: 欢迎来到您的演示项目 @@ -201,7 +201,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 497d8068cdf..def7cd1a66c 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -89,7 +89,7 @@ zh-CN: token_caption: 要详细了解如何激活企业版,请查阅我们的[文档](docs_url)。 add_token: 上传企业版支持令牌 replace_token: 替换您当前的支持令牌 - order: 订购本地部署版的 Enterprise edition + order: 订购本地部署的 Enterprise edition paste: 粘贴您企业版的支持令牌 required_for_feature: 此功能仅限具激活的企业版支持令牌的订阅者使用。 enterprise_link: 如需了解详细信息,请单击此处。 @@ -114,7 +114,7 @@ zh-CN: import: title: 导入 jira: - title: Jira 导入 + title: Jira Migrator description: 使用此工具从您的 Jira 实例中导入数据。您可以配置多个 Jira 主机,并选择每次导入运行要导入的内容。 errors: cannot_delete_with_imports: 无法删除具有现有导入的 Jira 主机 @@ -125,8 +125,8 @@ zh-CN: title: Jira 配置 new: 新配置 banner: - title: 受限导入 - description: 此导入工具目前处于测试阶段,只能导入基本数据:项目、问题(名称、标题、描述、附件)、用户(名称、电子邮件地址、项目成员资格)、状态和类型。不能导入工作流、自定义字段、问题关系或权限。我们目前仅支持 Jira Server/Data Center 版本 10.x 和 11.x。目前不支持云实例。 + title: Limited import capabilities + description: Jira Migrator 目前处于测试阶段,只能导入基本数据:项目、问题(名称、标题、描述、附件)、用户(名称、电子邮件地址、项目成员资格)、状态和类型。不能导入工作流、自定义字段、问题关系或权限。我们目前仅支持 Jira Server/Data Center 版本 10.x 和 11.x。目前不支持云实例。 form: fields: name: 名称 @@ -162,7 +162,7 @@ zh-CN: run: title: 导入运行 history: 历史记录 - remove_error: 无法移除正在运行的 Jira 导入 + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: 另一个 Jira 导入运行当前正在执行或等待审核。请先完成或撤消当前运行,然后再开始新的导入。 project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +381,9 @@ zh-CN: notification_text_default: "

您好!

已创建一个新项目:projectValue:name

谢谢

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ zh-CN: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ zh-CN: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: 默认转换 @@ -1517,6 +1521,9 @@ zh-CN: dependencies: 依赖项 activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: 项目 import/jira: @@ -1525,7 +1532,7 @@ zh-CN: personal_access_token: 个人访问令牌 import/jira_open_project_reference: jira: Jira - jira_import: Jira 导入 + jira_import: Jira Migrator announcements: show_until: 显示截止日期 attachment: @@ -1603,7 +1610,7 @@ zh-CN: page: 页 row_count: 行数 column_count: 列数 - widgets: 微件 + widgets: 小部件 journal: notes: 备注 cause_type: Cause 类型 @@ -3331,6 +3338,11 @@ zh-CN: quick_add: label: 添加… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: 提供商令牌由 OpenProject 签发,允许其他应用程序访问。客户端令牌由其他应用程序签发,允许 OpenProject 对其进行访问。 no_results: @@ -3393,6 +3405,72 @@ zh-CN: disabled_text: 管理员未启用 RSS 令牌。请联系管理员以使用此功能。 storages: unknown_storage: 未知存储 + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: 指定人 @@ -3424,6 +3502,7 @@ zh-CN: invalid_filter: 无效的通知过滤器 label_accessibility: 辅助功能 label_account: 帐户 + label_actions: 操作 label_active: 激活 label_activate_user: 激活用户 label_active_in_new_projects: 新项目动态 @@ -3464,6 +3543,7 @@ zh-CN: label_ical_access_key_generation_hint: 订阅日历时自动生成的。 label_ical_access_key_latest: 最近 label_ical_access_key_revoke: 撤消 + label_integrations: Integrations label_add_column: 添加列 label_applied_status: 应用的状态 label_archive_project: 归档项目 @@ -3714,7 +3794,7 @@ zh-CN: label_external_links: 外部链接 label_locale: 语言和地区 label_jump_to_a_project: 跳转到一个项目... - label_jira_import: Jira 导入 + label_jira_import: Jira Migrator label_keyword_plural: 关键词 label_language_based: 基于用户的语言 label_last_activity: 最近一次活动 @@ -3954,7 +4034,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} 超时" @@ -4714,7 +4794,7 @@ zh-CN: managed: 在 OpenProject 中创建新的存储库 storage: not_available: 磁盘存储开销不可用于此存储库。 - update_timeout: 在 N 分钟内保留存储库最后所需的磁盘空间信息。由于计算存储库所需的磁盘空间可能增加系统开销,增加该值可以减少性能影响。 + update_timeout: 在 N 分钟内保留存储库最后所需磁盘空间的信息。由于计算存储库所需的磁盘空间可能增加系统开销,增加该值可以减少性能影响。 oauth_application_details_html: 'The client secret value will not be accessible again after you close this window. Please copy these values into the Nextcloud OpenProject Integration settings:' oauth_application_details_link_text: 转到设置页面 setup_documentation_details: 如果您在配置新文件存储方面需要帮助,请查看文档: @@ -4942,7 +5022,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: 计算 完成% 层次结构总数 @@ -4958,12 +5038,12 @@ zh-CN: setting_welcome_text: 欢迎块文本 setting_welcome_title: 欢迎块标题 setting_welcome_on_homescreen: 在主屏幕上显示欢迎信息 - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: 默认突出显示模式 @@ -5428,7 +5508,7 @@ zh-CN: warning_user_limit_reached_admin_html: 'Adding additional users will exceed the current limit. Please [upgrade your plan](upgrade_url) to be able to ensure external users are able to access this instance. ' - warning_user_limit_reached_instructions: '您已达到用户限制(%{current}/%{max} 活跃用户)。请联系 sales@openproject.com 升级您的企业版计划以添加额外用户。 + warning_user_limit_reached_instructions: '您达到了用户限制(%{current}/%{max}活跃用户)。 请联系sales@openproject.com以升级您的Enterprise edition计划并添加其他用户。 ' warning_protocol_mismatch_html: '' diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index c46fb37257e..18d13308b5f 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -114,7 +114,7 @@ zh-TW: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ zh-TW: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -162,7 +162,7 @@ zh-TW: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +381,9 @@ zh-TW: notification_text_default: "

您好,

已建立新專案: projectValue:name

謝謝

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +392,14 @@ zh-TW: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +413,7 @@ zh-TW: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: 預設轉換 @@ -1514,6 +1518,9 @@ zh-TW: dependencies: 依賴套件 activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1522,7 +1529,7 @@ zh-TW: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: 只顯示到 attachment: @@ -3326,6 +3333,11 @@ zh-TW: quick_add: label: 新增… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: 提供者令牌由 OpenProject 發行,允許其他應用程式存取。用戶端識別碼由其他應用程式發行,允許 OpenProject 存取它們。 no_results: @@ -3388,6 +3400,72 @@ zh-TW: disabled_text: RSS 令牌並非由管理員啟用。請聯絡您的管理員以使用此功能。 storages: unknown_storage: 不明的儲存區 + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: 執行者 @@ -3419,6 +3497,7 @@ zh-TW: invalid_filter: 無效的通知過濾器 label_accessibility: 輔助功能 label_account: 帳號 + label_actions: 操作 label_active: 啟用 label_activate_user: 啟動使用者 label_active_in_new_projects: 在新專案中啟用 @@ -3459,6 +3538,7 @@ zh-TW: label_ical_access_key_generation_hint: 訂閱日曆時自動生成的。 label_ical_access_key_latest: 最新 label_ical_access_key_revoke: 撤銷 + label_integrations: Integrations label_add_column: 新增欄位 label_applied_status: 套用的狀態 label_archive_project: 封存專案 @@ -3644,7 +3724,7 @@ zh-TW: label_filter_add: 新增條件 label_filter_by: 篩選條件: label_filter_any_name_attribute: 名稱屬性 - label_filter_plural: 篩選條件 + label_filter_plural: 篩選器 label_filters_toggle: 顯示/隱藏篩選條件 label_float: 浮點數 label_folder: 資料夾 @@ -3658,8 +3738,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} @@ -3671,7 +3751,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_project_identifier: Project identifier @@ -3709,7 +3789,7 @@ zh-TW: label_external_links: 外部連結 label_locale: 語言和地區 label_jump_to_a_project: 前往一個專案... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: 關鍵字 label_language_based: 根據使用者的語言 label_last_activity: 最後的活動 @@ -3723,7 +3803,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: 清單 @@ -4961,12 +5041,12 @@ zh-TW: setting_welcome_text: 歡迎區塊文字 setting_welcome_title: 歡迎區塊標題 setting_welcome_on_homescreen: 在主頁面上顯示歡迎區塊 - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: 預設顯示模式 diff --git a/config/locales/en.yml b/config/locales/en.yml index 8987cd93c14..76f886d1b46 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -121,7 +121,7 @@ en: import: title: "Import" jira: - title: "Jira Import" + title: "Jira Migrator" description: "Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run." errors: cannot_delete_with_imports: "Cannot delete Jira host with existing imports" @@ -132,8 +132,8 @@ en: title: "Jira configuration" new: "New configuration" banner: - title: "Limited import" - description: "This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time." + title: "Limited import capabilities" + description: "This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time." form: fields: name: "Name" @@ -169,8 +169,8 @@ en: run: title: "Import run" history: "History" - remove_error: "A Jira import cannot be removed while it is running" - import_blocked_error: "Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import." + remove_error: "A Jira import run cannot be removed while it is running" + import_blocked_error: "Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import." project_identifier_taken: "You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry." blank: title: "No import runs set up yet" @@ -399,22 +399,26 @@ en:

Thank you

work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: existing_identifiers_notice: > - Existing identifiers for %{project_count} projects don't meet requirements for project-based alphanumerical identifiers. + Existing identifiers for %{project_count} projects don't meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. - Click on 'Autofix and save' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + Click on 'Autofix and save' to update identifiers for all projects in this manner and enable project-based semantic identifiers. box_header: label_project: Project label_previous_identifier: Previous identifier label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -430,7 +434,7 @@ en: checkbox_label: I understand that this will permanently change all work package IDs success_banner: Successfully updated work package identifier format. in_progress: - banner_message: Project identifiers are currently being updated to project-based alphanumerical identifiers. This may take some time. + banner_message: Project identifiers are currently being updated to project-based semantic identifiers. This may take some time. workflows: tabs: default_transitions: "Default transitions" @@ -1619,6 +1623,9 @@ en: activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1627,7 +1634,7 @@ en: personal_access_token: "Personal access token" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira import" + jira_import: "Jira Migrator" announcements: show_until: "Display until" attachment: @@ -3526,6 +3533,11 @@ en: label: "Add…" my_account: + notifications_and_email: + title: "Notification and email" + tabs: + notifications: "Notification settings" + email_reminders: "Email reminders" access_tokens: description: "Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them." no_results: @@ -3588,6 +3600,72 @@ en: disabled_text: "RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature." storages: unknown_storage: "Unknown storage" + email_reminders: + immediate_reminders: + title: "Send me an email reminder" + mentioned: "Notify me when I am mentioned" + personal_reminder: "Notify me for personal reminders" + daily_reminders: + title: "Send me daily email reminders for unread notifications" + caption: "You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC." + enabled: "Enable daily email reminders" + add_time: "Add time" + remove_time: "Remove time" + time_slot_label: "Reminder time (UTC)" + workdays: + title: "Receive email reminders on these days" + submit_button: "Update reminder days" + pause_reminders: + title: "Pause email notifications" + enabled: "Temporarily pause daily email reminders" + date_range: "Pause period" + email_alerts: + title: "Email alerts for other items that are not work packages" + news_added: "News added" + news_commented: "Comment on a news item" + document_added: "Document added" + forum_messages: "Forum message posted" + wiki_page_added: "Wiki page added" + wiki_page_updated: "Wiki page updated" + membership_added: "Membership added" + membership_updated: "Membership updated" + submit_button: "Update alerts" + notifications: + participating: + title: "Participating" + submit_button: "Update preferences" + mentioned: "Mentioned" + watched: "Watching" + assignee: "Assignee" + responsible: "Accountable" + shared: "Shared with me" + date_alerts: + title: "Date alerts" + submit_button: "Update date alerts" + start_date: "Start date" + due_date: "Finish date" + overdue: "Overdue" + times: + same_day: "On the same day" + one_day_before: "1 day before" + three_days_before: "3 days before" + seven_days_before: "7 days before" + one_day_after: "1 day after" + three_days_after: "3 days after" + seven_days_after: "7 days after" + non_participating: + title: "Non-participating" + submit_button: "Update preferences" + work_package_created: "New work packages" + work_package_commented: "All new comments" + work_package_processed: "All status changes" + work_package_prioritized: "All priority changes" + work_package_scheduled: "All date changes" + project_specific_settings: + title: "Project-specific notification settings" + add_button: "Add project-specific notifications" + dialog_title: "Add project-specific notifications" + list_header: "Projects with specific notifications" notifications: reasons: @@ -3621,6 +3699,7 @@ en: label_accessibility: "Accessibility" label_account: "Account" + label_actions: "Actions" label_active: "Active" label_activate_user: "Activate user" label_active_in_new_projects: "Active in new projects" @@ -3661,6 +3740,7 @@ en: label_ical_access_key_generation_hint: "Automatically generated when subscribing to a calendar." label_ical_access_key_latest: "latest" label_ical_access_key_revoke: "Revoke" + label_integrations: "Integrations" label_add_column: "Add column" label_applied_status: "Applied status" label_archive_project: "Archive project" @@ -3911,7 +3991,7 @@ en: label_external_links: "External links" label_locale: "Language and region" label_jump_to_a_project: "Jump to a project..." - label_jira_import: "Jira Import" + label_jira_import: "Jira Migrator" label_keyword_plural: "Keywords" label_language_based: "Based on user's language" label_last_activity: "Last activity" @@ -5201,11 +5281,11 @@ en: setting_welcome_text: "Welcome block text" setting_welcome_title: "Welcome block title" setting_welcome_on_homescreen: "Display welcome block on homescreen" - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: > + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: > Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: > + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: > Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. setting_work_package_list_default_highlighting_mode: "Default highlighting mode" setting_work_package_list_default_highlighted_attributes: "Default inline highlighted attributes" diff --git a/config/locales/js-en.yml b/config/locales/js-en.yml index 6e8a9765fc0..c32e868091d 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -631,49 +631,6 @@ en: settings: change_notification_settings: 'You can modify your notification settings to ensure you never miss an important update.' title: "Notification settings" - notify_me: "Notify me" - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: "Mentioned" - description: "Receive a notification every time someone mentions me anywhere" - assignee: "Assignee" - responsible: "Accountable" - shared: "Shared" - watched: "Watcher" - work_package_commented: "All new comments" - work_package_created: "New work packages" - work_package_processed: "All status changes" - work_package_prioritized: "All priority changes" - work_package_scheduled: "All date changes" - global: - immediately: - title: "Participating" - description: "Notifications for all activities in work packages you are involved in (assignee, accountable or watcher)." - delayed: - title: "Non-participating" - description: "Additional notifications for activities in all projects." - date_alerts: - title: "Date alerts" - description: "Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher)." - overdue: When overdue - project_specific: - title: "Project-specific notification settings" - description: "These project-specific settings override default settings above." - add: "Add setting for project" - already_selected: "This project is already selected" - remove: "Remove project settings" pagination: no_other_page: "You are on the only page." @@ -701,40 +658,6 @@ en: Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. - reminders: - settings: - daily: - add_time: "Add time" - enable: "Enable daily email reminders" - explanation: "You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone}" - no_time_zone: "Until you configure a time zone for your account, the times will be interpreted to be in UTC." - time_label: "Time %{counter}:" - title: "Send me daily email reminders for unread notifications" - workdays: - title: "Receive email reminders on these days" - immediate: - title: "Send me an email reminder" - mentioned: "Immediately when someone @mentions me" - personal_reminder: "Immediately when I receive a personal reminder" - alerts: - title: "Email alerts for other items (that are not work packages)" - explanation: > - Notifications today are limited to work packages. - You can choose to continue receiving email alerts for these events until they are included in notifications: - news_added: "News added" - news_commented: "Comment on a news item" - document_added: "Documents added" - forum_messages: "New forum messages" - wiki_page_added: "Wiki page added" - wiki_page_updated: "Wiki page updated" - membership_added: "Membership added" - membership_updated: "Membership updated" - title: "Email reminders" - pause: - label: "Temporarily pause daily email reminders" - first_day: "First day" - last_day: "Last day" - text_are_you_sure: "Are you sure?" text_are_you_sure_to_cancel: "You have unsaved changes on this page. Are you sure you want to discard them?" breadcrumb: "Breadcrumb" diff --git a/config/routes.rb b/config/routes.rb index ad657256f9a..889ba506625 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -950,6 +950,17 @@ Rails.application.routes.draw do get "/change_status/:change_action" => "users#change_status_info", as: "change_status_info" post :change_status post :resend_invitation + patch :update_reminders + patch :update_workdays + patch :update_email_alerts + patch :update_participating + patch :update_non_participating + patch :update_date_alerts + get "project_notifications/new" => "users#new_project_settings", as: "new_project_settings" + post "project_notifications" => "users#create_project_settings", as: "project_notifications" + get "project_notifications/:project_id/edit" => "users#edit_project_settings", as: "edit_project_settings" + patch "project_notifications/:project_id" => "users#update_project_settings", as: "project_setting" + delete "project_notifications/:project_id" => "users#destroy_project_settings" get :deletion_info end end @@ -1039,13 +1050,23 @@ Rails.application.routes.draw do get "/my/locale", action: "locale" get "/my/interface", action: "interface" get "/my/notifications", action: "notifications" - get "/my/reminders", action: "reminders" get "/my/working_hours", action: "working_hours" get "/my/non_working_times", action: "non_working_times" patch "/my/account", action: "update_account" patch "/my/settings", action: "update_settings" + patch "/my/workdays", action: "update_workdays" + patch "/my/email_alerts", action: "update_email_alerts" + patch "/my/participating", action: "update_participating" + patch "/my/non_participating", action: "update_non_participating" + patch "/my/date_alerts", action: "update_date_alerts" + + get "/my/project_notifications/new", action: "new_project_settings", as: "new_my_project_settings" + post "/my/project_notifications", action: "create_project_settings", as: "my_project_notifications" + get "/my/project_notifications/:project_id/edit", action: "edit_project_settings", as: "edit_my_project_settings" + patch "/my/project_notifications/:project_id", action: "update_project_settings", as: "my_project_setting" + delete "/my/project_notifications/:project_id", action: "destroy_project_settings" end scope controller: "onboarding" do diff --git a/config/static_links.yml b/config/static_links.yml index 03c503d8288..43d936ca817 100644 --- a/config/static_links.yml +++ b/config/static_links.yml @@ -153,6 +153,9 @@ security_badge_documentation: shortcuts: href: https://www.openproject.org/docs/user-guide/keyboard-shortcuts-access-keys/ label: homescreen.links.shortcuts +wiki_docs: + xwiki_setup: + href: https://www.openproject.org/docs/system-admin-guide/integrations/ storage_docs: health_status: href: https://www.openproject.org/docs/system-admin-guide/files/external-file-storages/health-status/ diff --git a/db/migrate/20260319120000_add_case_insensitive_uniqueness_for_project_identifiers.rb b/db/migrate/20260319120000_add_case_insensitive_uniqueness_for_project_identifiers.rb new file mode 100644 index 00000000000..f9a794eabd1 --- /dev/null +++ b/db/migrate/20260319120000_add_case_insensitive_uniqueness_for_project_identifiers.rb @@ -0,0 +1,75 @@ +# 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 AddCaseInsensitiveUniquenessForProjectIdentifiers < ActiveRecord::Migration[8.0] + disable_ddl_transaction! + + def up + deduplicate_case_colliding_identifiers + remove_index :projects, :identifier, unique: true, algorithm: :concurrently, if_exists: true + add_index :projects, "LOWER(identifier)", + unique: true, + name: "index_projects_on_lower_identifier", + algorithm: :concurrently, + if_not_exists: true + end + + # Note: does not undo identifier renames from deduplication. Suffixed identifiers + # (e.g. "FOO_2") remain valid and unique under the restored case-sensitive index. + def down + remove_index :projects, name: "index_projects_on_lower_identifier", algorithm: :concurrently, if_exists: true + add_index :projects, :identifier, unique: true, algorithm: :concurrently + end + + private + + # Resolves any existing case-colliding identifiers (e.g. "Foo" and "foo") so that + # the unique LOWER(identifier) index can be created without violation errors. + # The oldest project (by id) keeps its identifier; duplicates get a "_N" suffix. + # + # The NOT EXISTS guard skips rows where the suffixed identifier would itself collide. + # In practice this is extremely unlikely (requires both case-colliding identifiers + # AND a pre-existing "_N" variant). If it occurs, the subsequent index creation + # will fail, surfacing the issue for manual resolution. + def deduplicate_case_colliding_identifiers + execute <<~SQL.squish + UPDATE projects SET identifier = projects.identifier || '_' || counter.rn + FROM ( + SELECT id, row_number() OVER (PARTITION BY LOWER(identifier) ORDER BY id) AS rn + FROM projects + ) AS counter + WHERE projects.id = counter.id AND counter.rn > 1 + AND NOT EXISTS ( + SELECT 1 FROM projects p2 + WHERE LOWER(p2.identifier) = LOWER(projects.identifier || '_' || counter.rn) + ); + SQL + end +end diff --git a/db/migrate/20260328120000_rename_work_packages_identifier_setting_values.rb b/db/migrate/20260328120000_rename_work_packages_identifier_setting_values.rb new file mode 100644 index 00000000000..907b45b74e6 --- /dev/null +++ b/db/migrate/20260328120000_rename_work_packages_identifier_setting_values.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. +#++ + +require Rails.root.join("db/migrate/migration_utils/setting_renamer") + +class RenameWorkPackagesIdentifierSettingValues < ActiveRecord::Migration[8.0] + SETTING_NAME = "work_packages_identifier" + + def up + Migration::MigrationUtils::SettingRenamer.rename_value(SETTING_NAME, "numeric", "classic") + Migration::MigrationUtils::SettingRenamer.rename_value(SETTING_NAME, "alphanumeric", "semantic") + end + + def down + Migration::MigrationUtils::SettingRenamer.rename_value(SETTING_NAME, "classic", "numeric") + Migration::MigrationUtils::SettingRenamer.rename_value(SETTING_NAME, "semantic", "alphanumeric") + end +end diff --git a/db/migrate/20260330100000_create_work_package_semantic_ids.rb b/db/migrate/20260330100000_create_work_package_semantic_ids.rb new file mode 100644 index 00000000000..0b9111137cd --- /dev/null +++ b/db/migrate/20260330100000_create_work_package_semantic_ids.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. +#++ + +class CreateWorkPackageSemanticIds < ActiveRecord::Migration[8.1] + def up + # Atomic counter for per-project WP sequence allocation + add_column :projects, :wp_sequence_counter, :integer, default: 0, null: false, if_not_exists: true + + # Per-project sequence number for semantic identifiers (e.g. PROJ-42) + add_column :work_packages, :sequence_number, :integer, if_not_exists: true + # Current semantic identifier stored directly on the work package (e.g. "PROJ-42") + add_column :work_packages, :identifier, :string, if_not_exists: true + + create_table :work_package_semantic_aliases, if_not_exists: true do |t| + t.string :identifier, null: false + t.references :work_package, null: false, foreign_key: true + t.timestamps + end + + # Unique identifier across all WPs (past and present) + add_index :work_package_semantic_aliases, :identifier, unique: true, if_not_exists: true + + # Fast lookup and uniqueness of the current semantic identifier (partial: excludes pre-backfill NULLs) + add_index :work_packages, :identifier, + unique: true, + where: "identifier IS NOT NULL", + if_not_exists: true + + # Enforce uniqueness of sequence numbers within a project (partial: excludes pre-backfill NULLs) + add_index :work_packages, %i[project_id sequence_number], + unique: true, + where: "sequence_number IS NOT NULL", + if_not_exists: true + end + + def down + drop_table :work_package_semantic_aliases, if_exists: true + remove_index :work_packages, %i[project_id sequence_number], if_exists: true + remove_column :work_packages, :identifier, if_exists: true + remove_column :work_packages, :sequence_number, if_exists: true + remove_column :projects, :wp_sequence_counter, if_exists: true + end +end diff --git a/db/migrate/migration_utils/setting_renamer.rb b/db/migrate/migration_utils/setting_renamer.rb index 5591f5038eb..3a188a444fc 100644 --- a/db/migrate/migration_utils/setting_renamer.rb +++ b/db/migrate/migration_utils/setting_renamer.rb @@ -34,13 +34,21 @@ module Migration # define all the following methods as class methods class << self def rename(source_name, target_name) - ActiveRecord::Base.connection.execute <<-SQL.squish + ActiveRecord::Base.connection.execute <<~SQL.squish UPDATE #{settings_table} SET name = #{quote_value(target_name)} WHERE name = #{quote_value(source_name)} SQL end + def rename_value(setting_name, from, to) + ActiveRecord::Base.connection.execute <<~SQL.squish + UPDATE #{settings_table} + SET value = #{quote_value(to)} + WHERE name = #{quote_value(setting_name)} AND value = #{quote_value(from)} + SQL + end + private def settings_table diff --git a/db/migrate/tables/base.rb b/db/migrate/tables/base.rb index d5622b4663a..afeec9fa31c 100644 --- a/db/migrate/tables/base.rb +++ b/db/migrate/tables/base.rb @@ -56,6 +56,6 @@ class Tables::Base end def self.table(_migration) - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/db/migrate/tables/projects.rb b/db/migrate/tables/projects.rb index 2b95ea20d0b..9d7d2d57559 100644 --- a/db/migrate/tables/projects.rb +++ b/db/migrate/tables/projects.rb @@ -49,7 +49,7 @@ class Tables::Projects < Tables::Base t.index :lft, name: "index_projects_on_lft" t.index :rgt, name: "index_projects_on_rgt" - t.index :identifier, unique: true + t.index "LOWER(identifier)", unique: true, name: "index_projects_on_lower_identifier" t.index %i[lft rgt] end end diff --git a/docker/dev/backend/Dockerfile b/docker/dev/backend/Dockerfile index e2a04a60889..8e6a5df6bc6 100644 --- a/docker/dev/backend/Dockerfile +++ b/docker/dev/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:4.0.1-trixie AS develop +FROM ruby:4.0.2-trixie AS develop LABEL org.opencontainers.image.authors="operations@openproject.com" ARG DEV_UID=1000 diff --git a/docker/prod/Dockerfile b/docker/prod/Dockerfile index 7a6930e75de..59a4743206a 100755 --- a/docker/prod/Dockerfile +++ b/docker/prod/Dockerfile @@ -1,4 +1,4 @@ -ARG RUBY_VERSION="4.0.1" +ARG RUBY_VERSION="4.0.2" ARG DEBIAN_BASE="trixie" # Add SBOM scan context for intermediate steps ARG BUILDKIT_SBOM_SCAN_CONTEXT=true diff --git a/docs/development/development-environment/docker/README.md b/docs/development/development-environment/docker/README.md index 5c7ccab5ba8..406091f884b 100644 --- a/docs/development/development-environment/docker/README.md +++ b/docs/development/development-environment/docker/README.md @@ -63,10 +63,10 @@ You can run tests inside the `backend-test` container. You can run specific test ```shell # Run all tests (not recommended) -docker compose run --rm backend-test bundle exec rspec +bin/compose rspec # Run the specified test -docker compose run --rm backend-test bundle exec rspec spec/features/work_package_show_spec.rb +bin/compose rspec spec/features/work_package_show_spec.rb ``` *** diff --git a/docs/development/development-environment/linux/README.md b/docs/development/development-environment/linux/README.md index 223026715eb..8b405235892 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 `4.0.1`, which we also require. +At the time of this writing, the latest stable version is `4.0.2`, 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. `4.0.1` and install that version. +Read the first line e.g. `4.0.2` 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. `4.0.1` and install that version. +Read the first line e.g. `4.0.2` and install that version. ```shell # Install the required version as read from the .ruby-version file -rbenv install 4.0.1 +rbenv install 4.0.2 ``` 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 4.0.1 +rbenv global 4.0.2 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 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25] +ruby 4.0.2 (2026-03-17 revision d3da9fec82) +PRISM [arm64-darwin25] bundler --version -4.0.3 +4.0.9 node --version v22.21.0 diff --git a/docs/development/development-environment/macos/README.md b/docs/development/development-environment/macos/README.md index ba6694a3e7e..b38a65a01ba 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 "4.0.1" +the time of writing, this version is "4.0.2" #### 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. `4.0.1` and install that version. +Read the first line e.g. `4.0.2` and install that version. ```shell # Install the required version as read from the .ruby-version file -rbenv install 4.0.1 +rbenv install 4.0.2 ``` 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 4.0.1 +rbenv global 4.0.2 ``` You also need to install [bundler](https://github.com/bundler/bundler/), the ruby gem bundler. @@ -134,7 +134,7 @@ You should now have an active ruby and node installation. Verify that it works w ```shell $ ruby --version -ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25] +ruby 4.0.2 (2026-03-17 revision d3da9fec82) +PRISM [arm64-darwin25] $ bundler --version 4.0.3 @@ -196,7 +196,7 @@ automatically loaded to the application's environment. > [!TIP] > Instead of using the `gssencmode` flag in `config/database.yml`, you can add `export PGGSSENCMODE="disable"` to your -> Shell profile (`~/.zprofile` by default). This will prevent Ruby crashes for *all* of your projects. +> Shell profile (`~/.zprofile` by default). This will prevent Ruby crashes for _all_ of your projects. Some users report Ruby crashes despite having set this flag to disable. If this is the case for you as well, try adding `export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=yes` to your Shell profile. @@ -328,7 +328,7 @@ sudo ln -sfn $(brew --prefix)/opt/openjdk/libexec/openjdk.jdk /Library/Java/Java #### Subversion To test the integration with Subversion repositories, we rely on the `svnadmin` command to be available. If subversion -is not installed, the tests *will be skipped*. To run the tests, install subversion with +is not installed, the tests _will be skipped_. To run the tests, install subversion with ```shell brew install subversion diff --git a/docs/installation-and-operations/installation/manual/README.md b/docs/installation-and-operations/installation/manual/README.md index 2092086ffbf..ccc9758556d 100644 --- a/docs/installation-and-operations/installation/manual/README.md +++ b/docs/installation-and-operations/installation/manual/README.md @@ -10,16 +10,16 @@ sidebar_navigation: false Please be aware that: -* This guide **requires** that you have a clean **Ubuntu 18.04** **x64** installation -with administrative rights (i.e. you must be able to `sudo`). We have tested -the installation guide on an Ubuntu Server image, but it should work on any -derivative. You may need to alter some of the commands to match your -derivative. +- This guide **requires** that you have a clean **Ubuntu 18.04** **x64** installation + with administrative rights (i.e. you must be able to `sudo`). We have tested + the installation guide on an Ubuntu Server image, but it should work on any + derivative. You may need to alter some of the commands to match your + derivative. -* OpenProject will be installed with a **PostgreSQL** database. +- OpenProject will be installed with a **PostgreSQL** database. -* OpenProject will be served in a production environment with the **Apache** server -(this guide should work similarly with other servers, like nginx and others) +- OpenProject will be served in a production environment with the **Apache** server + (this guide should work similarly with other servers, like nginx and others) > **NOTE:** We have highlighted commands to execute like this @@ -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. `4.0.1` and install that version. +Read the first line e.g. `4.0.2` and install that version. ```shell -[openproject@host] rbenv install 4.0.1 +[openproject@host] rbenv install 4.0.2 [openproject@host] rbenv rehash -[openproject@host] rbenv global 4.0.1 +[openproject@host] rbenv global 4.0.2 ``` To check our Ruby installation we run `ruby --version`. It should output something very similar to: ```text -ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin25] +ruby 4.0.2 (2026-03-17 revision d3da9fec82) +PRISM [arm64-darwin25] ``` ## Installation of Node @@ -216,7 +216,6 @@ rails_cache_store: :memcache ``` > **NOTE:** You should validate your `yml` files, for example with [yamlchecker.com](https://yamlchecker.com/). Both, the `database.yml` and `configuration.yml` file are sensitive to whitespace. It is pretty easy to write invalid `yml` files without seeing the error. Validating those files prevents you from such errors. -> To configure the environment variables such as the number of web server threads `OPENPROJECT_WEB_WORKERS`, copy the `.env.example` to `.env` and add the environment variables you want to configure. The variables will be automatically loaded to the application's environment. @@ -428,7 +427,7 @@ If you need to restart the server (for example after a configuration change), do ## Frequently asked questions (FAQ) -* **I followed the installation guide faithfully and OpenProject is running. Now, how do I log in?** +- **I followed the installation guide faithfully and OpenProject is running. Now, how do I log in?** The `db:seed` command listed above creates a default admin-user. The username is `admin` and the default password is `admin`. You are forced to change the admin password on the first login. If you cannot login as the admin user, make sure that you have executed the `db:seed` command. @@ -437,18 +436,18 @@ If you need to restart the server (for example after a configuration change), do [openproject@all] RAILS_ENV="production" ./bin/rake db:seed ``` -* **When accessing OpenProject, I get an error page. How do I find out what went wrong?** +- **When accessing OpenProject, I get an error page. How do I find out what went wrong?** Things can go wrong on different levels. You can find the apache error logs here: `/var/log/apache2/error.log` The OpenProject log can be found here: `/home/openproject/openproject/log/production.log` -* **I cannot solve an error, not even with the log files. How do I get help?** +- **I cannot solve an error, not even with the log files. How do I get help?** You can find help in [the OpenProject forums](https://community.openproject.org/projects/openproject/boards). Please tell us, if possible, what you have done (e.g. which guide you have used to install OpenProject), how to reproduce the error, and provide the appropriate error logs. It often helps to have a look at the already answered questions, or to search the Internet for the error. Most likely someone else has already solved the same problem. -* **I get errors, since I have installed an OpenProject plug-in** +- **I get errors, since I have installed an OpenProject plug-in** With each new OpenProject core version, the plug-ins might need to be updated. Please make sure that the plug-in versions of all you plug-ins works with the OpenProject version you use. Many plug-ins follow the OpenProject version with their version number (So, if you have installed OpenProject version 4.1.0, the plug-in should also have the version 4.1.0). diff --git a/docs/user-guide/keyboard-shortcuts-access-keys/README.md b/docs/user-guide/keyboard-shortcuts-access-keys/README.md index f068312b85a..ce8aa0d63df 100644 --- a/docs/user-guide/keyboard-shortcuts-access-keys/README.md +++ b/docs/user-guide/keyboard-shortcuts-access-keys/README.md @@ -73,7 +73,7 @@ OpenProject (since version 3.0) offers useful keyboard shortcuts to enhance you ------ -### OS X +### macOS - Firefox: Ctrl + Opt + <access key number> - Google Chrome: Ctrl + Opt + <access key number> diff --git a/frontend/.mcp.json b/frontend/.mcp.json new file mode 100644 index 00000000000..d8562ee2149 --- /dev/null +++ b/frontend/.mcp.json @@ -0,0 +1,8 @@ +{ + "mcpServers": { + "angular-cli": { + "command": "npx", + "args": ["-y", "@angular/cli@21.1.5", "mcp"] + } + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index f011d32b7a0..89247893c59 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -126,8 +126,7 @@ "turbo_power": "^0.7.1", "typedjson": "^1.5.1", "urijs": "^1.19.11", - "uuid": "^13.0.0", - "zone.js": "~0.15.1" + "uuid": "^13.0.0" }, "devDependencies": { "@angular-builders/custom-esbuild": "^21.0.3", @@ -5269,9 +5268,9 @@ } }, "node_modules/@hono/node-server": { - "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==", + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", "engines": { "node": ">=18.14.1" }, @@ -15765,9 +15764,9 @@ } }, "node_modules/hono": { - "version": "4.12.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", - "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", + "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", "engines": { "node": ">=16.9.0" } @@ -17967,9 +17966,9 @@ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "node_modules/lodash-es": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", - "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==" }, "node_modules/lodash.clonedeep": { "version": "4.5.0", @@ -20702,9 +20701,9 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "dev": true }, "node_modules/picocolors": { @@ -22187,9 +22186,9 @@ } }, "node_modules/router/node_modules/path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", + "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==", "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -25956,11 +25955,6 @@ "zod": "^3.25.0 || ^4.0.0" } }, - "node_modules/zone.js": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", - "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==" - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", @@ -29107,9 +29101,9 @@ } }, "@hono/node-server": { - "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==" + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==" }, "@hotwired/stimulus": { "version": "3.2.2", @@ -36029,9 +36023,9 @@ } }, "hono": { - "version": "4.12.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", - "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==" + "version": "4.12.12", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", + "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==" }, "hosted-git-info": { "version": "9.0.2", @@ -37560,9 +37554,9 @@ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==" }, "lodash-es": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz", - "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==" + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz", + "integrity": "sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==" }, "lodash.clonedeep": { "version": "4.5.0", @@ -39390,9 +39384,9 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" }, "path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "dev": true }, "picocolors": { @@ -40390,9 +40384,9 @@ } }, "path-to-regexp": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", - "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==" + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.0.tgz", + "integrity": "sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==" } } }, @@ -42844,11 +42838,6 @@ "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", "dev": true }, - "zone.js": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.15.1.tgz", - "integrity": "sha512-XE96n56IQpJM7NAoXswY3XRLcWFW83xe0BiAOeMD7K5k5xecOeul3Qcpx6GqEeeHNkW5DWL5zOyTbEfB4eti8w==" - }, "zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", diff --git a/frontend/package.json b/frontend/package.json index 041f430a450..c06f5e829b7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -181,8 +181,7 @@ "turbo_power": "^0.7.1", "typedjson": "^1.5.1", "urijs": "^1.19.11", - "uuid": "^13.0.0", - "zone.js": "~0.15.1" + "uuid": "^13.0.0" }, "optionalDependencies": { "fsevents": "*" diff --git a/frontend/src/app/app.module.ts b/frontend/src/app/app.module.ts index d3edb6674eb..a002c9df85e 100644 --- a/frontend/src/app/app.module.ts +++ b/frontend/src/app/app.module.ts @@ -39,6 +39,7 @@ import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpSpotModule } from 'core-app/spot/spot.module'; import { OpDragScrollDirective } from 'core-app/shared/directives/op-drag-scroll/op-drag-scroll.directive'; import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; +import { OpenprojectBoardsModule } from 'core-app/features/boards/openproject-boards.module'; import { OpenprojectAttachmentsModule } from 'core-app/shared/components/attachments/openproject-attachments.module'; import { OpenprojectEditorModule } from 'core-app/shared/components/editor/openproject-editor.module'; import { OpenprojectGridsModule } from 'core-app/shared/components/grids/openproject-grids.module'; @@ -135,12 +136,6 @@ import { } from 'core-app/shared/components/autocompleter/draggable-autocomplete/draggable-autocomplete.component'; import { OpExclusionInfoComponent } from 'core-app/shared/components/fields/display/info/op-exclusion-info.component'; import { OpenProjectJobStatusModule } from 'core-app/features/job-status/openproject-job-status.module'; -import { - NotificationsSettingsPageComponent, -} from 'core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component'; -import { - ReminderSettingsPageComponent, -} from 'core-app/features/user-preferences/reminder-settings/page/reminder-settings-page.component'; import { OpenProjectMyAccountModule } from 'core-app/features/user-preferences/user-preferences.module'; import { OpAttachmentsComponent } from 'core-app/shared/components/attachments/attachments.component'; import { @@ -149,6 +144,9 @@ import { import { WorkPackageSplitViewEntryComponent, } from 'core-app/features/work-packages/routing/wp-split-view/wp-split-view-entry.component'; +import { + BoardEntryComponent, +} from 'core-app/features/boards/board/board-partitioned-page/board-entry.component'; import { StorageLoginButtonComponent, } from 'core-app/shared/components/storages/storage-login-button/storage-login-button.component'; @@ -295,6 +293,9 @@ export function runBootstrap(appRef:ApplicationRef) { OpenprojectWorkPackagesModule, OpenprojectWorkPackageRoutesModule, + // Boards + OpenprojectBoardsModule, + // Work packages in graph representation OpenprojectWorkPackageGraphsModule, // Calendar module @@ -389,11 +390,9 @@ export class OpenProjectModule implements DoBootstrap { registerCustomElement('opce-storage-login-button', StorageLoginButtonComponent, { injector }); registerCustomElement('opce-custom-modal-overlay', OpCustomModalOverlayComponent, { injector }); - // TODO: These elements are now registered custom elements, but are actually single-use components. They should be removed when we move these pages to Rails. - registerCustomElement('opce-notification-settings', NotificationsSettingsPageComponent, { injector }); - registerCustomElement('opce-reminder-settings', ReminderSettingsPageComponent, { injector }); registerCustomElement('opce-notification-center', InAppNotificationCenterComponent, { injector }); registerCustomElement('opce-wp-split-view', WorkPackageSplitViewEntryComponent, { injector }); + registerCustomElement('opce-board-view', BoardEntryComponent, { injector }); registerCustomElement('opce-wp-full-view', WorkPackageFullViewEntryComponent, { injector }); registerCustomElement('opce-wp-full-create', WorkPackageFullCreateEntryComponent, { injector }); registerCustomElement('opce-wp-full-copy', WorkPackageFullCopyEntryComponent, { injector }); diff --git a/frontend/src/app/core/apiv3/api-v3.service.spec.ts b/frontend/src/app/core/apiv3/api-v3.service.spec.ts index a2391ce6cb3..034cfada593 100644 --- a/frontend/src/app/core/apiv3/api-v3.service.spec.ts +++ b/frontend/src/app/core/apiv3/api-v3.service.spec.ts @@ -28,7 +28,6 @@ import { TestBed, - waitForAsync, } from '@angular/core/testing'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -37,19 +36,16 @@ import { States } from 'core-app/core/states/states.service'; describe('APIv3Service', () => { let service:ApiV3Service; - beforeEach(waitForAsync(() => { - void TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ providers: [ States, PathHelperService, ApiV3Service, ], - }) - .compileComponents() - .then(() => { - service = TestBed.inject(ApiV3Service); - }); - })); + }).compileComponents(); + service = TestBed.inject(ApiV3Service); + }); function encodeParams(object:any) { return new URLSearchParams(object).toString(); diff --git a/frontend/src/app/core/config/configuration.service.ts b/frontend/src/app/core/config/configuration.service.ts index 56cc854c60b..ee186227657 100644 --- a/frontend/src/app/core/config/configuration.service.ts +++ b/frontend/src/app/core/config/configuration.service.ts @@ -147,6 +147,10 @@ export class ConfigurationService { return moment.localeData(I18n.locale).firstDayOfWeek(); } + public get wikisAvailable():boolean { + return this.systemPreference('wikisAvailable'); + } + public get hostName():string { return this.systemPreference('hostName'); } diff --git a/frontend/src/app/core/main-menu/submenu.service.ts b/frontend/src/app/core/main-menu/submenu.service.ts index c0374eab1d9..dff74195f52 100644 --- a/frontend/src/app/core/main-menu/submenu.service.ts +++ b/frontend/src/app/core/main-menu/submenu.service.ts @@ -6,9 +6,9 @@ import { StateService } from '@uirouter/core'; export class SubmenuService { constructor(protected $state:StateService) {} - reloadSubmenu(selectedQueryId:string|null):void { + reloadSubmenu(selectedQueryId:string|null, sidemenuId?:string):void { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment - const menuIdentifier:string|undefined = this.$state.current.data.sideMenuOptions?.sidemenuId; + const menuIdentifier:string|undefined = sidemenuId ?? this.$state.current.data?.sideMenuOptions?.sidemenuId; if (menuIdentifier) { const menu = document.getElementById(menuIdentifier) as FrameElement; @@ -18,7 +18,7 @@ export class SubmenuService { if (currentSrc && menu) { const frameUrl = new URL(currentSrc, window.location.origin); - const defaultQuery = sideMenuOptions.defaultQuery; + const defaultQuery = sideMenuOptions?.defaultQuery; if (selectedQueryId) { // If there is a default query passed in the route definition, it means that id passed as argument and not as parameter, diff --git a/frontend/src/app/core/path-helper/path-helper.service.ts b/frontend/src/app/core/path-helper/path-helper.service.ts index 5c30f35a952..dcc9a2a571a 100644 --- a/frontend/src/app/core/path-helper/path-helper.service.ts +++ b/frontend/src/app/core/path-helper/path-helper.service.ts @@ -247,6 +247,10 @@ export class PathHelperService { return `${this.boardsPath(projectIdentifier)}/new`; } + public boardDetailsPath(projectIdentifier:string|null, boardId:string|number, workPackageId:string|number) { + return `${this.boardsPath(projectIdentifier)}/${boardId}/details/${workPackageId}`; + } + public projectDashboardsPath(projectIdentifier:string) { return `${this.projectPath(projectIdentifier)}/dashboards`; } diff --git a/frontend/src/app/core/routing/base/application-base.component.ts b/frontend/src/app/core/routing/base/application-base.component.ts index 97ae414e336..fc4d7879a0a 100644 --- a/frontend/src/app/core/routing/base/application-base.component.ts +++ b/frontend/src/app/core/routing/base/application-base.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; export const appBaseSelector = 'openproject-base'; @@ -38,6 +38,10 @@ export const appBaseSelector = 'openproject-base'; `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class ApplicationBaseComponent { } diff --git a/frontend/src/app/core/routing/openproject.routes.ts b/frontend/src/app/core/routing/openproject.routes.ts index 25ff234b031..fb01324276b 100644 --- a/frontend/src/app/core/routing/openproject.routes.ts +++ b/frontend/src/app/core/routing/openproject.routes.ts @@ -29,7 +29,7 @@ import { StateDeclaration, StateService, Transition, TransitionService, UIRouter } from '@uirouter/core'; import { IToast, ToastService } from 'core-app/shared/components/toaster/toast.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { Injector } from '@angular/core'; +import { ApplicationRef, Injector } from '@angular/core'; import { FirstRouteService } from 'core-app/core/routing/first-route-service'; import { Ng2StateDeclaration, StatesModule } from '@uirouter/angular'; import { appBaseSelector, ApplicationBaseComponent } from 'core-app/core/routing/base/application-base.component'; @@ -68,12 +68,6 @@ export const OPENPROJECT_ROUTES:Ng2StateDeclaration[] = [ '!$default': { component: ApplicationBaseComponent }, }, }, - { - name: 'boards.**', - parent: 'optional_project', - url: '/boards', - loadChildren: () => import('../../features/boards/openproject-boards.module').then((m) => m.OpenprojectBoardsModule), - }, { name: 'bim.**', parent: 'optional_project', @@ -287,4 +281,12 @@ export function initializeUiRouterListeners(injector:Injector) { return true; }); + + // In zoneless mode, UIRouter transitions complete in microtasks but + // Angular's change detection doesn't run automatically afterwards. + // Force a CD cycle after every successful transition so that the new + // view is rendered and Stimulus controllers properly disconnect/connect. + $transitions.onSuccess({}, () => { + injector.get(ApplicationRef).tick(); + }); } diff --git a/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts b/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts index 09f24baa979..2de91e3dff7 100644 --- a/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts +++ b/frontend/src/app/features/bim/bcf/api/bcf-api.service.spec.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { TestBed, waitForAsync } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { BcfApiService } from 'core-app/features/bim/bcf/api/bcf-api.service'; import { BcfResourceCollectionPath, BcfResourcePath } from 'core-app/features/bim/bcf/api/bcf-path-resources'; import { BcfTopicPaths } from 'core-app/features/bim/bcf/api/topics/bcf-topic.paths'; @@ -34,18 +34,14 @@ import { BcfTopicPaths } from 'core-app/features/bim/bcf/api/topics/bcf-topic.pa describe('BcfApiService', () => { let service:BcfApiService; - beforeEach(waitForAsync(() => { - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ providers: [ BcfApiService, ], - }) - .compileComponents() - .then(() => { - service = TestBed.inject(BcfApiService); - }); - })); + }).compileComponents(); + service = TestBed.inject(BcfApiService); + }); describe('building the path', () => { it('can build projects', () => { diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts index 4e33473461a..1bbb9f85189 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-export-button.component.ts @@ -27,7 +27,7 @@ //++ import { - Component, Injector, OnDestroy, OnInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnDestroy, OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -53,6 +53,10 @@ import { JobStatusModalService } from 'core-app/features/job-status/job-status-m `, selector: 'bcf-export-button', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { public text = { @@ -73,7 +77,8 @@ export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnI readonly httpClient:HttpClient, readonly injector:Injector, readonly toastService:ToastService, - readonly state:StateService) { + readonly state:StateService, + readonly cdRef:ChangeDetectorRef) { super(); } @@ -92,6 +97,7 @@ export class BcfExportButtonComponent extends UntilDestroyedMixin implements OnI projectIdentifier!, JSON.stringify(filters), ); + this.cdRef.markForCheck(); }); } diff --git a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts index 9081664edeb..b320357f6ba 100644 --- a/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts +++ b/frontend/src/app/features/bim/ifc_models/toolbar/import-export-bcf/bcf-import-button.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path-helper.service'; @@ -42,6 +42,10 @@ import { BcfPathHelperService } from 'core-app/features/bim/bcf/helper/bcf-path- `, selector: 'bcf-import-button', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BcfImportButtonComponent { public text = { diff --git a/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts b/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts index 585510250fc..5c477b04f12 100644 --- a/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts +++ b/frontend/src/app/features/boards/board/add-list-modal/add-list-modal.component.ts @@ -27,14 +27,13 @@ //++ import { - ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, ViewChild, } from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Board } from 'core-app/features/boards/board/board'; -import { StateService } from '@uirouter/core'; import { BoardService } from 'core-app/features/boards/board/board.service'; import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; import { BoardActionService } from 'core-app/features/boards/board/board-actions/board-action.service'; @@ -42,6 +41,7 @@ import { HalResourceNotificationService } from 'core-app/features/hal/services/h import { tap } from 'rxjs/operators'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { firstValueFrom, Observable, @@ -52,6 +52,10 @@ import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @Component({ templateUrl: './add-list-modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class AddListModalComponent extends OpModalComponent implements OnInit { @ViewChild(OpAutocompleterComponent, { static: true }) public ngSelectComponent:OpAutocompleterComponent; @@ -113,11 +117,11 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { readonly cdRef:ChangeDetectorRef, readonly boardActions:BoardActionsRegistryService, readonly halNotification:HalResourceNotificationService, - readonly state:StateService, readonly boardService:BoardService, readonly I18n:I18nService, readonly apiV3Service:ApiV3Service, - readonly currentProject:CurrentProjectService) { + readonly currentProject:CurrentProjectService, + readonly pathHelper:PathHelperService) { super(locals, cdRef, elementRef); } @@ -141,9 +145,12 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { .then((board) => { this.inFlight = false; this.closeMe(); - void this.state.go('boards.partitioned.show', { board_id: board.id, isNew: true }); + void Turbo.visit(`${this.pathHelper.boardsPath(this.currentProject.identifier)}/${board.id}`); }) - .catch(() => (this.inFlight = false)); + .catch(() => { + this.inFlight = false; + this.cdRef.detectChanges(); + }); } onNewActionCreated() { @@ -190,6 +197,7 @@ export class AddListModalComponent extends OpModalComponent implements OnInit { .warningTextWhenNoOptionsAvailable(hasMember) .then((text) => { this.warningText = text; + this.cdRef.detectChanges(); }) .catch(() => {}); this.showWarning = this.ngSelectComponent.ngSelectInstance.searchTerm !== undefined && (values.length === 0); diff --git a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts index b68dc2f87d1..7321bcf61b4 100644 --- a/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/assignee/assignee-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UserResource } from 'core-app/features/hal/resources/user-resource'; @@ -35,6 +35,10 @@ import { UserResource } from 'core-app/features/hal/resources/user-resource'; styleUrls: ['./assignee-board-header.sass'], host: { class: 'title-container -small' }, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class AssigneeBoardHeaderComponent { @Input('resource') public user:UserResource; diff --git a/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts index ae559c84d08..cfdab8c28fc 100644 --- a/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/status/status-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { StatusResource } from 'core-app/features/hal/resources/status-resource'; @@ -34,6 +34,10 @@ import { StatusResource } from 'core-app/features/hal/resources/status-resource' styleUrls: ['./status-board-header.sass'], host: { class: 'title-container -small' }, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class StatusBoardHeaderComponent { @Input('resource') public status:StatusResource; diff --git a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts index 50b87614533..27fe9a979fc 100644 --- a/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/subproject/subproject-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -36,6 +36,10 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; styleUrls: ['./subproject-board-header.sass'], host: { class: 'title-container -small' }, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class SubprojectBoardHeaderComponent { @Input() public resource:HalResource; diff --git a/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts index a1b793bad2c..04040a17623 100644 --- a/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/subtasks/subtasks-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @@ -37,6 +37,10 @@ import idFromLink from 'core-app/features/hal/helpers/id-from-link'; styleUrls: ['./subtasks-board-header.sass'], host: { class: 'title-container -small' }, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class SubtasksBoardHeaderComponent implements OnInit { @Input() public resource:WorkPackageResource; diff --git a/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts b/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts index 90992070755..c090f3bb58d 100644 --- a/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts +++ b/frontend/src/app/features/boards/board/board-actions/version/version-action.service.ts @@ -4,7 +4,6 @@ import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { VersionResource } from 'core-app/features/hal/resources/version-resource'; import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op-context-menu.types'; import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/link-handling'; -import { StateService } from '@uirouter/core'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { VersionBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/version/version-board-header.component'; import { FormResource } from 'core-app/features/hal/resources/form-resource'; @@ -22,8 +21,6 @@ import { map } from 'rxjs/operators'; @Injectable() export class BoardVersionActionService extends CachedBoardActionService { - @InjectField() state:StateService; - @InjectField() halNotification:HalResourceNotificationService; filterName = 'version'; @@ -119,8 +116,8 @@ export class BoardVersionActionService extends CachedBoardActionService { .id(version) .patch({ status: newStatus }) .subscribe( - (version) => { - this.state.go('.', {}, { reload: true }); + () => { + Turbo.visit(window.location.href, { action: 'replace' }); }, (error) => this.halNotification.handleRawError(error), ); diff --git a/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts b/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts index fb29c183ddf..d57d88bc732 100644 --- a/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts +++ b/frontend/src/app/features/boards/board/board-actions/version/version-board-header.component.ts @@ -25,7 +25,7 @@ // // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { VersionResource } from 'core-app/features/hal/resources/version-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -35,6 +35,10 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service styleUrls: ['./version-board-header.sass'], host: { class: 'title-container -small' }, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class VersionBoardHeaderComponent { @Input('resource') public version:VersionResource; diff --git a/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts b/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts index 6c960942039..0a3ec34be8f 100644 --- a/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts +++ b/frontend/src/app/features/boards/board/board-filter/board-filter.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, Input } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { Board } from 'core-app/features/boards/board/board'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; @@ -7,7 +7,6 @@ import { HalResourceService } from 'core-app/features/hal/services/hal-resource. import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/query-filter-instance-resource'; import { UrlParamsHelperService } from 'core-app/features/work-packages/components/wp-query/url-params-helper'; -import { StateService } from '@uirouter/core'; import { debounceTime, skip, take } from 'rxjs/operators'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { Observable } from 'rxjs'; @@ -18,6 +17,10 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; selector: 'board-filter', templateUrl: './board-filter.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BoardFilterComponent extends UntilDestroyedMixin implements AfterViewInit { /** Current active */ @@ -32,8 +35,7 @@ export class BoardFilterComponent extends UntilDestroyedMixin implements AfterVi private readonly wpStatesInitialization:WorkPackageStatesInitializationService, private readonly wpTableFilters:WorkPackageViewFiltersService, private readonly urlParamsHelper:UrlParamsHelperService, - private readonly boardFilters:BoardFiltersService, - private readonly $state:StateService) { + private readonly boardFilters:BoardFiltersService) { super(); } @@ -71,9 +73,15 @@ export class BoardFilterComponent extends UntilDestroyedMixin implements AfterVi const filterHash = this.urlParamsHelper.buildV3GetFilters(filters); const query_props = JSON.stringify(filterHash); - this.boardFilters.filters.putValue(filterHash); + const url = new URL(window.location.href); + if (query_props) { + url.searchParams.set('query_props', query_props); + } else { + url.searchParams.delete('query_props'); + } + window.history.pushState({}, '', url); - this.$state.go('.', { query_props }, { custom: { notify: false } }); + this.boardFilters.filters.putValue(filterHash); }); } diff --git a/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts b/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts index b870e4edf75..6c1ee33907b 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list-menu.component.ts @@ -27,7 +27,7 @@ //++ import { - Component, EventEmitter, Input, Output, + ChangeDetectionStrategy, Component, EventEmitter, Input, Output, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; @@ -43,6 +43,10 @@ import { BoardActionService } from 'core-app/features/boards/board/board-actions selector: 'board-list-menu', templateUrl: './board-list-menu.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BoardListMenuComponent { @Input() board:Board; diff --git a/frontend/src/app/features/boards/board/board-list/board-list.component.ts b/frontend/src/app/features/boards/board/board-list/board-list.component.ts index 52406a60191..baa4f648b35 100644 --- a/frontend/src/app/features/boards/board/board-list/board-list.component.ts +++ b/frontend/src/app/features/boards/board/board-list/board-list.component.ts @@ -185,9 +185,9 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni this.resource.isNewWidget = false; // Set initial selection if split view open - if (this.state.includes(`${this.state.current.data.baseRoute}.details`)) { - const wpId = this.state.params.workPackageId; - this.wpViewSelectionService.initializeSelection([wpId]); + const detailsMatch = window.location.pathname.match(/\/details\/(\d+)/); + if (detailsMatch) { + this.wpViewSelectionService.initializeSelection([detailsMatch[1]]); } // If this query space changes its focused or selected @@ -495,15 +495,20 @@ export class BoardListComponent extends AbstractWidgetComponent implements OnIni } openStateLink(event:{ workPackageId:string; requestedState:string }) { - const params = { workPackageId: event.workPackageId }; - if (event.requestedState === 'split') { - this.keepTab.goCurrentDetailsState(params); + this.goToSplitView(event.workPackageId); } else { - this.keepTab.goCurrentShowState(params.workPackageId); + this.keepTab.goCurrentShowState(event.workPackageId); } } + private goToSplitView(workPackageId:string):void { + const base = this.pathHelper.boardDetailsPath(this.currentProject.identifier, this.board.id!, workPackageId); + const search = window.location.search; + const link = search ? `${base}${search}` : base; + Turbo.visit(link, { frame: 'content-bodyRight', action: 'advance' }); + } + private schema(workPackage:WorkPackageResource) { return this.schemaCache.of(workPackage); } diff --git a/frontend/src/app/features/boards/boards-root/boards-root.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts similarity index 51% rename from frontend/src/app/features/boards/boards-root/boards-root.component.ts rename to frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts index 65eb30d4516..0503484d44b 100644 --- a/frontend/src/app/features/boards/boards-root/boards-root.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-entry.component.ts @@ -1,20 +1,50 @@ -import { Component, Injector } from '@angular/core'; +//-- 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. +//++ + +import { ChangeDetectionStrategy, Component, ElementRef, Injector, Input } from '@angular/core'; +import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; +import { + WorkPackageIsolatedQuerySpaceDirective, +} from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; import { BoardConfigurationService } from 'core-app/features/boards/board/configuration-modal/board-configuration.service'; import { BoardActionsRegistryService } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; import { BoardStatusActionService } from 'core-app/features/boards/board/board-actions/status/status-action.service'; import { BoardVersionActionService } from 'core-app/features/boards/board/board-actions/version/version-action.service'; -import { QueryUpdatedService } from 'core-app/features/boards/board/query-updated/query-updated.service'; import { BoardAssigneeActionService } from 'core-app/features/boards/board/board-actions/assignee/assignee-action.service'; import { BoardSubprojectActionService } from 'core-app/features/boards/board/board-actions/subproject/subproject-action.service'; import { BoardSubtasksActionService } from 'core-app/features/boards/board/board-actions/subtasks/board-subtasks-action.service'; -import { - WorkPackageIsolatedQuerySpaceDirective, -} from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; +import { QueryUpdatedService } from 'core-app/features/boards/board/query-updated/query-updated.service'; @Component({ - selector: 'boards-entry', + selector: 'board-entry', hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], - template: '', + template: ``, + changeDetection: ChangeDetectionStrategy.OnPush, providers: [ BoardConfigurationService, BoardStatusActionService, @@ -26,11 +56,18 @@ import { ], standalone: false, }) -export class BoardsRootComponent { - constructor(readonly injector:Injector) { - // Register action services - const registry = injector.get(BoardActionsRegistryService); +export class BoardEntryComponent { + @Input() boardId:string; + constructor( + readonly elementRef:ElementRef, + readonly injector:Injector, + ) { + populateInputsFromDataset(this); + + document.body.classList.add('router--boards-full-view'); + + const registry = injector.get(BoardActionsRegistryService); registry.add('status', injector.get(BoardStatusActionService)); registry.add('assignee', injector.get(BoardAssigneeActionService)); registry.add('version', injector.get(BoardVersionActionService)); diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts index cecc9501023..fba32f8b51d 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-list-container.component.ts @@ -2,6 +2,7 @@ import { ChangeDetectionStrategy, Component, ElementRef, + Input, Injector, OnInit, QueryList, @@ -36,11 +37,11 @@ import { BoardActionsRegistryService, } from 'core-app/features/boards/board/board-actions/board-actions-registry.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; -import { - WorkPackageStatesInitializationService, -} from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; +import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @Component({ + selector: 'board-list-container', templateUrl: './board-list-container.component.html', styleUrls: ['./board-list-container.component.sass'], providers: [ @@ -50,6 +51,7 @@ import { standalone: false, }) export class BoardListContainerComponent extends UntilDestroyedMixin implements OnInit { + @Input() boardId:string; text = { delete: this.I18n.t('js.button_delete'), areYouSure: this.I18n.t('js.text_are_you_sure'), @@ -90,7 +92,7 @@ export class BoardListContainerComponent extends UntilDestroyedMixin implements private currentQueryUpdatedMonitoring:Subscription; constructor( -readonly I18n:I18nService, + readonly I18n:I18nService, readonly state:StateService, readonly toastService:ToastService, readonly halNotification:HalResourceNotificationService, @@ -102,16 +104,17 @@ readonly I18n:I18nService, readonly apiV3Service:ApiV3Service, readonly Boards:BoardService, readonly boardListCrossSelectionService:BoardListCrossSelectionService, - readonly wpStatesInitialization:WorkPackageStatesInitializationService, readonly Drag:DragAndDropService, readonly apiv3Service:ApiV3Service, readonly QueryUpdated:QueryUpdatedService, + readonly pathHelper:PathHelperService, + readonly currentProject:CurrentProjectService, ) { super(); } ngOnInit():void { - const id:string = this.state.params.board_id.toString(); + const id:string = this.boardId || this.state.params.board_id?.toString(); this.board$ = this .apiV3Service .boards @@ -128,10 +131,12 @@ readonly I18n:I18nService, .pipe( this.untilDestroyed(), filter((state) => state.focusedWorkPackage !== null), - filter(() => this.state.includes(`${this.state.current.data.baseRoute}.details`)), + filter(() => window.location.pathname.includes('/details/')), ).subscribe((selection) => { - // Update split screen - this.state.go(`${this.state.current.data.baseRoute}.details`, { workPackageId: selection.focusedWorkPackage }); + // Update split screen + const base = this.pathHelper.boardDetailsPath(this.currentProject.identifier, id, selection.focusedWorkPackage!); + const search = window.location.search; + Turbo.visit(search ? `${base}${search}` : base, { frame: 'content-bodyRight', action: 'advance' }); }); } diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.html b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.html new file mode 100644 index 00000000000..a0ee9d566e0 --- /dev/null +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.html @@ -0,0 +1,48 @@ +
+ +
+
+

+ +

+ @if (showToolbar) { +
    + @for (definition of toolbarButtonComponents; track definition) { + @if (!definition.show || definition.show()) { +
  • + +
  • + } + } +
+ } +
+
+ +
+ @if (filterContainerDefinition) { + + } +
+ +
+ + +
+
diff --git a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts index 85c123a2d7d..9d33220ca41 100644 --- a/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts +++ b/frontend/src/app/features/boards/board/board-partitioned-page/board-partitioned-page.component.ts @@ -2,7 +2,9 @@ import { ChangeDetectionStrategy, ChangeDetectorRef, Component, - Injector, OnInit, OnDestroy, + Injector, + Input, + OnInit, } from '@angular/core'; import { DynamicComponentDefinition, @@ -11,7 +13,6 @@ import { } from 'core-app/features/work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component'; import { StateService, - TransitionService, } from '@uirouter/core'; import { BoardFilterComponent } from 'core-app/features/boards/board/board-filter/board-filter.component'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; @@ -24,17 +25,18 @@ import { BoardsMenuButtonComponent } from 'core-app/features/boards/board/toolba import { catchError, finalize, + skip, take, } from 'rxjs/operators'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; -import { Ng2StateDeclaration } from '@uirouter/angular'; +import { Board } from 'core-app/features/boards/board/board'; import { BoardFiltersService } from 'core-app/features/boards/board/board-filter/board-filters.service'; import { CardViewHandlerRegistry } from 'core-app/features/work-packages/components/wp-card-view/event-handler/card-view-handler-registry'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { OpTitleService } from 'core-app/core/html/op-title.service'; -import { EMPTY } from 'rxjs'; +import { EMPTY, ReplaySubject } from 'rxjs'; import { SubmenuService } from 'core-app/core/main-menu/submenu.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @@ -44,7 +46,8 @@ export function boardCardViewHandlerFactory(injector:Injector) { } @Component({ - templateUrl: '../../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.html', + selector: 'board-partitioned-page', + templateUrl: './board-partitioned-page.component.html', styleUrls: [ '../../../work-packages/routing/partitioned-query-space-page/partitioned-query-space-page.component.sass', './board-partitioned-page.component.sass', @@ -56,7 +59,8 @@ export function boardCardViewHandlerFactory(injector:Injector) { ], standalone: false, }) -export class BoardPartitionedPageComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { +export class BoardPartitionedPageComponent extends UntilDestroyedMixin implements OnInit { + @Input() boardId:string; text = { button_more: this.I18n.t('js.button_more'), delete: this.I18n.t('js.button_delete'), @@ -71,15 +75,8 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement unnamed_list: this.I18n.t('js.boards.label_unnamed_list'), }; - /** Board observable */ - board$ = this - .apiV3Service - .boards - .id(this.state.params.board_id.toString()) - .observe(); - - /** Whether this is a new board just created */ - isNew = !!this.state.params.isNew; + /** Board subject */ + board$ = new ReplaySubject(1); /** Whether the board is editable */ editable:boolean; @@ -95,10 +92,6 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement /** Do we currently have query props ? */ showToolbarSaveButton:boolean; - /** Listener callbacks */ - // eslint-disable-next-line @typescript-eslint/ban-types - removeTransitionSubscription:Function; - /** Show a toolbar */ showToolbar = true; @@ -139,7 +132,6 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement constructor( readonly I18n:I18nService, readonly cdRef:ChangeDetectorRef, - readonly $transitions:TransitionService, readonly state:StateService, readonly toastService:ToastService, readonly halNotification:HalResourceNotificationService, @@ -159,29 +151,28 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement // Ensure board is being loaded this.Boards.loadAllBoards(); - this.removeTransitionSubscription = this.$transitions.onSuccess({}, (transition):any => { - const toState = transition.to(); - const params = transition.params('to'); + const boardId = this.boardId || this.state.params.board_id?.toString(); + this.apiV3Service.boards.id(boardId).observe() + .pipe(this.untilDestroyed()) + .subscribe((board) => this.board$.next(board)); - this.showToolbarSaveButton = !!params.query_props; - this.setPartition(toState); - - this - .board$ - .pipe(take(1)) - .subscribe((board) => { - this.titleService.setFirstPart(board.name); - }); - - this.cdRef.detectChanges(); - }); + // React to filter changes (board-filter updates boardFilters after pushing URL) + this.boardFilters.filters.values$() + .pipe( + this.untilDestroyed(), + skip(1), // skip the initial empty default value + ) + .subscribe(() => { + this.showToolbarSaveButton = !!new URLSearchParams(window.location.search).get('query_props'); + this.cdRef.detectChanges(); + }); this.board$ .pipe( this.untilDestroyed(), ) .subscribe((board) => { - const queryProps = this.state.params.query_props; + const queryProps = new URLSearchParams(window.location.search).get('query_props'); this.editable = board.editable; this.selectedTitle = board.name; this.titleService.setFirstPart(board.name); @@ -191,11 +182,6 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement }); } - ngOnDestroy():void { - super.ngOnDestroy(); - this.removeTransitionSubscription(); - } - breadcrumbItems() { return [ { href: this.pathHelperService.projectPath(this.currentProject.identifier!), text: (this.currentProject.name) }, @@ -213,8 +199,10 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement board.name = newName; board.filters = this.boardFilters.current; - const params = { isNew: false, query_props: null }; - this.state.go('.', params, { custom: { notify: false } }); + const url = new URL(window.location.href); + url.searchParams.delete('query_props'); + window.history.pushState({}, '', url); + this.showToolbarSaveButton = false; this.toolbarDisabled = true; this.Boards @@ -226,8 +214,8 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement }), finalize(() => { this.toolbarDisabled = false; - this.reloadSidemenu(); this.cdRef.detectChanges(); + this.reloadSidemenu(); }), ).subscribe(() => { this.toastService.addSuccess(this.text.updateSuccessful); @@ -245,17 +233,7 @@ export class BoardPartitionedPageComponent extends UntilDestroyedMixin implement return this.editable; } - /** - * We need to set the current partition to the grid to ensure - * either side gets expanded to full width if we're not in '-split' mode. - * - * @param state The current or entering state - */ - protected setPartition(state:Ng2StateDeclaration) { - this.currentPartition = (state.data?.partition) ? state.data.partition : '-split'; - } - private reloadSidemenu():void { - this.submenuService.reloadSubmenu(null); + this.submenuService.reloadSubmenu(null, 'boards_sidemenu'); } } diff --git a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts index 0809e4c5911..a24aee77456 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/board-configuration.modal.ts @@ -1,5 +1,6 @@ import { ApplicationRef, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, @@ -27,6 +28,10 @@ import { Board } from 'core-app/features/boards/board/board'; @Component({ templateUrl: './board-configuration.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BoardConfigurationModalComponent extends OpModalComponent implements OnInit, OnDestroy { public text = { diff --git a/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts b/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts index f61676faf74..489277262d9 100644 --- a/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts +++ b/frontend/src/app/features/boards/board/configuration-modal/tabs/highlighting-tab.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject, Injector, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; @@ -9,6 +9,10 @@ import { CardHighlightingMode } from 'core-app/features/work-packages/components @Component({ templateUrl: './highlighting-tab.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BoardHighlightingTabComponent implements TabComponent, OnInit { // Highlighting mode diff --git a/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts b/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts index 1d39c152c01..540b2176523 100644 --- a/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts +++ b/frontend/src/app/features/boards/board/inline-add/board-inline-add-autocompleter.component.ts @@ -28,6 +28,7 @@ import { AfterViewInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, @@ -59,6 +60,10 @@ import { HalResourceService } from 'core-app/features/hal/services/hal-resource. encapsulation: ViewEncapsulation.None, styleUrls: ['./board-inline-add-autocompleter.sass'], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BoardInlineAddAutocompleterComponent implements AfterViewInit { readonly text = { diff --git a/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts b/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts index 2e516b2402f..758fd6ad1d7 100644 --- a/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts +++ b/frontend/src/app/features/boards/board/toolbar-menu/boards-menu-button.component.ts @@ -1,4 +1,4 @@ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Board } from 'core-app/features/boards/board/board'; import { Observable } from 'rxjs'; @@ -13,6 +13,10 @@ import { Observable } from 'rxjs'; `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BoardsMenuButtonComponent { @Input() board$:Observable; diff --git a/frontend/src/app/features/boards/openproject-boards.module.ts b/frontend/src/app/features/boards/openproject-boards.module.ts index 65847007c8d..586034fcb15 100644 --- a/frontend/src/app/features/boards/openproject-boards.module.ts +++ b/frontend/src/app/features/boards/openproject-boards.module.ts @@ -30,9 +30,7 @@ import { NgModule } from '@angular/core'; import { OpSharedModule } from 'core-app/shared/shared.module'; import { OpenprojectWorkPackagesModule } from 'core-app/features/work-packages/openproject-work-packages.module'; import { OpenprojectModalModule } from 'core-app/shared/components/modal/modal.module'; -import { UIRouterModule } from '@uirouter/angular'; import { BoardListComponent } from 'core-app/features/boards/board/board-list/board-list.component'; -import { BoardsRootComponent } from 'core-app/features/boards/boards-root/boards-root.component'; import { BoardInlineAddAutocompleterComponent } from 'core-app/features/boards/board/inline-add/board-inline-add-autocompleter.component'; import { BoardsToolbarMenuDirective } from 'core-app/features/boards/board/toolbar-menu/boards-toolbar-menu.directive'; import { BoardConfigurationModalComponent } from 'core-app/features/boards/board/configuration-modal/board-configuration.modal'; @@ -43,9 +41,9 @@ import { BoardFilterComponent } from 'core-app/features/boards/board/board-filte import { BoardListMenuComponent } from 'core-app/features/boards/board/board-list/board-list-menu.component'; import { VersionBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/version/version-board-header.component'; import { DynamicModule } from 'ng-dynamic-component'; -import { BOARDS_ROUTES, uiRouterBoardsConfiguration } from 'core-app/features/boards/openproject-boards.routes'; import { BoardPartitionedPageComponent } from 'core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component'; import { BoardListContainerComponent } from 'core-app/features/boards/board/board-partitioned-page/board-list-container.component'; +import { BoardEntryComponent } from 'core-app/features/boards/board/board-partitioned-page/board-entry.component'; import { BoardsMenuButtonComponent } from 'core-app/features/boards/board/toolbar-menu/boards-menu-button.component'; import { AssigneeBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/assignee/assignee-board-header.component'; import { SubprojectBoardHeaderComponent } from 'core-app/features/boards/board/board-actions/subproject/subproject-board-header.component'; @@ -64,18 +62,12 @@ import { OpenprojectEnterpriseModule } from 'core-app/features/enterprise/openpr // Dynamic Module for actions DynamicModule, - - // Routes for /boards - UIRouterModule.forChild({ - states: BOARDS_ROUTES, - config: uiRouterBoardsConfiguration, - }), ], declarations: [ BoardPartitionedPageComponent, BoardListContainerComponent, + BoardEntryComponent, BoardListComponent, - BoardsRootComponent, BoardInlineAddAutocompleterComponent, BoardHighlightingTabComponent, BoardConfigurationModalComponent, diff --git a/frontend/src/app/features/boards/openproject-boards.routes.ts b/frontend/src/app/features/boards/openproject-boards.routes.ts deleted file mode 100644 index 85f44ea998e..00000000000 --- a/frontend/src/app/features/boards/openproject-boards.routes.ts +++ /dev/null @@ -1,105 +0,0 @@ -//-- 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. -//++ - -import { Ng2StateDeclaration, UIRouter } from '@uirouter/angular'; -import { BoardsRootComponent } from 'core-app/features/boards/boards-root/boards-root.component'; -import { BoardPartitionedPageComponent } from 'core-app/features/boards/board/board-partitioned-page/board-partitioned-page.component'; -import { BoardListContainerComponent } from 'core-app/features/boards/board/board-partitioned-page/board-list-container.component'; -import { makeSplitViewRoutes } from 'core-app/features/work-packages/routing/split-view-routes.template'; -import { WorkPackageSplitViewComponent } from 'core-app/features/work-packages/routing/wp-split-view/wp-split-view.component'; - -export const menuItemClass = 'boards-menu-item'; - -export const sidemenuId = 'boards_sidemenu'; -export const sideMenuOptions = { - sidemenuId, - hardReloadOnBaseRoute: true, -}; - -export const BOARDS_ROUTES:Ng2StateDeclaration[] = [ - { - name: 'boards', - parent: 'optional_project', - // The trailing slash is important - // cf., https://community.openproject.com/wp/29754 - url: '/boards/?query_props', - data: { - bodyClasses: 'router--boards-view-base', - menuItem: menuItemClass, - sideMenuOptions, - }, - params: { - // Use custom encoder/decoder that ensures validity of URL string - query_props: { type: 'opQueryString', dynamic: true }, - }, - component: BoardsRootComponent, - }, - { - name: 'boards.partitioned', - url: '{board_id}', - params: { - board_id: { type: 'int' }, - isNew: { type: 'bool', inherit: false, dynamic: true }, - }, - data: { - parent: 'boards', - bodyClasses: 'router--boards-full-view', - menuItem: menuItemClass, - sideMenuOptions, - }, - reloadOnSearch: false, - component: BoardPartitionedPageComponent, - redirectTo: 'boards.partitioned.show', - }, - { - name: 'boards.partitioned.show', - url: '', - data: { - baseRoute: 'boards.partitioned.show', - sideMenuOptions, - }, - views: { - 'content-left': { component: BoardListContainerComponent }, - }, - }, - ...makeSplitViewRoutes( - 'boards.partitioned.show', - menuItemClass, - WorkPackageSplitViewComponent, - ), -]; - -export function uiRouterBoardsConfiguration(uiRouter:UIRouter) { - // Ensure boards/ are being redirected correctly - // cf., https://community.openproject.com/wp/29754 - uiRouter.urlService.rules - .when( - new RegExp('^/projects/(.*)/boards$'), - (match) => `/projects/${match[1]}/boards/`, - ); -} diff --git a/frontend/src/app/features/calendar/op-calendar.service.spec.ts b/frontend/src/app/features/calendar/op-calendar.service.spec.ts index 96a766503c6..23ccec1742c 100644 --- a/frontend/src/app/features/calendar/op-calendar.service.spec.ts +++ b/frontend/src/app/features/calendar/op-calendar.service.spec.ts @@ -26,18 +26,17 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { waitForAsync } from '@angular/core/testing'; import { OpCalendarService } from 'core-app/features/calendar/op-calendar.service'; describe('OP calendar service', () => { let service:OpCalendarService; - beforeEach(waitForAsync(() => { + beforeEach(() => { // This is not a valid constructor call, but since we only want to test a helper method that does not // depend on injected services, we can pass null values here. // @ts-expect-error ignore invalid constructor call since we don't need a completely valid instance service = new OpCalendarService(null, null, null); - })); + }); describe('stripYearFromDateFormat', () => { it('from dotted syntax', () => { diff --git a/frontend/src/app/features/hal/resources/hal-resource.spec.ts b/frontend/src/app/features/hal/resources/hal-resource.spec.ts index a17b18ef3b8..3001b7e3561 100644 --- a/frontend/src/app/features/hal/resources/hal-resource.spec.ts +++ b/frontend/src/app/features/hal/resources/hal-resource.spec.ts @@ -27,7 +27,7 @@ //++ import { Injector } from '@angular/core'; -import { TestBed, waitForAsync } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { States } from 'core-app/core/states/states.service'; @@ -49,9 +49,8 @@ describe('HalResource', () => { class OtherResource extends HalResource { } - beforeEach(waitForAsync(() => { - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ imports: [OpenprojectHalModule], providers: [ HalResourceService, @@ -60,13 +59,10 @@ describe('HalResource', () => { provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ] -}) - .compileComponents() - .then(() => { - halResourceService = TestBed.inject(HalResourceService); - injector = TestBed.inject(Injector); - }); - })); +}).compileComponents(); + halResourceService = TestBed.inject(HalResourceService); + injector = TestBed.inject(Injector); + }); it('should be instantiable using a default object', () => { const resource = halResourceService.createHalResource({}, true); diff --git a/frontend/src/app/features/hal/resources/work-package-resource.spec.ts b/frontend/src/app/features/hal/resources/work-package-resource.spec.ts index e836b5e418f..6f755c36765 100644 --- a/frontend/src/app/features/hal/resources/work-package-resource.spec.ts +++ b/frontend/src/app/features/hal/resources/work-package-resource.spec.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { TestBed, waitForAsync } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; import { Injector } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; @@ -68,9 +68,8 @@ describe('WorkPackage', () => { loadWeekdays: () => of(true), }; - beforeEach(waitForAsync(() => { - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ imports: [OpenprojectHalModule], providers: [ HalResourceService, @@ -91,16 +90,13 @@ describe('WorkPackage', () => { provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ] -}) - .compileComponents() - .then(() => { - halResourceService = TestBed.inject(HalResourceService); - injector = TestBed.inject(Injector); - halResourceNotification = injector.get(HalResourceNotificationService); +}).compileComponents(); + halResourceService = TestBed.inject(HalResourceService); + injector = TestBed.inject(Injector); + halResourceNotification = injector.get(HalResourceNotificationService); - halResourceService.registerResource('WorkPackage', { cls: WorkPackageResource }); - }); - })); + halResourceService.registerResource('WorkPackage', { cls: WorkPackageResource }); + }); describe('when creating an empty work package', () => { beforeEach(createWorkPackage); diff --git a/frontend/src/app/features/my-page/my-page.component.ts b/frontend/src/app/features/my-page/my-page.component.ts index dc1308418b5..3908eef37a3 100644 --- a/frontend/src/app/features/my-page/my-page.component.ts +++ b/frontend/src/app/features/my-page/my-page.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewEncapsulation } from '@angular/core'; import { GRID_PROVIDERS } from 'core-app/shared/components/grids/grid/grid.component'; import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/grid-page.component'; @@ -8,6 +8,10 @@ import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/gr providers: GRID_PROVIDERS, encapsulation: ViewEncapsulation.None, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class MyPageComponent extends GridPageComponent { protected gridScopePath():string { diff --git a/frontend/src/app/features/overview/dashboard.component.ts b/frontend/src/app/features/overview/dashboard.component.ts index cb7fe493abc..2664c1fbeb5 100644 --- a/frontend/src/app/features/overview/dashboard.component.ts +++ b/frontend/src/app/features/overview/dashboard.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, ElementRef, inject, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, inject, Input, OnInit } from '@angular/core'; import { GridPageComponent } from 'core-app/shared/components/grids/grid/page/grid-page.component'; import { GRID_PROVIDERS } from 'core-app/shared/components/grids/grid/grid.component'; import { OpSharedModule } from 'core-app/shared/shared.module'; @@ -34,7 +34,7 @@ import { OpenprojectGridsModule } from 'core-app/shared/components/grids/openpro import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; // The OnPush change detection strategy makes the page very slow, especially removing widgets. See #66753 -// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection +// TODO: Investigate whether this can be migrated to OnPush after zoneless testing. @Component({ templateUrl: '../../shared/components/grids/grid/page/grid-page.component.html', styleUrls: ['../../shared/components/grids/grid/page/grid-page.component.sass'], @@ -44,6 +44,8 @@ import { populateInputsFromDataset } from 'core-app/shared/components/dataset-in ], providers: GRID_PROVIDERS, standalone: true, + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class DashboardComponent extends GridPageComponent implements OnInit { @Input() projectIdentifier:string; diff --git a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html deleted file mode 100644 index fc05e39b0d6..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.html +++ /dev/null @@ -1,25 +0,0 @@ -
- @if (!active) { - - } - - @if (active) { - - } -
diff --git a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.sass b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.sass deleted file mode 100644 index 4b50ef469c8..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.sass +++ /dev/null @@ -1,2 +0,0 @@ -.inline-create-container - margin-top: 1rem diff --git a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts deleted file mode 100644 index bdc2d1ed4ab..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { ChangeDetectionStrategy, Component, EventEmitter, Input, Output } from '@angular/core'; -import { UntypedFormArray } from '@angular/forms'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { - IProjectAutocompleteItem, -} from 'core-app/shared/components/autocompleter/project-autocompleter/project-autocomplete-item'; -import { IAPIFilter } from 'core-app/shared/components/autocompleter/op-autocompleter/typings'; -import { HalSourceLink } from 'core-app/features/hal/interfaces'; - -export interface NotificationSettingProjectOption { - name:string; - href:string; -} - -@Component({ - selector: 'op-notification-setting-inline-create', - templateUrl: './notification-setting-inline-create.component.html', - styleUrls: ['./notification-setting-inline-create.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class NotificationSettingInlineCreateComponent { - @Input() userId:string; - - @Input() settings:UntypedFormArray; - - @Output() selected = new EventEmitter(); - - /** Active inline-create mode */ - active = false; - - text = { - add_setting: this.I18n.t('js.notifications.settings.project_specific.add'), - please_select: this.I18n.t('js.placeholders.selection'), - already_selected: this.I18n.t('js.notifications.settings.project_specific.already_selected'), - }; - - public get APIFilters():IAPIFilter[] { - return [{ name: 'visible', operator: '=', values: [this.userId] }]; - } - - constructor( - private I18n:I18nService, - ) { } - - selectProject($event:NotificationSettingProjectOption):void { - this.selected.emit({ title: $event.name, href: $event.href }); - this.active = false; - } - - public mapProjectsFn(projects:IProjectAutocompleteItem[]):IProjectAutocompleteItem[] { - return projects.map((project) => ({ - ...project, - disabled: !!this.settings.controls.find( - (projectSetting) => (projectSetting.get('project')!.value as NotificationSettingProjectOption).href === project.href, - ), - disabledReason: this.text.already_selected, - })); - } -} diff --git a/frontend/src/app/features/user-preferences/notifications-settings/overdue-reminder-available-times.ts b/frontend/src/app/features/user-preferences/notifications-settings/overdue-reminder-available-times.ts deleted file mode 100644 index b05974538aa..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/overdue-reminder-available-times.ts +++ /dev/null @@ -1,37 +0,0 @@ -export function reminderAvailableTimeframes():{ value:string, title:string }[] { - return [ - { - value: 'PT0S', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.normal.PT0S'), - }, - { - value: 'P1D', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.normal.P1D'), - }, - { - value: 'P3D', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.normal.P3D'), - }, - { - value: 'P7D', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.normal.P7D'), - }, - ]; -} - -export function overDueReminderTimes():{ value:string, title:string }[] { - return [ - { - value: 'P1D', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.overdue.P1D'), - }, - { - value: 'P3D', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.overdue.P3D'), - }, - { - value: 'P7D', - title: window.I18n.t('js.notifications.settings.reminders.timeframes.overdue.P7D'), - }, - ]; -} diff --git a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.html b/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.html deleted file mode 100644 index 8756f19b027..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.html +++ /dev/null @@ -1,260 +0,0 @@ -
-
-

{{ text.notifyImmediately.title }}

-

{{ text.notifyImmediately.description }}

-
- - - - - - - - - - - - - - - - - - - - - -
-

{{ text.dateAlerts.title }}

-

{{ text.dateAlerts.description }}

-
- - @if (eeAvailable) { -
-
- - - @if (dateAlertsStatuses.startDate) { - - } -
-
- - - @if (dateAlertsStatuses.dueDate) { - - } -
-
- - - @if (dateAlertsStatuses.overdue) { - - } -
-
- } - - - -
-

{{ text.alsoNotifyFor.title }}

-

{{ text.alsoNotifyFor.description }}

-
- - - - - - - - - - - - - - - - - - - - - - -
-

{{ text.projectSpecific.title }}

-

{{text.projectSpecific.description}}

-
- - @if (userId) { - - } - -
- -
- diff --git a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.sass b/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.sass deleted file mode 100644 index 68b9aeea920..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.sass +++ /dev/null @@ -1,30 +0,0 @@ -@import "helpers" - -.op-reminder-settings-date-alerts - &--enable - display: flex - align-items: center - margin-top: $spot-spacing-2 - margin-bottom: $spot-spacing-0_75 - - &--row - line-height: 45px - display: flex - align-items: center - - &--active - margin-right: 0.5rem - - &--label - flex: 0 0 180px - margin-bottom: 0 - @include text-shortener - - &--time - height: $spot-spacing-2 - width: auto - min-width: $spot-spacing-8 - padding: $spot-spacing-0_25 $spot-spacing-1_5 $spot-spacing-0_25 $spot-spacing-0_75 - -.op-date-alert-ee-banner - width: 100% diff --git a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts deleted file mode 100644 index dbcc908d486..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/page/notifications-settings-page.component.ts +++ /dev/null @@ -1,280 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { take } from 'rxjs/internal/operators/take'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; -import { INotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model'; -import { BannersService } from 'core-app/core/enterprise/banners.service'; -import { enterpriseDocsUrl } from 'core-app/core/setup/globals/constants.const'; -import { overDueReminderTimes, reminderAvailableTimeframes } from '../overdue-reminder-available-times'; -import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; - -export const myNotificationsPageComponentSelector = 'op-notifications-page'; - -interface IToastSettingsValue { - assignee:boolean; - responsible:boolean; - shared:boolean; - workPackageCreated:boolean; - workPackageProcessed:boolean; - workPackageScheduled:boolean; - workPackagePrioritized:boolean; - workPackageCommented:boolean; -} - -interface IProjectNotificationSettingsValue extends IToastSettingsValue { - project:{ - title:string; - href:string; - }; - startDate:string|null; - dueDate:string|null; - overdue:string|null; -} - -interface IFullNotificationSettingsValue extends IToastSettingsValue { - projectSettings:IProjectNotificationSettingsValue[]; - startDate:{ active:boolean, time:string }; - dueDate:{ active:boolean, time:string }; - overdue:{ active:boolean, time:string }; -} - -@Component({ - templateUrl: './notifications-settings-page.component.html', - styleUrls: ['./notifications-settings-page.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class NotificationsSettingsPageComponent extends UntilDestroyedMixin implements OnInit { - @Input() userId:string; - - public availableTimes = reminderAvailableTimeframes(); - - public availableTimesOverdue = overDueReminderTimes(); - - public eeAvailable = false; - - public form = new UntypedFormGroup({ - assignee: new UntypedFormControl(false), - responsible: new UntypedFormControl(false), - shared: new UntypedFormControl(false), - workPackageCreated: new UntypedFormControl(false), - workPackageProcessed: new UntypedFormControl(false), - workPackageScheduled: new UntypedFormControl(false), - workPackagePrioritized: new UntypedFormControl(false), - workPackageCommented: new UntypedFormControl(false), - startDate: new UntypedFormGroup({ - active: new UntypedFormControl(false), - time: new UntypedFormControl(this.availableTimes[1]), - }), - dueDate: new UntypedFormGroup({ - active: new UntypedFormControl(false), - time: new UntypedFormControl(this.availableTimes[1]), - }), - overdue: new UntypedFormGroup({ - active: new UntypedFormControl(false), - time: new UntypedFormControl(this.availableTimesOverdue[0]), - }), - projectSettings: new UntypedFormArray([]), - }); - - text = { - notifyImmediately: { - title: this.I18n.t('js.notifications.settings.global.immediately.title'), - description: this.I18n.t('js.notifications.settings.global.immediately.description'), - }, - alsoNotifyFor: { - title: this.I18n.t('js.notifications.settings.global.delayed.title'), - description: this.I18n.t('js.notifications.settings.global.delayed.description'), - }, - dateAlerts: { - title: this.I18n.t('js.notifications.settings.global.date_alerts.title'), - description: this.I18n.t('js.notifications.settings.global.date_alerts.description'), - }, - mentioned: { - title: this.I18n.t('js.notifications.settings.reasons.mentioned.title'), - description: this.I18n.t('js.notifications.settings.reasons.mentioned.description'), - }, - watched: this.I18n.t('js.notifications.settings.reasons.watched'), - work_package_commented: this.I18n.t('js.notifications.settings.reasons.work_package_commented'), - work_package_created: this.I18n.t('js.notifications.settings.reasons.work_package_created'), - work_package_processed: this.I18n.t('js.notifications.settings.reasons.work_package_processed'), - work_package_prioritized: this.I18n.t('js.notifications.settings.reasons.work_package_prioritized'), - work_package_scheduled: this.I18n.t('js.notifications.settings.reasons.work_package_scheduled'), - save: this.I18n.t('js.button_save'), - projectSpecific: { - title: this.I18n.t('js.notifications.settings.project_specific.title'), - description: this.I18n.t('js.notifications.settings.project_specific.description'), - }, - assignee: this.I18n.t('js.notifications.settings.reasons.assignee'), - responsible: this.I18n.t('js.notifications.settings.reasons.responsible'), - shared: this.I18n.t('js.notifications.settings.reasons.shared'), - startDate: this.I18n.t('js.work_packages.properties.startDate'), - dueDate: this.I18n.t('js.work_packages.properties.dueDate'), - overdue: this.I18n.t('js.notifications.settings.global.overdue'), - }; - - dateAlertsStatuses = { - startDate: false, - dueDate: false, - overdue: false, - }; - - constructor( - readonly elementRef:ElementRef, - readonly changeDetectorRef:ChangeDetectorRef, - readonly I18n:I18nService, - readonly storeService:UserPreferencesService, - readonly currentUserService:CurrentUserService, - readonly bannersService:BannersService, - readonly configurationService:ConfigurationService, - ) { - super(); - populateInputsFromDataset(this); - } - - ngOnInit():void { - this.form.disable(); - this.eeAvailable = this.bannersService.allowsTo('date_alerts'); - - this - .currentUserService - .user$ - .pipe(take(1)) - .subscribe((user) => { - this.userId = this.userId || user.id!; - this.storeService.get(this.userId); - }); - - this.form.get('startDate.active')?.valueChanges.subscribe((newValue) => { - this.dateAlertsStatuses.startDate = !!newValue; - }); - - this.form.get('dueDate.active')?.valueChanges.subscribe((newValue) => { - this.dateAlertsStatuses.dueDate = !!newValue; - }); - - this.form.get('overdue.active')?.valueChanges.subscribe((newValue) => { - this.dateAlertsStatuses.overdue = !!newValue; - }); - - this.storeService.query.notificationsForGlobal$ - .pipe(this.untilDestroyed()) - .subscribe((settings) => { - if (!settings) { - return; - } - - this.form.get('assignee')?.setValue(settings.assignee); - this.form.get('responsible')?.setValue(settings.responsible); - this.form.get('shared')?.setValue(settings.shared); - this.form.get('workPackageCreated')?.setValue(settings.workPackageCreated); - this.form.get('workPackageProcessed')?.setValue(settings.workPackageProcessed); - this.form.get('workPackageScheduled')?.setValue(settings.workPackageScheduled); - this.form.get('workPackagePrioritized')?.setValue(settings.workPackagePrioritized); - this.form.get('workPackageCommented')?.setValue(settings.workPackageCommented); - - this.form.get('startDate.active')?.setValue(!!settings.startDate); - this.form.get('startDate.time')?.setValue(settings.startDate || this.availableTimes[1].value); - - this.form.get('dueDate.active')?.setValue(!!settings.dueDate); - this.form.get('dueDate.time')?.setValue(settings.dueDate || this.availableTimes[1].value); - - this.form.get('overdue.active')?.setValue(!!settings.overdue); - this.form.get('overdue.time')?.setValue(settings.overdue || this.availableTimesOverdue[0].value); - }); - - this.storeService.query.projectNotifications$ - .pipe(this.untilDestroyed()) - .subscribe((settings) => { - if (!settings) { - return; - } - - const projectSettings = new UntypedFormArray([]); - projectSettings.clear(); - settings - .sort( - (a, b):number => a._links.project.title!.localeCompare(b._links.project.title!), - ) - .forEach((setting) => projectSettings.push(new UntypedFormGroup({ - project: new UntypedFormControl(setting._links.project), - assignee: new UntypedFormControl(setting.assignee), - responsible: new UntypedFormControl(setting.responsible), - shared: new UntypedFormControl(setting.shared), - workPackageCreated: new UntypedFormControl(setting.workPackageCreated), - workPackageProcessed: new UntypedFormControl(setting.workPackageProcessed), - workPackageScheduled: new UntypedFormControl(setting.workPackageScheduled), - workPackagePrioritized: new UntypedFormControl(setting.workPackagePrioritized), - workPackageCommented: new UntypedFormControl(setting.workPackageCommented), - startDate: new UntypedFormControl(setting.startDate), - dueDate: new UntypedFormControl(setting.dueDate), - overdue: new UntypedFormControl(setting.overdue), - }))); - - this.form.setControl('projectSettings', projectSettings); - this.changeDetectorRef.detectChanges(); - }); - - this.form.enable(); - } - - public saveChanges():void { - const prefs = this.storeService.store.getValue(); - const notificationSettings = (this.form.value as IFullNotificationSettingsValue); - const globalNotification = prefs.notifications.find((notification) => !notification._links.project.href)!; - const globalPrefs:INotificationSetting = { - ...globalNotification, - _links: { project: { href: null } }, - watched: true, - mentioned: true, - assignee: notificationSettings.assignee, - responsible: notificationSettings.responsible, - shared: notificationSettings.shared, - workPackageCreated: notificationSettings.workPackageCreated, - workPackageProcessed: notificationSettings.workPackageProcessed, - workPackageScheduled: notificationSettings.workPackageScheduled, - workPackagePrioritized: notificationSettings.workPackagePrioritized, - workPackageCommented: notificationSettings.workPackageCommented, - startDate: notificationSettings.startDate.active ? notificationSettings.startDate.time : null, - dueDate: notificationSettings.dueDate.active ? notificationSettings.dueDate.time : null, - overdue: notificationSettings.overdue.active ? notificationSettings.overdue.time : null, - }; - - const projectPrefs:INotificationSetting[] = notificationSettings.projectSettings.map((settings) => ({ - _links: { project: { href: settings.project.href } }, - watched: true, - mentioned: true, - assignee: settings.assignee, - responsible: settings.responsible, - shared: settings.shared, - workPackageCreated: settings.workPackageCreated, - workPackageProcessed: settings.workPackageProcessed, - workPackageScheduled: settings.workPackageScheduled, - workPackagePrioritized: settings.workPackagePrioritized, - workPackageCommented: settings.workPackageCommented, - newsAdded: false, - newsCommented: false, - documentAdded: false, - forumMessages: false, - wikiPageAdded: false, - wikiPageUpdated: false, - membershipAdded: false, - membershipUpdated: false, - startDate: settings.startDate, - dueDate: settings.dueDate, - overdue: settings.overdue, - })); - - this.storeService.update(this.userId, { - ...prefs, - notifications: [ - globalPrefs, - ...projectPrefs, - ], - }); - } -} diff --git a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html deleted file mode 100644 index 4834bdd9576..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.html +++ /dev/null @@ -1,278 +0,0 @@ -@if (settings.length > 0) { -
- - - - - @for (item of settings.controls; track item) { - - } - - - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - @if (eeAvailable) { - - - @for (item of settings.controls; track item) { - - } - - } - @if (eeAvailable) { - - - @for (item of settings.controls; track item) { - - } - - } - @if (eeAvailable) { - - - @for (item of settings.controls; track item) { - - } - - } - @if (eeAvailable) { - - - @for (item of settings.controls; track item) { - - } - - } - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item) { - - } - - - - @for (item of settings.controls; track item; let index = $index) { - - } - - -
- {{ text.notify_me }} - - {{ item.controls.project.value.title }} -
-
{{ text.notifyImmediately.title }}
-

{{ text.notifyImmediately.description }}

-
- {{ text.mentioned_header.title }} -
{{ text.watched_header }}
- {{ text.assignee }} - - -
- {{ text.responsible }} - - -
- {{ text.shared }} - - -
-
{{ text.dateAlerts.title }}
-

{{ text.dateAlerts.description }}

-
- {{ text.startDate }} - - -
- {{ text.dueDate }} - - -
- {{ text.overdue }} - - -
-
{{ text.alsoNotifyFor.title }}
-

{{ text.alsoNotifyFor.description }}

-
{{ text.work_package_created_header }} - -
{{ text.work_package_processed_header }} - -
{{ text.work_package_scheduled_header }} - -
{{ text.work_package_prioritized_header }} - -
{{ text.work_package_commented_header }} - -
- -
-
-} - -@if (userId) { - -} diff --git a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.sass b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.sass deleted file mode 100644 index a42ccde83a9..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.sass +++ /dev/null @@ -1,21 +0,0 @@ -@import "helpers" - -:host - max-width: 100% - width: 100% - -.op-table - margin-bottom: $spot-spacing-1 - -.op-reminder-settings-table-date-alerts - &--time - height: $spot-spacing-2 - width: auto - min-width: $spot-spacing-8 - padding: $spot-spacing-0_25 $spot-spacing-1_5 $spot-spacing-0_25 $spot-spacing-0_75 - margin: auto - -.op-table--cell--date-alerts - border: 1px solid var(--borderColor-default) - text-align: center - padding: $spot-spacing-0_75 $spot-spacing-1 diff --git a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts b/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts deleted file mode 100644 index 3263b25e8f6..00000000000 --- a/frontend/src/app/features/user-preferences/notifications-settings/table/notification-settings-table.component.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { UntypedFormArray, UntypedFormControl, UntypedFormGroup } from '@angular/forms'; -import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import idFromLink from 'core-app/features/hal/helpers/id-from-link'; -import { BannersService } from 'core-app/core/enterprise/banners.service'; -import { overDueReminderTimes, reminderAvailableTimeframes } from '../overdue-reminder-available-times'; -import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import { HalSourceLink } from 'core-app/features/hal/interfaces'; - -@Component({ - selector: 'op-notification-settings-table', - templateUrl: './notification-settings-table.component.html', - styleUrls: ['./notification-settings-table.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class NotificationSettingsTableComponent implements OnInit { - @Input() userId:string; - - @Input() settings:UntypedFormArray; - - public eeAvailable = false; - - public availableTimes = [ - { - value: null, - title: this.I18n.t('js.notifications.settings.reminders.no_notification'), - }, - ...reminderAvailableTimeframes(), - ]; - - public availableTimesOverdue = [ - { - value: null, - title: this.I18n.t('js.notifications.settings.reminders.no_notification'), - }, - ...overDueReminderTimes(), - ]; - - text = { - notify_me: this.I18n.t('js.notifications.settings.notify_me'), - save: this.I18n.t('js.button_save'), - mentioned_header: { - title: this.I18n.t('js.notifications.settings.reasons.mentioned.title'), - description: this.I18n.t('js.notifications.settings.reasons.mentioned.description'), - }, - notifyImmediately: { - title: this.I18n.t('js.notifications.settings.global.immediately.title'), - description: this.I18n.t('js.notifications.settings.global.immediately.description'), - }, - alsoNotifyFor: { - title: this.I18n.t('js.notifications.settings.global.delayed.title'), - description: this.I18n.t('js.notifications.settings.global.delayed.description'), - }, - dateAlerts: { - title: this.I18n.t('js.notifications.settings.global.date_alerts.title'), - description: this.I18n.t('js.notifications.settings.global.date_alerts.description'), - }, - assignee: this.I18n.t('js.notifications.settings.reasons.assignee'), - responsible: this.I18n.t('js.notifications.settings.reasons.responsible'), - shared: this.I18n.t('js.notifications.settings.reasons.shared'), - watched_header: this.I18n.t('js.notifications.settings.reasons.watched'), - work_package_commented_header: this.I18n.t('js.notifications.settings.reasons.work_package_commented'), - work_package_created_header: this.I18n.t('js.notifications.settings.reasons.work_package_created'), - work_package_processed_header: this.I18n.t('js.notifications.settings.reasons.work_package_processed'), - work_package_prioritized_header: this.I18n.t('js.notifications.settings.reasons.work_package_prioritized'), - work_package_scheduled_header: this.I18n.t('js.notifications.settings.reasons.work_package_scheduled'), - remove_project_settings: this.I18n.t('js.notifications.settings.project_specific.remove'), - startDate: this.I18n.t('js.work_packages.properties.startDate'), - dueDate: this.I18n.t('js.work_packages.properties.dueDate'), - overdue: this.I18n.t('js.notifications.settings.global.overdue'), - }; - - constructor( - private I18n:I18nService, - private pathHelper:PathHelperService, - readonly bannersService:BannersService, - readonly configurationService:ConfigurationService, - ) {} - - ngOnInit():void { - this.eeAvailable = this.bannersService.allowsTo('date_alerts'); - } - - projectLink(href:string) { - return this.pathHelper.projectPath(idFromLink(href)); - } - - addProjectSettings(project:HalSourceLink):void { - this.settings.push(new UntypedFormGroup({ - project: new UntypedFormControl(project), - assignee: new UntypedFormControl(false), - responsible: new UntypedFormControl(false), - shared: new UntypedFormControl(false), - workPackageCreated: new UntypedFormControl(false), - workPackageProcessed: new UntypedFormControl(false), - workPackageScheduled: new UntypedFormControl(false), - workPackagePrioritized: new UntypedFormControl(false), - workPackageCommented: new UntypedFormControl(false), - startDate: new UntypedFormControl(this.availableTimes[2].value), - dueDate: new UntypedFormControl(this.availableTimes[2].value), - overdue: new UntypedFormControl(this.availableTimesOverdue[0].value), - })); - } - - removeProjectSettings(index:number):void { - this.settings.removeAt(index); - } -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component.html b/frontend/src/app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component.html deleted file mode 100644 index 2312f5211d6..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component.html +++ /dev/null @@ -1,19 +0,0 @@ - -
-

-

-
- - @for (setting of alerts; track setting) { - - - - } -
diff --git a/frontend/src/app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component.ts b/frontend/src/app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component.ts deleted file mode 100644 index 94e2847819e..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; -import { - UntypedFormGroup, - FormGroupDirective, -} from '@angular/forms'; - -export type EmailAlertType = - 'newsAdded'|'newsCommented'|'documentAdded'|'forumMessages'|'wikiPageAdded'| - 'wikiPageUpdated'|'membershipAdded'|'membershipUpdated'; - -export const emailAlerts:EmailAlertType[] = [ - 'newsAdded', - 'newsCommented', - 'documentAdded', - 'forumMessages', - 'wikiPageAdded', - 'wikiPageUpdated', - 'membershipAdded', - 'membershipUpdated', -]; - -@Component({ - selector: 'op-email-alerts-settings', - templateUrl: './email-alerts-settings.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class EmailAlertsSettingsComponent implements OnInit { - form:UntypedFormGroup; - - alerts:EmailAlertType[] = emailAlerts; - - text = { - title: this.I18n.t('js.reminders.settings.alerts.title'), - explanation: this.I18n.t('js.reminders.settings.alerts.explanation'), - newsAdded: this.I18n.t('js.reminders.settings.alerts.news_added'), - newsCommented: this.I18n.t('js.reminders.settings.alerts.news_commented'), - documentAdded: this.I18n.t('js.reminders.settings.alerts.document_added'), - forumMessages: this.I18n.t('js.reminders.settings.alerts.forum_messages'), - wikiPageAdded: this.I18n.t('js.reminders.settings.alerts.wiki_page_added'), - wikiPageUpdated: this.I18n.t('js.reminders.settings.alerts.wiki_page_updated'), - membershipAdded: this.I18n.t('js.reminders.settings.alerts.membership_added'), - membershipUpdated: this.I18n.t('js.reminders.settings.alerts.membership_updated'), - }; - - constructor( - private I18n:I18nService, - private storeService:UserPreferencesService, - private rootFormGroup:FormGroupDirective, - ) { - } - - ngOnInit():void { - this.form = this.rootFormGroup.control.get('emailAlerts') as UntypedFormGroup; - } -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component.html b/frontend/src/app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component.html deleted file mode 100644 index 1a1bc1accf8..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component.html +++ /dev/null @@ -1,29 +0,0 @@ - -
-

-
- - - - - - - - -
diff --git a/frontend/src/app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component.ts b/frontend/src/app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component.ts deleted file mode 100644 index e7bf3f707d4..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; -import { - UntypedFormGroup, - FormGroupDirective, -} from '@angular/forms'; - -@Component({ - selector: 'op-immediate-reminder-settings', - templateUrl: './immediate-reminder-settings.component.html', - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class ImmediateReminderSettingsComponent implements OnInit { - form:UntypedFormGroup; - - text = { - title: this.I18n.t('js.reminders.settings.immediate.title'), - explanation: this.I18n.t('js.reminders.settings.immediate.explanation'), - mentioned: this.I18n.t('js.reminders.settings.immediate.mentioned'), - personalReminder: this.I18n.t('js.reminders.settings.immediate.personal_reminder'), - }; - - constructor( - private I18n:I18nService, - private storeService:UserPreferencesService, - private rootFormGroup:FormGroupDirective, - ) { - } - - ngOnInit():void { - this.form = this.rootFormGroup.control.get('immediateReminders') as UntypedFormGroup; - } -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.html b/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.html deleted file mode 100644 index bec895d0f5b..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.html +++ /dev/null @@ -1,19 +0,0 @@ -@if (formInitialized) { -
- - - - -
- -
- -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.sass b/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.sass deleted file mode 100644 index f0a45e0f578..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.sass +++ /dev/null @@ -1,6 +0,0 @@ -// TODO: remove once we have a standard -// styling for these section headings -.form--section-title - text-transform: initial - border-bottom: initial - margin-bottom: initial !important \ No newline at end of file diff --git a/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.ts b/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.ts deleted file mode 100644 index e9ff944f64d..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/page/reminder-settings-page.component.ts +++ /dev/null @@ -1,193 +0,0 @@ -import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit } from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { CurrentUserService } from 'core-app/core/current-user/current-user.service'; -import { take } from 'rxjs/internal/operators/take'; -import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; -import { UntypedFormArray, UntypedFormBuilder } from '@angular/forms'; -import { - DailyRemindersSettings, - ImmediateRemindersSettings, - IUserPreference, - PauseRemindersSettings, -} from 'core-app/features/user-preferences/state/user-preferences.model'; -import { - emailAlerts, - EmailAlertType, -} from 'core-app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component'; -import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; -import { filter, withLatestFrom } from 'rxjs/operators'; -import { filterObservable } from 'core-app/shared/helpers/rxjs/filterWith'; -import { INotificationSetting } from 'core-app/features/user-preferences/state/notification-setting.model'; -import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; - -interface IReminderSettingsFormValue { - immediateReminders:ImmediateRemindersSettings, - dailyReminders:DailyRemindersSettings, - pauseReminders:Partial, - emailAlerts:Record; - workdays:boolean[]; -} - -@Component({ - templateUrl: './reminder-settings-page.component.html', - styleUrls: ['./reminder-settings-page.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class ReminderSettingsPageComponent extends UntilDestroyedMixin implements OnInit { - @Input() userId:string; - - public form = this.fb.group({ - immediateReminders: this.fb.group({ - mentioned: this.fb.control(false), - personalReminder: this.fb.control(false), - }), - dailyReminders: this.fb.group({ - enabled: this.fb.control(false), - times: this.fb.array([]), - }), - pauseReminders: this.fb.group({ - enabled: this.fb.control(false), - firstDay: this.fb.control(''), - lastDay: this.fb.control(''), - }), - workdays: this.fb.array([ - this.fb.control(false), - this.fb.control(true), - this.fb.control(true), - this.fb.control(true), - this.fb.control(true), - this.fb.control(true), - this.fb.control(false), - ]), - emailAlerts: this.fb.group({ - newsAdded: this.fb.control(false), - newsCommented: this.fb.control(false), - documentAdded: this.fb.control(false), - forumMessages: this.fb.control(false), - wikiPageAdded: this.fb.control(false), - wikiPageUpdated: this.fb.control(false), - membershipAdded: this.fb.control(false), - membershipUpdated: this.fb.control(false), - }), - }); - - text = { - title: this.I18n.t('js.reminders.settings.title'), - save: this.I18n.t('js.button_save'), - }; - - formInitialized = false; - - constructor( - readonly elementRef:ElementRef, - readonly I18n:I18nService, - readonly storeService:UserPreferencesService, - readonly currentUserService:CurrentUserService, - readonly fb:UntypedFormBuilder, - readonly cdRef:ChangeDetectorRef, - ) { - super(); - populateInputsFromDataset(this); - } - - ngOnInit():void { - this - .currentUserService - .user$ - .pipe(take(1)) - .subscribe((user) => { - this.userId = this.userId || user?.id!; - this.storeService.get(this.userId); - }); - - this.storeService.query.select() - .pipe( - filter((settings) => !!settings), - withLatestFrom(this.storeService.query.globalNotification$), - filterObservable(this.storeService.query.selectLoading(), (val) => !val), - ) - .subscribe(([settings, globalSetting]) => { - this.buildForm(settings, globalSetting); - }); - } - - private buildForm(settings:IUserPreference, globalSetting:INotificationSetting) { - this.form.get('immediateReminders.mentioned')?.setValue(settings.immediateReminders.mentioned); - this.form.get('immediateReminders.personalReminder')?.setValue(settings.immediateReminders.personalReminder); - - this.form.get('dailyReminders.enabled')?.setValue(settings.dailyReminders.enabled); - - this.form.get('pauseReminders')?.patchValue(settings.pauseReminders); - - const dailyReminderTimes = this.form.get('dailyReminders.times') as UntypedFormArray; - dailyReminderTimes.clear({ emitEvent: false }); - [...settings.dailyReminders.times].sort().forEach((time) => { - dailyReminderTimes.push(this.fb.control(time), { emitEvent: false }); - }); - - dailyReminderTimes.enable({ emitEvent: true }); - - const workdays = this.form.get('workdays') as UntypedFormArray; - for (let i = 0; i <= 6; i++) { - const control = workdays.at(i); - control.setValue(settings.workdays.includes(i + 1)); - } - - emailAlerts.forEach((alert) => { - this.form.get(`emailAlerts.${alert}`)?.setValue(globalSetting[alert]); - }); - - this.formInitialized = true; - this.cdRef.detectChanges(); - } - - public saveChanges():void { - const prefs = this.storeService.query.getValue(); - const globalNotifications = prefs.notifications.filter((notification) => !notification._links.project.href); - const projectNotifications = prefs.notifications.filter((notification) => !!notification._links.project.href); - const reminderSettings = (this.form.value as IReminderSettingsFormValue); - const workdays = ReminderSettingsPageComponent.buildWorkdays(reminderSettings.workdays); - const pauseReminders = ReminderSettingsPageComponent.buildPauses(reminderSettings.pauseReminders); - const { dailyReminders, immediateReminders } = reminderSettings; - - this.storeService.update(this.userId, { - ...prefs, - workdays, - dailyReminders, - immediateReminders, - pauseReminders, - notifications: [ - ...globalNotifications.map((notification) => ( - { - ...notification, - ...reminderSettings.emailAlerts, - } - )), - ...projectNotifications, - ], - }); - } - - private static buildWorkdays(formValues:boolean[]):number[] { - return formValues - .reduce( - (result, val, index) => { - if (val) { - return result.concat([index + 1]); - } - - return result; - }, - [] as number[], - ); - } - - private static buildPauses(formValues:Partial):Partial { - if (formValues.enabled) { - return formValues; - } - - return { enabled: false }; - } -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.html b/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.html deleted file mode 100644 index a5b12fe528e..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.html +++ /dev/null @@ -1,23 +0,0 @@ -
- - - - - @if ((enabled$ | async)) { - - } -
diff --git a/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.sass b/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.sass deleted file mode 100644 index 774cd70c18a..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.sass +++ /dev/null @@ -1,6 +0,0 @@ -.op-pause-reminders - display: flex - align-items: center - - &--checkbox - margin-right: 2rem \ No newline at end of file diff --git a/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.ts b/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.ts deleted file mode 100644 index fbededd5d1b..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/pause-reminders/pause-reminders.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; -import { - UntypedFormGroup, - FormGroupDirective, -} from '@angular/forms'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { - map, - startWith, -} from 'rxjs/operators'; -import { Observable } from 'rxjs'; - -@Component({ - selector: 'op-pause-reminders', - templateUrl: './pause-reminders.component.html', - styleUrls: ['./pause-reminders.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class PauseRemindersComponent implements OnInit { - form:UntypedFormGroup; - - selectedDates$:Observable<[string, string]>; - - enabled$:Observable; - - text = { - label: this.I18n.t('js.reminders.settings.pause.label'), - date_placeholder: this.I18n.t('js.placeholders.date'), - first_day: this.I18n.t('js.reminders.settings.pause.first_day'), - last_day: this.I18n.t('js.reminders.settings.pause.first_day'), - }; - - constructor( - private I18n:I18nService, - private rootFormGroup:FormGroupDirective, - ) { - } - - ngOnInit():void { - this.form = this.rootFormGroup.control.get('pauseReminders') as UntypedFormGroup; - this.selectedDates$ = this - .form - .valueChanges - .pipe( - startWith(this.form.value), - map((form:{ firstDay:string, lastDay:string }) => [form.firstDay, form.lastDay]), - ); - - this.enabled$ = this - .form - .valueChanges - .pipe( - startWith(this.form.value), - map((form:{ enabled:boolean }) => form.enabled), - ); - } - - setDates($event:[string, string]):void { - const [firstDay, lastDay] = $event; - this.form.patchValue({ - firstDay, - lastDay, - }); - } -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.html b/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.html deleted file mode 100644 index 01e36dce6fc..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.html +++ /dev/null @@ -1,73 +0,0 @@ -@if ((selectedTimes$ | async); as selectedTimes) { - -
-

-

-
- - - - @for (time of selectedTimes; track i; let i = $index) { -
- @if ((activeTimes$ | async); as activeTimes) { - - } - - - @if (timeRemovable$ | async) { - - } -
- } - -
-} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.sass b/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.sass deleted file mode 100644 index 6699f05ee65..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.sass +++ /dev/null @@ -1,37 +0,0 @@ -@import "helpers" - -.op-reminder-settings-daily-time - &--enable - display: flex - align-items: center - margin-top: 30px - margin-bottom: 10px - - &--row - margin-left: 40px - line-height: 45px - display: flex - align-items: center - - &:nth-of-type(1) - margin-top: 10px - - &--active - flex: 0 0 25px - - &--label - flex: 0 0 80px - margin-bottom: 0 - @include text-shortener - - &--time - height: 32px - width: 150px - - &--remove - padding-left: 10px - - &--add - margin-top: 20px - margin-left: 40px - width: auto diff --git a/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.ts b/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.ts deleted file mode 100644 index e0d081fc302..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component.ts +++ /dev/null @@ -1,251 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { - map, - shareReplay, - startWith, -} from 'rxjs/operators'; -import { - combineLatest, - NEVER, - Observable, -} from 'rxjs'; -import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; -import { - UntypedFormArray, - UntypedFormControl, - UntypedFormGroup, - FormGroupDirective, -} from '@angular/forms'; -import { ConfigurationService } from 'core-app/core/config/configuration.service'; -import moment from 'moment'; - -@Component({ - selector: 'op-reminder-settings-daily-time', - templateUrl: './reminder-settings-daily-time.component.html', - styleUrls: ['./reminder-settings-daily-time.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class ReminderSettingsDailyTimeComponent implements OnInit { - // All times that are available in a day with a 1 hour gap between each. - // ['00:00', '01:00', ..., '24:00'] - public availableTimes:string[] = ReminderSettingsDailyTimeComponent.setupAvailableTimes(); - - // The times (hours) that the user deactivated. Those are only stored within the component - // as the inactive hours are not persisted. This list is then interleaved with the list - // of times stored in the backend. As the order of the times should be kept, - // the position needs to be maintained. - // Upon a reload of the page, it is accepted to loose this information. - public inactiveTimes:{ position:number, time:string }[] = []; - - public form:UntypedFormGroup; - - // Hours suggested if a new time is added by a user. - public suggestedTimes = ['08:00', '12:00', '15:00', '18:00']; - - // Whether the reminder are active at all. - public enabled$:Observable; - - // The active times as present in the store interleaved with the inactive - // times. - public selectedTimes$:Observable = NEVER; - - // Times that are truly active: - // * the reminders are not disabled completely - // * the times are not inactive individually. - public activeTimes$:Observable = NEVER; - - // Times can only be removed if the element is active and if there is more than one time present. - public timeRemovable$:Observable = NEVER; - - // Times can not be added if the element is disabled or if all the possible times have already been added (active or not). - public nonAddable$:Observable = NEVER; - - text = { - title: this.I18n.t('js.reminders.settings.daily.title'), - explanation: this.I18n.t('js.reminders.settings.daily.explanation', - { no_time_zone: this.configurationService.isTimezoneSet() ? '' : this.I18n.t('js.reminders.settings.daily.no_time_zone') }), - timeLabel: (counter:number):string => this.I18n.t('js.reminders.settings.daily.time_label', { counter }), - addTime: this.I18n.t('js.reminders.settings.daily.add_time'), - enable: this.I18n.t('js.reminders.settings.daily.enable'), - }; - - constructor( - private I18n:I18nService, - private storeService:UserPreferencesService, - private rootFormGroup:FormGroupDirective, - private configurationService:ConfigurationService, - ) { - } - - ngOnInit():void { - this.form = this.rootFormGroup.control.get('dailyReminders') as UntypedFormGroup; - - this.enabled$ = this - .form - .valueChanges - .pipe( - startWith(() => this.form.get('enabled')?.value as boolean), - map(() => this.form.get('enabled')?.value as boolean), - shareReplay(1), - ); - - this.selectedTimes$ = (this - .form - .get('times') as UntypedFormArray) - .valueChanges - .pipe( - startWith(() => this.form.get('times')?.value as UntypedFormArray), - map(() => { - const timesArray = this.form.get('times') as UntypedFormArray; - const activeTimes = timesArray.controls.map((c) => c.value as string); - - this - .inactiveTimes - .sort((a, b) => a.position - b.position) - .forEach((inactiveTime) => { - activeTimes.splice(inactiveTime.position, 0, inactiveTime.time); - }); - - return activeTimes; - }), - shareReplay(1), - ); - - this.timeRemovable$ = combineLatest([ - this.enabled$, - this.selectedTimes$, - ]).pipe(map(([enabled, selectedTimes]) => enabled && selectedTimes.length > 1)); - - this.nonAddable$ = combineLatest([ - this.enabled$, - this.selectedTimes$, - ]).pipe(map(([enabled, selectedTimes]) => !enabled || selectedTimes.length === this.availableTimes.length)); - - this.activeTimes$ = combineLatest([ - this.enabled$, - this.selectedTimes$, - ]).pipe( - map(([enabled, times]) => (enabled ? times : [])), - ); - } - - addTime(selectedTimes:string[]):void { - const time = this.firstAvailableSuggested(selectedTimes) || this.firstAfterSelected(selectedTimes); - - if (time) { - this.storeTimes(selectedTimes.concat(time)); - } - } - - changeTime(newTime:string, selectedTimes:string[], index:number):void { - selectedTimes.splice(index, 1, newTime); - - this.storeTimes(selectedTimes); - } - - isActive(time:string):boolean { - return !this.inactiveTimes.find((inactive) => inactive.time === time); - } - - removeTime(selectedTimes:string[], index:number):void { - this.inactiveTimes = this - .inactiveTimes - .filter((inactiveTime) => inactiveTime.time !== selectedTimes[index]); - - this.inactiveTimes - .forEach((inactiveTime) => { - if (inactiveTime.position > index) { - inactiveTime.position -= 1; - } - }); - - selectedTimes.splice(index, 1); - - if (selectedTimes.length === 1) { - this.inactiveTimes = []; - } - - // Activate the first time if none is active. - if (selectedTimes.length === this.inactiveTimes.length) { - this.inactiveTimes.shift(); - } - - this.storeTimes(selectedTimes); - } - - toggleActive(active:boolean, index:number, selectedTimes:string[]):void { - if (!active) { - this.inactiveTimes.push({ position: index, time: selectedTimes[index] }); - } else { - this.inactiveTimes = this.inactiveTimes.filter((inactiveTime) => inactiveTime.time !== selectedTimes[index]); - } - - this.storeTimes(selectedTimes); - } - - timeLabel(time:string):string { - return this - .I18n - .toTime( - 'time.formats.time', - ReminderSettingsDailyTimeComponent.dateForHour(parseInt(time.split(':')[0], 10)), - ); - } - - isDisabled(time:string, activeTimes:string[]):boolean { - return activeTimes.length === 0 || (activeTimes.length === 1 && activeTimes[0] === time); - } - - private storeTimes(selectedTimes:string[]) { - const times = selectedTimes - .filter( - (selected) => !this.inactiveTimes - .map((inactive) => inactive.time) - .includes(selected), - ); - - const timesForm = this.form.get('times') as UntypedFormArray; - timesForm.clear({ emitEvent: false }); - times.forEach((time) => { - timesForm.push(new UntypedFormControl(time), { emitEvent: false }); - }); - - timesForm.enable({ emitEvent: true }); - } - - private firstAvailableSuggested(selectedTimes:string[]) { - return this.availableTimes.find((v) => this.suggestedTimes.includes(v) && !selectedTimes.includes(v)); - } - - private firstAfterSelected(selectedTimes:string[]) { - const indexLastSelected = this.availableTimes.indexOf(selectedTimes[selectedTimes.length - 1]); - - for (let i = indexLastSelected; i < 24 + indexLastSelected; i++) { - if (!selectedTimes.includes(this.availableTimes[i % 24])) { - return this.availableTimes[i % 24]; - } - } - - return null; - } - - private static setupAvailableTimes() { - return Array.from({ length: 24 }, (v, i) => ReminderSettingsDailyTimeComponent - .dateForHour(i) - .toLocaleTimeString('en-US', { hour12: false, hour: 'numeric', minute: 'numeric' })); - } - - private static dateForHour(hour:number) { - const currentTime = new Date(); - currentTime.setTime(1000 * 60 * 60 * (hour - 1)); - const convertTimeObject = new Date(moment(currentTime).utc().hours(hour).format('YYYY-MM-DDTHH:mm:ss')); - - return convertTimeObject; - } -} diff --git a/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.html b/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.html deleted file mode 100644 index 5d072c119d0..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.html +++ /dev/null @@ -1,21 +0,0 @@ - -
-

-
- - @for (workday of localeWorkdays; track workday; let i = $index) { - - - - } - - -
diff --git a/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.sass b/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.sass deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.ts b/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.ts deleted file mode 100644 index 4cf2267f5c5..00000000000 --- a/frontend/src/app/features/user-preferences/reminder-settings/workdays/workdays-settings.component.ts +++ /dev/null @@ -1,70 +0,0 @@ -import { - ChangeDetectionStrategy, - Component, - OnInit, -} from '@angular/core'; -import { - UntypedFormArray, - UntypedFormControl, - FormGroupDirective, -} from '@angular/forms'; -import moment from 'moment'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; - -@Component({ - selector: 'op-workdays-settings', - templateUrl: './workdays-settings.component.html', - styleUrls: ['./workdays-settings.component.sass'], - changeDetection: ChangeDetectionStrategy.OnPush, - standalone: false, -}) -export class WorkdaysSettingsComponent implements OnInit { - control:UntypedFormArray; - - /** - * The locale might render workdays in a different order, which is what moment return with localeSorted - * and used for rendering the component. - */ - localeWorkdays:string[] = moment.weekdays(true); - - /** - * Almost* ISO workdays with localized strings. - * ISO workdays are 1=Monday, ... 7=Sunday which is what we persist - * - * Working with the FormArray however, we use 0=Monday, 6=Sunday and add one before saving - * @private - */ - private isoWorkdays:string[] = WorkdaysSettingsComponent.buildISOWeekdays(); - - text = { - title: this.I18n.t('js.reminders.settings.workdays.title'), - }; - - constructor( - private I18n:I18nService, - readonly formGroup:FormGroupDirective, - ) { - } - - ngOnInit():void { - this.control = this.formGroup.control.get('workdays') as UntypedFormArray; - } - - indexOfLocalWorkday(day:string):number { - return this.isoWorkdays.indexOf(day); - } - - controlForLocalWorkday(day:string):UntypedFormControl { - const index = this.indexOfLocalWorkday(day); - return this.control.at(index) as UntypedFormControl; - } - - /** Workdays from moment.js are in non-ISO order, that means Sunday=0, Saturday=6 */ - static buildISOWeekdays():string[] { - const days = moment.weekdays(false); - - days.push(days.shift()!); - - return days; - } -} diff --git a/frontend/src/app/features/user-preferences/user-preferences.module.ts b/frontend/src/app/features/user-preferences/user-preferences.module.ts index 42915655cb2..544aaadc82a 100644 --- a/frontend/src/app/features/user-preferences/user-preferences.module.ts +++ b/frontend/src/app/features/user-preferences/user-preferences.module.ts @@ -6,44 +6,13 @@ import { OpenprojectAutocompleterModule, } from 'core-app/shared/components/autocompleter/openproject-autocompleter.module'; import { UserPreferencesService } from 'core-app/features/user-preferences/state/user-preferences.service'; -import { - NotificationsSettingsPageComponent, -} from 'core-app/features/user-preferences/notifications-settings/page/notifications-settings-page.component'; -import { - NotificationSettingInlineCreateComponent, -} from 'core-app/features/user-preferences/notifications-settings/inline-create/notification-setting-inline-create.component'; -import { - NotificationSettingsTableComponent, -} from './notifications-settings/table/notification-settings-table.component'; -import { ReminderSettingsPageComponent } from './reminder-settings/page/reminder-settings-page.component'; -import { - ReminderSettingsDailyTimeComponent, -} from 'core-app/features/user-preferences/reminder-settings/reminder-time/reminder-settings-daily-time.component'; -import { - ImmediateReminderSettingsComponent, -} from 'core-app/features/user-preferences/reminder-settings/immediate-reminders/immediate-reminder-settings.component'; -import { - EmailAlertsSettingsComponent, -} from 'core-app/features/user-preferences/reminder-settings/email-alerts/email-alerts-settings.component'; -import { WorkdaysSettingsComponent } from './reminder-settings/workdays/workdays-settings.component'; -import { PauseRemindersComponent } from './reminder-settings/pause-reminders/pause-reminders.component'; import { OpenprojectEnterpriseModule } from 'core-app/features/enterprise/openproject-enterprise.module'; @NgModule({ providers: [ UserPreferencesService, ], - declarations: [ - NotificationsSettingsPageComponent, - NotificationSettingInlineCreateComponent, - NotificationSettingsTableComponent, - ReminderSettingsPageComponent, - ReminderSettingsDailyTimeComponent, - ImmediateReminderSettingsComponent, - EmailAlertsSettingsComponent, - WorkdaysSettingsComponent, - PauseRemindersComponent, - ], + declarations: [], imports: [ CommonModule, OpSharedModule, diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts index 2f71507324c..feec49ccfae 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-time-value/filter-date-time-value.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, Input, HostBinding, @@ -46,6 +47,10 @@ import { AbstractDateTimeValueController } from '../abstract-filter-date-time-va selector: 'op-filter-date-time-value', templateUrl: './filter-date-time-value.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FilterDateTimeValueComponent extends AbstractDateTimeValueController implements OnInit { @HostBinding('id') get id() { diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts index a44e50b69a3..dcc3fccaf18 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-times-value/filter-date-times-value.component.ts @@ -28,6 +28,7 @@ import { Moment } from 'moment'; import { + ChangeDetectionStrategy, Component, HostBinding, Input, @@ -46,6 +47,10 @@ import { validDate } from 'core-app/shared/components/datepicker/helpers/date-mo selector: 'op-filter-date-times-value', templateUrl: './filter-date-times-value.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FilterDateTimesValueComponent extends AbstractDateTimeValueController implements OnInit { @HostBinding('id') get id() { diff --git a/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts index d36f9de0d8d..b3fc174bdf4 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-date-value/filter-date-value.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, HostBinding, Input, @@ -44,6 +45,10 @@ import moment from 'moment-timezone'; selector: 'op-filter-date-value', templateUrl: './filter-date-value.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FilterDateValueComponent extends UntilDestroyedMixin { @HostBinding('id') get id() { diff --git a/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts index 80e5e44af23..2812c33b302 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-dates-value/filter-dates-value.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, HostBinding, Input, @@ -44,6 +45,10 @@ import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/que selector: 'op-filter-dates-value', templateUrl: './filter-dates-value.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FilterDatesValueComponent extends UntilDestroyedMixin { @HostBinding('id') get id() { diff --git a/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts index 971e072c233..2002cde4d30 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-integer-value/filter-integer-value.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -39,6 +39,10 @@ import { QueryFilterResource } from 'core-app/features/hal/resources/query-filte selector: 'op-filter-integer-value', templateUrl: './filter-integer-value.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FilterIntegerValueComponent extends UntilDestroyedMixin { @Input() public shouldFocus = false; diff --git a/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts b/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts index 54b93725063..64b35623ffe 100644 --- a/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/filter-string-value/filter-string-value.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedEventEmitter } from 'core-app/shared/helpers/rxjs/debounced-event-emitter'; @@ -38,6 +38,10 @@ import { QueryFilterInstanceResource } from 'core-app/features/hal/resources/que selector: 'op-filter-string-value', templateUrl: './filter-string-value.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FilterStringValueComponent extends UntilDestroyedMixin { @Input() public shouldFocus = false; diff --git a/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts b/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts index 69664ad43aa..2a3bba8124a 100644 --- a/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/query-filter/query-filter.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, EventEmitter, HostBinding, @@ -54,6 +55,10 @@ import { WorkPackageViewBaselineService } from 'core-app/features/work-packages/ templateUrl: './query-filter.component.html', encapsulation: ViewEncapsulation.None, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class QueryFilterComponent implements OnInit { @HostBinding('class.op-query-filter') className = true; diff --git a/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts b/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts index cba6054524c..6052eecc646 100644 --- a/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts +++ b/frontend/src/app/features/work-packages/components/filters/quick-filter-by-text-input/quick-filter-by-text-input.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, EventEmitter, Output } from '@angular/core'; +import { ChangeDetectionStrategy, Component, EventEmitter, Output } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; import { Subject } from 'rxjs'; @@ -42,6 +42,10 @@ import { QueryFilterResource } from 'core-app/features/hal/resources/query-filte selector: 'wp-filter-by-text-input', templateUrl: './quick-filter-by-text-input.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageFilterByTextInputComponent extends UntilDestroyedMixin { @Output() public deactivateFilter = new EventEmitter(); diff --git a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts index 69079cc74f7..2adba03db45 100644 --- a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb-parent.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, EventEmitter, Input, @@ -42,6 +43,10 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service templateUrl: './wp-breadcrumb-parent.html', selector: 'wp-breadcrumb-parent', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageBreadcrumbParentComponent { @Input() workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts index d3e6c689375..f56b4b1a23a 100644 --- a/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-breadcrumb/wp-breadcrumb.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -36,6 +36,10 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service styleUrls: ['./wp-breadcrumb.sass'], selector: 'wp-breadcrumb', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageBreadcrumbComponent { @Input() workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts b/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts index c78ac5003ae..88c060906d3 100644 --- a/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-buttons/wp-status-button/wp-status-button.component.ts @@ -29,7 +29,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { HalResourceEditingService } from 'core-app/shared/components/fields/edit/services/hal-resource-editing.service'; import { - ChangeDetectorRef, Component, Input, OnInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { Highlighting } from 'core-app/features/work-packages/components/wp-fast-table/builders/highlighting/highlighting.functions'; @@ -42,6 +42,10 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; styleUrls: ['./wp-status-button.component.sass'], templateUrl: './wp-status-button.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageStatusButtonComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts b/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts index 4c0ab445b3d..70ea0c0272f 100644 --- a/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-card-view/wp-single-card/wp-single-card.component.ts @@ -35,7 +35,7 @@ import { isClickedWithModifier } from 'core-app/shared/helpers/link-handling/lin import isNewResource from 'core-app/features/hal/helpers/is-new-resource'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; import { StatusResource } from 'core-app/features/hal/resources/status-resource'; -import { combineLatest } from 'rxjs'; +import { EMPTY, merge } from 'rxjs'; import { map } from 'rxjs/operators'; import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; import SpotDropAlignmentOption from 'core-app/spot/drop-alignment-options'; @@ -131,10 +131,13 @@ export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implemen ngOnInit():void { // Update selection state - combineLatest([ + // Use merge instead of combineLatest: params$ only emits on uiRouter transitions and + // may never emit on pages that don't use uiRouter (e.g. boards). With merge, any + // emission from either source triggers re-evaluation of the selection state. + merge( this.wpTableSelection.live$(), - this.uiRouterGlobals.params$, - ]) + this.uiRouterGlobals.params$ ?? EMPTY, + ) .pipe( this.untilDestroyed(), map(() => { @@ -145,7 +148,7 @@ export class WorkPackageSingleCardComponent extends UntilDestroyedMixin implemen return this.wpTableSelection.isSelected(this.workPackage.id!); }), ) - .subscribe((selected) => { + .subscribe((selected:boolean) => { this.selected = selected; this.cdRef.detectChanges(); }); diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts index 7e9287aef2f..3685e3af233 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.ts @@ -32,6 +32,7 @@ import { ChangeDetectorRef, Component, ElementRef, + inject, OnInit, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -51,28 +52,24 @@ export class CustomDateActionAdminComponent implements OnInit { public visibleValue = ''; - /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ - public selectedOperator:any; - private onKey = 'on'; private currentKey = 'current'; private currentFieldValue = '%CURRENT_DATE%'; + private elementRef = inject(ElementRef); + private cdRef = inject(ChangeDetectorRef); + public appRef = inject(ApplicationRef); + private I18n = inject(I18nService); + + public selectedOperatorKey = this.onKey; + public operators = [ { key: this.onKey, label: this.I18n.t('js.custom_actions.date.specific') }, { key: this.currentKey, label: this.I18n.t('js.custom_actions.date.current_date') }, ]; - constructor( - private elementRef:ElementRef, - private cdRef:ChangeDetectorRef, - public appRef:ApplicationRef, - private I18n:I18nService, - ) { - } - // cannot use $onInit as it would be called before the operators gets filled public ngOnInit() { const element = this.elementRef.nativeElement as HTMLElement; @@ -80,18 +77,18 @@ export class CustomDateActionAdminComponent implements OnInit { this.fieldValue = element.dataset.fieldValue! || ''; if (this.fieldValue === this.currentFieldValue) { - this.selectedOperator = this.operators[1]; + this.selectedOperatorKey = this.currentKey; } else { - this.selectedOperator = this.operators[0]; + this.selectedOperatorKey = this.onKey; this.visibleValue = this.fieldValue; } this.toggleValueVisibility(); + this.cdRef.markForCheck(); } public toggleValueVisibility() { - /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */ - this.valueVisible = this.selectedOperator.key === this.onKey; + this.valueVisible = this.selectedOperatorKey === this.onKey; if (this.fieldValue === this.currentFieldValue) { this.fieldValue = ''; } @@ -100,8 +97,7 @@ export class CustomDateActionAdminComponent implements OnInit { } private updateDbValue() { - /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */ - if (this.selectedOperator.key === this.currentKey) { + if (this.selectedOperatorKey === this.currentKey) { this.fieldValue = this.currentFieldValue; } } diff --git a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.html b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.html index d48aaea0622..21318ec8ec0 100644 --- a/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.html +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.html @@ -2,12 +2,13 @@ diff --git a/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts b/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts index 481832b26c4..70919c585f0 100644 --- a/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-edit/wp-edit-field/wp-replacement-label.component.ts @@ -27,7 +27,7 @@ //++ import { - Component, ElementRef, Input, OnInit, + ChangeDetectionStrategy, Component, ElementRef, Input, OnInit, } from '@angular/core'; import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @@ -35,6 +35,10 @@ import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-f selector: 'wp-replacement-label', templateUrl: './wp-replacement-label.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageReplacementLabelComponent implements OnInit { @Input() public fieldName:string; diff --git a/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts b/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts index 84263bebc70..4a8ebf88201 100644 --- a/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-form-group/wp-attribute-group.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, HostBinding, Injector, Input, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, Injector, Input, ViewEncapsulation } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { EditFormComponent } from 'core-app/shared/components/fields/edit/edit-form/edit-form.component'; @@ -42,6 +42,10 @@ import { styleUrls: ['./wp-attribute-group.component.sass'], encapsulation: ViewEncapsulation.None, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageFormAttributeGroupComponent extends UntilDestroyedMixin { @HostBinding('class.wp-attribute-group') className = true; diff --git a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts index ecab383fb15..97473e2d33a 100644 --- a/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-inline-create/wp-inline-create.component.ts @@ -28,6 +28,7 @@ import { AfterViewInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, @@ -74,6 +75,10 @@ import { delegate, DelegateEvent } from '@knowledgecode/delegate'; selector: '[wpInlineCreate]', templateUrl: './wp-inline-create.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageInlineCreateComponent extends UntilDestroyedMixin implements OnInit, AfterViewInit { @Input() colspan:number; diff --git a/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts b/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts index 25dd9c08817..40070a787e4 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations-count/wp-relations-count.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { combineLatest } from 'rxjs'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; @@ -7,6 +7,11 @@ import { WorkPackageRelationsService } from 'core-app/features/work-packages/com @Component({ templateUrl: './wp-relations-count.html', selector: 'wp-relations-count', + standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsCountComponent extends UntilDestroyedMixin implements OnInit { @Input() wpId:string; @@ -14,7 +19,8 @@ export class WorkPackageRelationsCountComponent extends UntilDestroyedMixin impl public count = 0; constructor(protected apiV3Service:ApiV3Service, - protected wpRelations:WorkPackageRelationsService) { + protected wpRelations:WorkPackageRelationsService, + protected cdRef:ChangeDetectorRef) { super(); } @@ -38,6 +44,7 @@ export class WorkPackageRelationsCountComponent extends UntilDestroyedMixin impl const childrenCount = _.size(workPackage.children); this.count = relationCount + childrenCount; + this.cdRef.markForCheck(); }); } } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts index bec391f3fd9..d0ffc9275db 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/children/wp-children-query.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; @@ -52,6 +52,10 @@ import { WorkPackageRelationsService } from 'core-app/features/work-packages/com { provide: WorkPackageInlineCreateService, useClass: WpChildrenInlineCreateService }, ], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageChildrenQueryComponent extends WorkPackageRelationQueryBase implements OnInit { @Input() public workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts index c4902fd431c..3057f20973b 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/inline/add-existing/wp-relation-inline-add-existing.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageInlineCreateService, @@ -55,6 +55,10 @@ import { FilterOperator } from 'core-app/shared/helpers/api-v3/api-v3-filter-bui @Component({ templateUrl: './wp-relation-inline-add-existing.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpRelationInlineAddExistingComponent { public selectedWpId:string; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts index ecfb67aa0a3..30a888bb6ce 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/embedded/relations/wp-relation-query.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, Inject, Input, @@ -56,6 +57,10 @@ import { GroupDescriptor } from 'core-app/features/work-packages/components/wp-s { provide: WorkPackageInlineCreateService, useClass: WpRelationInlineCreateService }, ], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationQueryComponent extends WorkPackageRelationQueryBase implements OnInit { @Input() public workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts index 5e22e3abdbb..2fafea2ad62 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relation-row/wp-relation-row.component.ts @@ -1,7 +1,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { - ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Input, OnInit, ViewChild, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { HalEventsService } from 'core-app/features/hal/services/hal-events.service'; @@ -17,6 +17,10 @@ import { Highlighting } from 'core-app/features/work-packages/components/wp-fast selector: 'wp-relation-row', templateUrl: './wp-relation-row.template.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationRowComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; @@ -93,6 +97,7 @@ export class WorkPackageRelationRowComponent extends UntilDestroyedMixin impleme this.untilDestroyed(), ).subscribe((wp) => { this.relatedWorkPackage = wp; + this.cdRef.markForCheck(); }); } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts index 3535d03b265..3d2c1564cd0 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-create.component.ts @@ -1,5 +1,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, Input, } from '@angular/core'; @@ -13,6 +15,10 @@ import { WorkPackageRelationsService } from '../wp-relations.service'; selector: 'wp-relations-create', templateUrl: './wp-relation-create.template.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsCreateComponent { @Input() readonly workPackage:WorkPackageResource; @@ -38,6 +44,7 @@ export class WorkPackageRelationsCreateComponent { protected wpRelations:WorkPackageRelationsService, protected notificationService:WorkPackageNotificationService, protected halEvents:HalEventsService, + protected cdRef:ChangeDetectorRef, ) { } @@ -72,5 +79,6 @@ export class WorkPackageRelationsCreateComponent { this.showRelationsCreateForm = !this.showRelationsCreateForm; // Reset value this.selectedWpId = ''; + this.cdRef.markForCheck(); } } diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts index 9d70bd26248..7b1390eb61a 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-group/wp-relations-group.component.ts @@ -28,7 +28,7 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { - Component, ElementRef, EventEmitter, HostBinding, Input, Output, ViewChild, + ChangeDetectionStrategy, Component, ElementRef, EventEmitter, HostBinding, Input, Output, ViewChild, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -36,6 +36,10 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; selector: 'wp-relations-group', templateUrl: './wp-relations-group.template.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsGroupComponent { @HostBinding('class.attributes-group') className = true; diff --git a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts index 9662eb217d6..1249d28bbe7 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-hierarchy/wp-relations-hierarchy.directive.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; @@ -43,6 +43,10 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; templateUrl: './wp-relations-hierarchy.template.html', hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsHierarchyComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts index 404de722baa..8ebc7a6c611 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/overview-tab/overview-tab.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { StateService } from '@uirouter/core'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -37,6 +37,10 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; templateUrl: './overview-tab.html', selector: 'wp-overview-tab', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageOverviewTabComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackage:WorkPackageResource; @@ -49,6 +53,7 @@ export class WorkPackageOverviewTabComponent extends UntilDestroyedMixin impleme readonly I18n:I18nService, readonly $state:StateService, readonly apiV3Service:ApiV3Service, + readonly cdRef:ChangeDetectorRef, ) { super(); } @@ -64,6 +69,9 @@ export class WorkPackageOverviewTabComponent extends UntilDestroyedMixin impleme .pipe( this.untilDestroyed(), ) - .subscribe((wp) => this.workPackage = wp); + .subscribe((wp) => { + this.workPackage = wp; + this.cdRef.markForCheck(); + }); } } diff --git a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts index fbd6bf18614..4d2b69fbc3d 100644 --- a/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-single-view-tabs/relations-tab/relations-tab.component.ts @@ -27,7 +27,7 @@ //++ import { UIRouterGlobals } from '@uirouter/core'; -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Input, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { UntilDestroyedMixin } from 'core-app/shared/helpers/angular/until-destroyed.mixin'; @@ -37,6 +37,10 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; templateUrl: './relations-tab.html', selector: 'wp-relations-tab', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageRelationsTabComponent extends UntilDestroyedMixin implements OnInit { @Input() public workPackageId?:string; @@ -47,6 +51,7 @@ export class WorkPackageRelationsTabComponent extends UntilDestroyedMixin implem readonly I18n:I18nService, readonly uiRouterGlobals:UIRouterGlobals, readonly apiV3Service:ApiV3Service, + readonly cdRef:ChangeDetectorRef, ) { super(); } @@ -65,6 +70,7 @@ export class WorkPackageRelationsTabComponent extends UntilDestroyedMixin implem ) .subscribe((wp) => { this.workPackage = wp; + this.cdRef.markForCheck(); }); } } diff --git a/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts b/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts index 0577678cc14..121c81957a2 100644 --- a/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-subject/wp-subject.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, Input, } from '@angular/core'; @@ -40,6 +41,10 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; selector: 'wp-subject', templateUrl: './wp-subject.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageSubjectComponent extends UntilDestroyedMixin { @Input() workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts b/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts index 90288add659..75f67ec7be8 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/config-menu/config-menu.component.ts @@ -1,5 +1,5 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Component, Injector } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector } from '@angular/core'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { OPContextMenuService } from 'core-app/shared/components/op-context-menu/op-context-menu.service'; import { WpTableConfigurationModalComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; @@ -8,6 +8,10 @@ import { WpTableConfigurationModalComponent } from 'core-app/features/work-packa templateUrl: './config-menu.template.html', selector: 'wp-table-config-menu', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackagesTableConfigMenuComponent { public text = { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts index d74fe0181b3..afadf97d491 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/columns-tab.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Injector, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { QueryColumn } from 'core-app/features/work-packages/components/wp-query/query-column'; import { @@ -14,6 +14,10 @@ import { @Component({ templateUrl: './columns-tab.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationColumnsTabComponent implements TabComponent, OnInit { public availableColumnsOptions = this.wpTableColumns.all.map((c) => this.column2Like(c)); diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts index 9c94f7544e3..3526c27874d 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/display-settings-tab.component.ts @@ -3,13 +3,17 @@ import { TabComponent } from 'core-app/features/work-packages/components/wp-tabl import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; import { WorkPackageViewSumService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sum.service'; -import { Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; import { QueryGroupByResource } from 'core-app/features/hal/resources/query-group-by-resource'; @Component({ selector: 'op-wp-table-configuration-settings-tab', templateUrl: './display-settings-tab.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationDisplaySettingsTabComponent implements TabComponent, OnInit { // Display mode @@ -46,6 +50,7 @@ export class WpTableConfigurationDisplaySettingsTabComponent implements TabCompo readonly wpTableGroupBy:WorkPackageViewGroupByService, readonly wpTableHierarchies:WorkPackageViewHierarchiesService, readonly wpTableSums:WorkPackageViewSumService, + readonly cdRef:ChangeDetectorRef, ) { } public onSave() { @@ -79,6 +84,7 @@ export class WpTableConfigurationDisplaySettingsTabComponent implements TabCompo .then(() => { this.availableGroups = _.sortBy(this.wpTableGroupBy.available, 'name'); this.currentGroup = this.wpTableGroupBy.current || this.availableGroups[0]; + this.cdRef.markForCheck(); }); } } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts index 5c06c8401a2..aa35a7696cf 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/sort-by-tab.component.ts @@ -1,4 +1,4 @@ -import { Component, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewSortByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-sort-by.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; @@ -24,6 +24,10 @@ export type SortingMode = 'automatic'|'manual'; @Component({ templateUrl: './sort-by-tab.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationSortByTabComponent implements TabComponent, OnInit { public text = { @@ -59,7 +63,8 @@ export class WpTableConfigurationSortByTabComponent implements TabComponent, OnI constructor(readonly injector:Injector, readonly I18n:I18nService, - readonly wpTableSortBy:WorkPackageViewSortByService) { + readonly wpTableSortBy:WorkPackageViewSortByService, + readonly cdRef:ChangeDetectorRef) { } @@ -105,6 +110,7 @@ export class WpTableConfigurationSortByTabComponent implements TabComponent, OnI this.updateUsedColumns(); this.fillUpSortElements(); + this.cdRef.markForCheck(); }); } diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts index 00c52546b20..0d8e8618b26 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/tabs/timelines-tab.component.ts @@ -1,4 +1,5 @@ import { + ChangeDetectionStrategy, Component, Injector, OnInit, @@ -15,6 +16,10 @@ import { StateService } from '@uirouter/angular'; @Component({ templateUrl: './timelines-tab.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationTimelinesTabComponent implements TabComponent, OnInit { public timelineVisible = false; diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts index 35ba32c5aea..e4effd2709a 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration-relation-selector.ts @@ -1,4 +1,6 @@ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, Injector, OnInit, @@ -16,6 +18,10 @@ import { SchemaCacheService } from 'core-app/core/schemas/schema-cache.service'; templateUrl: './wp-table-configuration-relation-selector.html', selector: 'wp-table-configuration-relation-selector', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpTableConfigurationRelationSelectorComponent implements OnInit { private relationFilterIds:string[] = [ @@ -60,18 +66,19 @@ export class WpTableConfigurationRelationSelectorComponent implements OnInit { readonly I18n:I18nService, readonly wpTableFilters:WorkPackageViewFiltersService, readonly ConfigurationService:ConfigurationService, - readonly schemaCache:SchemaCacheService) { + readonly schemaCache:SchemaCacheService, + readonly cdRef:ChangeDetectorRef) { } ngOnInit() { - const self:WpTableConfigurationRelationSelectorComponent = this; + void this.initializeRelationFilters(); + } - this.wpTableFilters - .onReady() - .then(() => { - self.availableRelationFilters = self.relationFiltersOf(self.wpTableFilters.availableFilters) as QueryFilterResource[]; - self.setSelectedRelationFilter(); - }); + private async initializeRelationFilters():Promise { + await this.wpTableFilters.onReady(); + this.availableRelationFilters = this.relationFiltersOf(this.wpTableFilters.availableFilters) as QueryFilterResource[]; + this.setSelectedRelationFilter(); + this.cdRef.markForCheck(); } private setSelectedRelationFilter():void { diff --git a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts index 5ce5d373973..d6f4a718a94 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal.ts @@ -1,5 +1,6 @@ import { ApplicationRef, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentFactoryResolver, @@ -41,6 +42,10 @@ export const WpTableConfigurationModalPrependToken = new InjectionToken `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class EmbeddedTablesMacroComponent { @Input() public queryProps:object; diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts index cc4975bbdb2..629b5d22e37 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table-entry.component.ts @@ -1,5 +1,5 @@ import { - Component, ElementRef, Input, + ChangeDetectionStrategy, Component, ElementRef, Input, } from '@angular/core'; import { populateInputsFromDataset } from 'core-app/shared/components/dataset-inputs'; import { @@ -17,6 +17,10 @@ export const wpTableEntrySelector = 'wp-embedded-table-entry'; [configuration]="configuration" /> `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageEmbeddedTableEntryComponent { @Input() public queryProps:unknown; diff --git a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts index ef0cb7cafaf..d2306ca88d4 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component.ts @@ -1,4 +1,4 @@ -import { AfterViewInit, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component, EventEmitter, Input, OnDestroy, OnInit, Output } from '@angular/core'; import { WorkPackageViewTimelineService, } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-timeline.service'; @@ -31,6 +31,10 @@ import { QueryRequestParams } from 'core-app/features/work-packages/components/w selector: 'wp-embedded-table', templateUrl: './wp-embedded-table.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseComponent implements OnInit, AfterViewInit, OnDestroy { @Input() public queryId?:string; @@ -96,7 +100,10 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo .wpListService .loadQueryFromExisting(query, params, this.queryProjectScope), ) - .then((query) => this.initializeStates(query)); + .then((query) => { + this.initializeStates(query); + this.cdRef.markForCheck(); + }); }); } @@ -162,6 +169,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo .then((query:QueryResource) => { this.initializeStates(query); this.onQueryLoaded.emit(query); + this.cdRef.markForCheck(); return query; }) .catch((error) => { @@ -169,6 +177,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo 'js.error.embedded_table_loading', { message: _.get(error, 'message', error) }, ); + this.cdRef.markForCheck(); this.onError.emit(error); }); diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts index 7fdb4da6927..b626f4afdb6 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-query-configuration.component.ts @@ -1,5 +1,5 @@ import { - AfterViewInit, ChangeDetectorRef, Component, Inject, OnInit, ViewChild, + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, Inject, OnInit, ViewChild, } from '@angular/core'; import { WorkPackageEmbeddedTableComponent } from 'core-app/features/work-packages/components/wp-table/embedded/wp-embedded-table.component'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; @@ -23,6 +23,10 @@ export interface QueryConfigurationLocals { hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], providers: [[{ provide: WpTableConfigurationService, useClass: RestrictedWpTableConfigurationService }]], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class ExternalQueryConfigurationComponent implements OnInit, AfterViewInit { @ViewChild('embeddedTableForConfiguration', { static: true }) private embeddedTable:WorkPackageEmbeddedTableComponent; diff --git a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts index 31fd96b32be..20c9e8341f0 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/external-configuration/external-relation-query-configuration.component.ts @@ -1,4 +1,5 @@ import { + ChangeDetectionStrategy, Component, } from '@angular/core'; import { WpTableConfigurationService } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.service'; @@ -16,6 +17,10 @@ import { ExternalQueryConfigurationComponent } from 'core-app/features/work-pack { provide: WpTableConfigurationModalPrependToken, useValue: WpTableConfigurationRelationSelectorComponent }, ], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class ExternalRelationQueryConfigurationComponent extends ExternalQueryConfigurationComponent { } diff --git a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts index 7d0fce3901f..56bf3d9deee 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component.spec.ts @@ -28,7 +28,7 @@ import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; -import { inject, TestBed, waitForAsync } from '@angular/core/testing'; +import { inject, TestBed } from '@angular/core/testing'; import { States } from 'core-app/core/states/states.service'; import { WorkPackageViewPaginationService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-pagination.service'; import { WorkPackageTablePaginationComponent } from 'core-app/features/work-packages/components/wp-table/table-pagination/wp-table-pagination.component'; @@ -67,15 +67,14 @@ function pageString(element:HTMLElement) { } describe('wpTablePagination Directive', () => { - beforeEach(waitForAsync(() => { + beforeEach(async () => { window.OpenProject = new OpenProject(); const WeekdayServiceStub = { loadWeekdays: () => of(true), }; - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + await TestBed.configureTestingModule({ declarations: [ WorkPackageTablePaginationComponent, OpIconComponent, @@ -95,7 +94,7 @@ describe('wpTablePagination Directive', () => { provideHttpClient(withInterceptorsFromDi()), ] }).compileComponents(); - })); + }); describe('page ranges and links', () => { it('should display the correct page range', diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts index 9ed2b49cb00..9e4e34a7982 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive.ts @@ -28,6 +28,7 @@ import { AfterViewInit, + ChangeDetectionStrategy, Component, ElementRef, Injector, @@ -94,6 +95,10 @@ import { IDay } from 'core-app/core/state/days/day.model'; selector: 'wp-timeline-container', templateUrl: './wp-timeline-container.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTimelineTableController extends UntilDestroyedMixin implements AfterViewInit { private element:HTMLElement; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts index d912dceefb1..ca8fd1e5948 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-relations.directive.ts @@ -27,7 +27,7 @@ //++ import { - Component, ElementRef, Injector, OnInit, + ChangeDetectionStrategy, Component, ElementRef, Injector, OnInit, } from '@angular/core'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { State } from '@openproject/reactivestates'; @@ -81,6 +81,10 @@ function newSegment(vp:TimelineViewParameters, selector: 'wp-timeline-relations', template: '
', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTableTimelineRelations extends UntilDestroyedMixin implements OnInit { @InjectField() querySpace:IsolatedQuerySpace; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts index 71a2d5115e4..1d384759303 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/global-elements/wp-timeline-static-elements.directive.ts @@ -26,6 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ import { + ChangeDetectionStrategy, Component, ElementRef, OnInit, @@ -43,6 +44,10 @@ import { TodayLineElement } from './wp-timeline.today-line'; selector: 'wp-timeline-static-elements', template: '
', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTableTimelineStaticElements implements OnInit { public element:HTMLElement; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts index 4b982d1c73a..44aa2768c37 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/grid/wp-timeline-grid.directive.ts @@ -27,6 +27,7 @@ //++ import { AfterViewInit, + ChangeDetectionStrategy, Component, ElementRef, } from '@angular/core'; @@ -46,6 +47,10 @@ import { WeekdayService } from 'core-app/core/days/weekday.service'; selector: 'wp-timeline-grid', template: '
', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTableTimelineGrid implements AfterViewInit { private activeZoomLevel:TimelineZoomLevel; diff --git a/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts b/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts index f7d40eb6c3f..c92bb105c57 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/timeline/header/wp-timeline-header.directive.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit } from '@angular/core'; import { WorkPackageTimelineTableController } from 'core-app/features/work-packages/components/wp-table/timeline/container/wp-timeline-container.directive'; import moment, { Moment } from 'moment'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -44,6 +44,10 @@ import { selector: timelineHeaderSelector, templateUrl: './wp-timeline-header.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTimelineHeaderController implements OnInit { public element:HTMLElement; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts index 2dd873ce1b2..ef1d857e15d 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tab-wrapper/wp-tab-wrapper.component.ts @@ -28,6 +28,7 @@ import { UIRouterGlobals } from '@uirouter/core'; import { + ChangeDetectionStrategy, Component, Input, OnInit, @@ -44,6 +45,10 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-packag templateUrl: './wp-tab-wrapper.html', selector: 'op-wp-tab', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpTabWrapperComponent implements OnInit { @Input() public workPackageId:string; diff --git a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts index 421af350c62..82ee26fa3c0 100644 --- a/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts +++ b/frontend/src/app/features/work-packages/components/wp-tabs/components/wp-tabs/wp-tabs.component.spec.ts @@ -1,5 +1,5 @@ import { Input, NO_ERRORS_SCHEMA } from '@angular/core'; -import { ComponentFixture, fakeAsync, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; import { KeepTabService } from 'core-app/features/work-packages/components/wp-single-view-tabs/keep-tab/keep-tab.service'; @@ -61,9 +61,9 @@ describe('WpTabsComponent', () => { fixture.detectChanges(); }); - it('displays the visible tab', fakeAsync(() => { + it('displays the visible tab', () => { const tabLink:HTMLElement = fixture.debugElement.query(By.css('[data-qa-tab-id="displayable-test-tab"]')).nativeElement; expect(tabLink.innerText).toContain('Displayable TestTab'); - })); + }); }); diff --git a/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts b/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts index 9de3c777814..f9e78f9c99c 100644 --- a/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-type-status/wp-type-status.component.ts @@ -27,12 +27,16 @@ //++ import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ selector: 'wp-type-status', templateUrl: './wp-type-status.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageTypeStatusComponent { @Input() workPackage:WorkPackageResource; diff --git a/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts b/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts index 504c129f94e..21cac7911e7 100644 --- a/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-base/wp--base.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { EditFormRoutingService } from 'core-app/shared/components/fields/edit/edit-form/edit-form-routing.service'; import { WorkPackageEditFormRoutingService } from 'core-app/features/work-packages/routing/wp-edit-form/wp-edit-form-routing.service'; import { @@ -47,6 +47,10 @@ export const wpBaseSelector = 'work-packages-base'; { provide: EditFormRoutingService, useClass: WorkPackageEditFormRoutingService }, ], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackagesBaseComponent { } diff --git a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html index f662ff05abb..27c5c0e46f4 100644 --- a/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html +++ b/frontend/src/app/features/work-packages/routing/wp-split-view/wp-split-view.html @@ -48,7 +48,7 @@ data-notification-selector='notification-scroll-container' > + [ndcDynamicInputs]="{ workPackage: workPackage }" /> } diff --git a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts index b4d5cc405f4..ad3b882cff1 100644 --- a/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts +++ b/frontend/src/app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy-indentation.service.spec.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { TestBed, waitForAsync } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { States } from 'core-app/core/states/states.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; import { WorkPackageViewHierarchiesService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-hierarchy.service'; @@ -58,7 +58,7 @@ describe('WorkPackageViewIndentation service', () => { }; } - beforeEach(waitForAsync(() => { + beforeEach(async () => { parentServiceSpy = jasmine.createSpyObj( 'WorkPackageRelationHierarchyService', ['changeParent'], @@ -66,8 +66,7 @@ describe('WorkPackageViewIndentation service', () => { parentServiceSpy.changeParent.and.resolveTo(); - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + await TestBed.configureTestingModule({ providers: [ States, IsolatedQuerySpace, @@ -77,15 +76,12 @@ describe('WorkPackageViewIndentation service', () => { { provide: WorkPackageRelationsHierarchyService, useValue: parentServiceSpy }, WorkPackageViewHierarchyIdentationService, ], - }) - .compileComponents() - .then(() => { - service = TestBed.inject(WorkPackageViewHierarchyIdentationService); - querySpace = TestBed.inject(IsolatedQuerySpace); - hierarchyServiceStub = TestBed.inject(WorkPackageViewHierarchiesService); - states = TestBed.inject(States); - }); - })); + }).compileComponents(); + service = TestBed.inject(WorkPackageViewHierarchyIdentationService); + querySpace = TestBed.inject(IsolatedQuerySpace); + hierarchyServiceStub = TestBed.inject(WorkPackageViewHierarchiesService); + states = TestBed.inject(States); + }); describe('canIndent', () => { it('Cannot indent without changeParent link', () => { diff --git a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.spec.ts b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.spec.ts index f5cd792ef21..3eef69de171 100644 --- a/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.spec.ts +++ b/frontend/src/app/shared/components/attribute-help-texts/attribute-help-text.component.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, fakeAsync, flush, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { CUSTOM_ELEMENTS_SCHEMA, DebugElement } from '@angular/core'; import { AttributeHelpTextComponent } from 'core-app/shared/components/attribute-help-texts/attribute-help-text.component'; import { By } from '@angular/platform-browser'; @@ -17,11 +17,11 @@ describe('AttributeHelpTextComponent', () => { let modalServiceStub:jasmine.SpyObj; const i18nStub = { t: (_scope:string|string[], _options?:Record) => 'Show help text' }; - beforeEach(() => { + beforeEach(async () => { modalServiceStub = jasmine.createSpyObj('AttributeHelpTextModalService', ['show']); modalServiceStub.show.and.resolveTo(); - void TestBed + await TestBed .configureTestingModule({ declarations: [ AttributeHelpTextComponent, @@ -97,7 +97,7 @@ describe('AttributeHelpTextComponent', () => { expect(button.nativeElement.dataset.qaHelpTextFor).toEqual('subject'); }); - it('should call modalService on click', fakeAsync(() => { + it('should call modalService on click', async () => { const button = element.query(By.css("[role='button']")); button.nativeElement.click(); @@ -105,14 +105,16 @@ describe('AttributeHelpTextComponent', () => { expect(button.nativeElement.ariaDisabled).toEqual('true'); - flush(); + await Promise.resolve(); + await modalServiceStub.show.calls.mostRecent().returnValue; + await new Promise(resolve => setTimeout(resolve, 0)); fixture.detectChanges(); expect(modalServiceStub.show).toHaveBeenCalledOnceWith('1'); expect(button.nativeElement.ariaDisabled).toEqual('false'); - })); + }); - it('should call modalService only once', fakeAsync(() => { + it('should call modalService only once', async () => { const button = element.query(By.css("[role='button']")); button.nativeElement.click(); @@ -125,10 +127,12 @@ describe('AttributeHelpTextComponent', () => { button.triggerEventHandler('keydown.space'); fixture.detectChanges(); - flush(); + await Promise.resolve(); + await modalServiceStub.show.calls.mostRecent().returnValue; + await new Promise(resolve => setTimeout(resolve, 0)); fixture.detectChanges(); expect(modalServiceStub.show).toHaveBeenCalledOnceWith('1'); expect(button.nativeElement.ariaDisabled).toEqual('false'); - })); + }); }); diff --git a/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts index cc97b9ac391..7f0a46f252d 100644 --- a/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/members-autocompleter/members-autocompleter.component.ts @@ -1,7 +1,7 @@ import { firstValueFrom, Observable } from 'rxjs'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { HttpParams } from '@angular/common/http'; -import { Component, inject, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Input, OnInit } from '@angular/core'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { IUserAutocompleteItem, @@ -17,6 +17,10 @@ import { ID } from '@datorama/akita'; @Component({ templateUrl: '../op-autocompleter/op-autocompleter.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class MembersAutocompleterComponent extends UserAutocompleterComponent implements OnInit { @Input() principalType?:PrincipalType; @@ -24,6 +28,7 @@ export class MembersAutocompleterComponent extends UserAutocompleterComponent im readonly pathHelper = inject(PathHelperService); readonly currentUser = inject(CurrentUserService); readonly apiV3Service = inject(ApiV3Service); + readonly cdRef = inject(ChangeDetectorRef); ngOnInit() { super.ngOnInit(); @@ -36,6 +41,7 @@ export class MembersAutocompleterComponent extends UserAutocompleterComponent im if (canCreate) { this.addTag = this.createPlaceholderUser.bind(this); this.addTagText = this.I18n.t('js.invite_user_modal.placeholder_add_tag'); + this.cdRef.markForCheck(); } }); } diff --git a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts index 3b6e82b95f7..35d11473846 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.spec.ts @@ -1,4 +1,4 @@ -import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { States } from 'core-app/core/states/states.service'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { NO_ERRORS_SCHEMA } from '@angular/core'; @@ -52,13 +52,13 @@ describe('autocompleter', () => { }, ]; - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [OpAutocompleterComponent], - schemas: [NO_ERRORS_SCHEMA], - imports: [NgSelectModule], - providers: [States, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()] -}).compileComponents(); + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [OpAutocompleterComponent], + schemas: [NO_ERRORS_SCHEMA], + imports: [NgSelectModule], + providers: [States, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()], + }).compileComponents(); fixture = TestBed.createComponent(OpAutocompleterComponent); getOptionsFnSpy = jasmine.createSpy('getOptionsFn').and.callFake((searchTerm:string) => { @@ -79,62 +79,72 @@ describe('autocompleter', () => { fixture.componentInstance.debounceTimeMs = 0; }); - it('should load the ng-select correctly', fakeAsync(() => { - fixture.detectChanges(); - tick(); + it('should load the ng-select correctly', () => { + jasmine.clock().install(); + try { + fixture.detectChanges(); + jasmine.clock().tick(0); - const autocompleter = document.querySelector('.ng-select-container'); + const autocompleter = document.querySelector('.ng-select-container'); - expect(document.contains(autocompleter)).toBeTruthy(); - })); + expect(document.contains(autocompleter)).toBeTruthy(); + } finally { + jasmine.clock().uninstall(); + } + }); describe('without debounce', () => { - it('should load items', fakeAsync(() => { - tick(); - fixture.detectChanges(); - fixture.componentInstance.ngAfterViewInit(); - tick(1000); - fixture.detectChanges(); - const select = fixture.componentInstance.ngSelectInstance; + it('should load items', () => { + jasmine.clock().install(); + try { + jasmine.clock().tick(0); + fixture.detectChanges(); + fixture.componentInstance.ngAfterViewInit(); + jasmine.clock().tick(1000); + fixture.detectChanges(); + const select = fixture.componentInstance.ngSelectInstance; - expect(select.isOpen).toBeFalse(); - select.open(); - select.focus(); + expect(select.isOpen).toBeFalse(); + select.open(); + select.focus(); - expect(select.isOpen).toBeTrue(); + expect(select.isOpen).toBeTrue(); - expect(select.itemsList.items.length).toEqual(0); + expect(select.itemsList.items.length).toEqual(0); - const inputDebugElement = fixture.debugElement.query(By.css('input[role=combobox]')); - const inputElement = inputDebugElement.nativeElement as HTMLInputElement; + const inputDebugElement = fixture.debugElement.query(By.css('input[role=combobox]')); + const inputElement = inputDebugElement.nativeElement as HTMLInputElement; - fixture.detectChanges(); - tick(); + fixture.detectChanges(); + jasmine.clock().tick(0); - expect(getOptionsFnSpy).toHaveBeenCalledWith(''); + expect(getOptionsFnSpy).toHaveBeenCalledWith(''); - inputElement.value = 'Wor'; - inputElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - tick(); + inputElement.value = 'Wor'; + inputElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + jasmine.clock().tick(0); - expect(getOptionsFnSpy).toHaveBeenCalledWith('Wor'); + expect(getOptionsFnSpy).toHaveBeenCalledWith('Wor'); - fixture.detectChanges(); + fixture.detectChanges(); - expect(select.itemsList.items.length).toEqual(2); + expect(select.itemsList.items.length).toEqual(2); - inputElement.value = 'package 2'; - inputElement.dispatchEvent(new Event('input')); - fixture.detectChanges(); - tick(); + inputElement.value = 'package 2'; + inputElement.dispatchEvent(new Event('input')); + fixture.detectChanges(); + jasmine.clock().tick(0); - expect(getOptionsFnSpy).toHaveBeenCalledWith('package 2'); + expect(getOptionsFnSpy).toHaveBeenCalledWith('package 2'); - fixture.detectChanges(); + fixture.detectChanges(); - expect(select.itemsList.items.length).toEqual(1); - })); + expect(select.itemsList.items.length).toEqual(1); + } finally { + jasmine.clock().uninstall(); + } + }); }); describe('with debounce', () => { @@ -142,11 +152,12 @@ describe('autocompleter', () => { fixture.componentInstance.debounceTimeMs = 50; }); - it('should load items with debounce', fakeAsync(() => { - tick(); + it('should load items with debounce', async () => { fixture.detectChanges(); fixture.componentInstance.ngAfterViewInit(); - tick(1000); + + // Wait for ngAfterViewInit's internal setTimeout(25ms) and debounce to fire. + await new Promise(resolve => setTimeout(resolve, 100)); fixture.detectChanges(); const select = fixture.componentInstance.ngSelectInstance; @@ -163,18 +174,22 @@ describe('autocompleter', () => { fixture.detectChanges(); + // Wait for the initial '' search to fire via debounce. + await new Promise(resolve => setTimeout(resolve, 100)); + expect(getOptionsFnSpy).toHaveBeenCalledWith(''); getOptionsFnSpy.calls.reset(); inputElement.value = 'Wor'; inputElement.dispatchEvent(new Event('input')); fixture.detectChanges(); - tick(); expect(getOptionsFnSpy).not.toHaveBeenCalled(); - tick(50); + + // Wait for debounce (debounceTimeMs=50, but 0 in test env). + await new Promise(resolve => setTimeout(resolve, 100)); expect(getOptionsFnSpy).toHaveBeenCalledWith('Wor'); - })); + }); }); }); diff --git a/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts b/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts index baf439af296..d086822a54e 100644 --- a/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/work-package-autocompleter/wp-autocompleter.component.ts @@ -26,13 +26,17 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { AfterViewInit, Component } from '@angular/core'; +import { AfterViewInit, ChangeDetectionStrategy, Component } from '@angular/core'; import { CreateAutocompleterComponent } from 'core-app/shared/components/autocompleter/create-autocompleter/create-autocompleter.component'; @Component({ templateUrl: '../create-autocompleter/create-autocompleter.component.html', selector: 'wp-autocompleter', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageAutocompleterComponent extends CreateAutocompleterComponent implements AfterViewInit { } diff --git a/frontend/src/app/shared/components/date/op-date-time.component.ts b/frontend/src/app/shared/components/date/op-date-time.component.ts index 0b2deaf34d2..4020cd79f35 100644 --- a/frontend/src/app/shared/components/date/op-date-time.component.ts +++ b/frontend/src/app/shared/components/date/op-date-time.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; import { TimezoneService } from 'core-app/core/datetime/timezone.service'; @Component({ @@ -39,6 +39,10 @@ import { TimezoneService } from 'core-app/core/datetime/timezone.service';
`, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class OpDateTimeComponent implements OnInit { @Input() dateTimeValue:any; diff --git a/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts b/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts index ca07630a2c3..0000790accc 100644 --- a/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts +++ b/frontend/src/app/shared/components/datepicker/modal-single-date-picker/modal-single-date-picker.component.ts @@ -28,6 +28,7 @@ import { AfterContentInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, @@ -53,7 +54,6 @@ import { SpotDropModalTeleportationService, } from 'core-app/spot/components/drop-modal/drop-modal-teleportation.service'; -// eslint-disable-next-line change-detection-strategy/on-push @Component({ selector: 'op-modal-single-date-picker', templateUrl: './modal-single-date-picker.component.html', @@ -67,6 +67,10 @@ import { }, ], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class OpModalSingleDatePickerComponent implements ControlValueAccessor, OnInit, AfterContentInit { @Output() closed = new EventEmitter(); diff --git a/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts b/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts index 6a10ca2d57c..e66895b3706 100644 --- a/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts +++ b/frontend/src/app/shared/components/editable-toolbar-title/editable-toolbar-title.component.ts @@ -26,6 +26,8 @@ // See COPYRIGHT and LICENSE files for more details. //++ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, @@ -50,6 +52,10 @@ export const selectableTitleIdentifier = 'editable-toolbar-title'; templateUrl: './editable-toolbar-title.html', styleUrls: ['./editable-toolbar-title.sass'], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class EditableToolbarTitleComponent implements OnInit, OnChanges { @Input('title') public inputTitle:string; @@ -95,7 +101,7 @@ export class EditableToolbarTitleComponent implements OnInit, OnChanges { duplicate_query_title: this.I18n.t('js.work_packages.query.errors.duplicate_query_title'), }; - constructor(readonly injector:Injector) { + constructor(readonly injector:Injector, private cdRef:ChangeDetectorRef) { } ngOnInit():void { @@ -108,6 +114,7 @@ export class EditableToolbarTitleComponent implements OnInit, OnChanges { } this.selectedTitle = evt.detail ?? ''; + this.cdRef.markForCheck(); setTimeout(() => { const field:HTMLInputElement = this.inputField!.nativeElement; field.focus(); @@ -188,7 +195,7 @@ export class EditableToolbarTitleComponent implements OnInit, OnChanges { this.emitSave(this.selectedTitle); // Unset in-flight after some delay not to trigger the blur - setTimeout(() => this.inFlight = false, 100); + setTimeout(() => { this.inFlight = false; this.cdRef.markForCheck(); }, 100); } public get isEmpty():boolean { diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts index f956d8e6a9b..4739667235a 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.component.ts @@ -27,6 +27,9 @@ //++ import { + ApplicationRef, + ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, @@ -36,7 +39,6 @@ import { OnInit, Optional, Output, - ApplicationRef, } from '@angular/core'; import { StateService, Transition, TransitionService } from '@uirouter/core'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; @@ -62,6 +64,10 @@ import { firstValueFrom } from 'rxjs'; selector: 'edit-form,[edit-form]', template: '', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class EditFormComponent extends EditForm implements OnInit, OnDestroy { @Input() resource:HalResource; @@ -81,6 +87,7 @@ export class EditFormComponent extends EditForm implements OnInit, constructor(public readonly injector:Injector, protected readonly elementRef:ElementRef, private appRef:ApplicationRef, + private readonly cdRef:ChangeDetectorRef, protected readonly $transitions:TransitionService, protected readonly ConfigurationService:ConfigurationService, protected readonly editingPortalService:EditingPortalService, @@ -203,6 +210,7 @@ export class EditFormComponent extends EditForm implements OnInit, } protected focusOnFirstError():void { + this.cdRef.detectChanges(); // Focus the first field that is erroneous this.elementRef.nativeElement .querySelector(`.${activeFieldContainerClassName}.-error .${activeFieldClassName}`) diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.spec.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.spec.ts new file mode 100644 index 00000000000..f3a3bc9386d --- /dev/null +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.spec.ts @@ -0,0 +1,106 @@ +//-- 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. +//++ + +import { ApplicationRef, Injector } from '@angular/core'; +import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; +import { HalResource } from 'core-app/features/hal/resources/hal-resource'; +import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; + +class TestEditForm extends EditForm { + constructor( + injector:Injector, + private readonly requireVisibleSpy:(fieldName:string) => Promise, + private readonly activateFieldSpy:() => Promise, + private readonly resetSpy:(fieldName:string, focus?:boolean) => void, + ) { + super(injector); + } + + public requireVisible(fieldName:string):Promise { + return this.requireVisibleSpy(fieldName); + } + + public activateField():Promise { + return this.activateFieldSpy(); + } + + public reset(fieldName:string, focus?:boolean):void { + this.resetSpy(fieldName, focus); + } + + protected focusOnFirstError():void { + return undefined; + } +} + +describe('EditForm', () => { + it('does not require visibility twice for newly erroneous inactive fields', async () => { + const tick = jasmine.createSpy('tick'); + const requireVisible = jasmine.createSpy('requireVisible').and.resolveTo(); + const activateField = jasmine.createSpy('activateField').and.resolveTo({} as EditFieldHandler); + const reset = jasmine.createSpy('reset'); + const injector = { + get: jasmine.createSpy('get').and.callFake((token:unknown) => { + if (token === ApplicationRef) { + return { tick }; + } + + throw new Error(`Unexpected token: ${String(token)}`); + }), + } as unknown as Injector; + + const form = new TestEditForm(injector, requireVisible, activateField, reset); + const change = { + inFlight: false, + schema: { + ofProperty: jasmine.createSpy('ofProperty').and.returnValue({ + writable: true, + name: 'Foo', + } as IFieldSchema), + }, + getForm: jasmine.createSpy('getForm').and.resolveTo(), + }; + + form.resource = { id: 1 } as unknown as HalResource; + form.halEditing = { + changeFor: jasmine.createSpy('changeFor').and.returnValue(change), + } as never; + form.halNotification = { + handleRawError: jasmine.createSpy('handleRawError'), + showEditingBlockedError: jasmine.createSpy('showEditingBlockedError'), + } as never; + form.errorsPerAttribute = { foo: ['Required'] }; + + (form as unknown as { setErrorsForFields:(fields:string[]) => void }).setErrorsForFields(['foo']); + await Promise.resolve(); + await Promise.resolve(); + + expect(requireVisible).toHaveBeenCalledTimes(1); + }); +}); diff --git a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts index 4d74508c080..8cc5d768a45 100644 --- a/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts +++ b/frontend/src/app/shared/components/fields/edit/edit-form/edit-form.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Injector } from '@angular/core'; +import { ApplicationRef, Injector } from '@angular/core'; import { States } from 'core-app/core/states/states.service'; import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; import { @@ -43,6 +43,7 @@ import { ErrorResource } from 'core-app/features/hal/resources/error-resource'; import isNewResource from 'core-app/features/hal/helpers/is-new-resource'; import { HalError } from 'core-app/features/hal/services/hal-error'; import { FormResource } from 'core-app/features/hal/resources/form-resource'; +import { HalResourceEditFieldHandler } from 'core-app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler'; export const activeFieldContainerClassName = 'inline-edit--active-field'; export const activeFieldClassName = 'inline-edit--field'; @@ -173,6 +174,7 @@ export abstract class EditForm { // Mark changeset as in flight this.change.inFlight = true; + this.notifyActiveFieldStateChanged(); // Request custom field validation this.change.validateCustomFields = true; @@ -199,18 +201,24 @@ export abstract class EditForm { this.onSaved(result); this.change.inFlight = false; }) - .catch((error:ErrorResource|unknown) => { + .catch((error:unknown) => { + // Reset flags before handling errors so active portals can drop + // their disabled state in zoneless mode. + this.change.inFlight = false; + this.change.validateCustomFields = false; + this.notifyActiveFieldStateChanged(); + this.halNotification.handleRawError(error, this.resource); if (error instanceof HalError && error.resource) { this.handleSubmissionErrors(error.resource); - reject(); + this.injector.get(ApplicationRef).tick(); + reject(error instanceof Error ? error : new Error('Edit form submission failed.')); + return; } - this.change.inFlight = false; - this.change.validateCustomFields = false; - - return Promise.reject(error); + this.injector.get(ApplicationRef).tick(); + reject(error instanceof Error ? error : new Error('Edit form submission failed.')); }); }); } @@ -255,18 +263,25 @@ export abstract class EditForm { } private setErrorsForFields(erroneousFields:string[]) { - // Accumulate errors for the given response - const promises:Promise[] = erroneousFields.map((fieldName:string) => this.requireVisible(fieldName).then(() => { + // Immediately set errors on already-active fields (synchronous, no polling needed). + // This handles the common case where the field is already open when the 422 arrives. + erroneousFields.forEach((fieldName:string) => { if (this.activeFields[fieldName]) { this.activeFields[fieldName].setErrors(this.errorsPerAttribute[fieldName] || []); } + }); - return this.activateWhenNeeded(fieldName) as any; - })); + // Activate any fields that are not yet visible / open (e.g. required custom fields). + const promises:Promise[] = erroneousFields.map((fieldName:string) => this.activateWhenNeeded(fieldName)); Promise.all(promises) .then(() => { - setTimeout(() => this.focusOnFirstError()); + // Run CD again after any newly required fields are activated so their + // portal bindings reflect the reset inFlight state in zoneless mode. + queueMicrotask(() => { + this.injector.get(ApplicationRef).tick(); + this.focusOnFirstError(); + }); }) .catch(() => { console.error('Failed to activate all erroneous fields.'); @@ -331,4 +346,12 @@ export abstract class EditForm { this.halNotification.handleRawError(error); }); } + + private notifyActiveFieldStateChanged():void { + Object.values(this.activeFields).forEach((handler) => { + if (handler instanceof HalResourceEditFieldHandler) { + handler.notifyStateChanged(); + } + }); + } } diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts index 2b7bda22d37..10502c89c9a 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/edit-form-portal.component.ts @@ -1,5 +1,7 @@ import { AfterViewInit, + ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, EventEmitter, @@ -10,6 +12,8 @@ import { Output, } from '@angular/core'; import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; +import { HalResourceEditFieldHandler } from 'core-app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler'; +import { takeUntil } from 'rxjs/operators'; import { OpEditingPortalChangesetToken, OpEditingPortalHandlerToken, @@ -24,6 +28,10 @@ import { ResourceChangeset } from 'core-app/shared/components/fields/changeset/r selector: 'edit-form-portal', templateUrl: './edit-form-portal.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit { @Input() schemaInput:IFieldSchema; @@ -52,6 +60,7 @@ export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit readonly injector:Injector, readonly editField:EditFieldService, readonly elementRef:ElementRef, + readonly cdRef:ChangeDetectorRef, ) { } ngOnInit() { @@ -67,6 +76,16 @@ export class EditFormPortalComponent implements OnInit, OnDestroy, AfterViewInit this.componentClass = this.editField.getSpecificClassFor(this.change.pristineResource._type, this.handler.fieldName, this.schema.type); this.fieldInjector = createLocalInjector(this.injector, this.change, this.handler, this.schema); + + if (this.handler instanceof HalResourceEditFieldHandler) { + this.handler.errorsChanged$ + .pipe(takeUntil(this.handler.onDestroy)) + .subscribe(() => this.cdRef.detectChanges()); + + this.handler.stateChanged$ + .pipe(takeUntil(this.handler.onDestroy)) + .subscribe(() => this.cdRef.detectChanges()); + } } ngOnDestroy() { diff --git a/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts b/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts index 0993310435a..d1afb5eb3fd 100644 --- a/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts +++ b/frontend/src/app/shared/components/fields/edit/editing-portal/editing-portal-service.ts @@ -67,7 +67,10 @@ export class EditingPortalService { take(1), ) .toPromise() - .then(() => fieldHandler); + .then(() => { + ref.changeDetectorRef.detectChanges(); // ensure error classes applied in zoneless mode + return fieldHandler; + }); } /** diff --git a/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts b/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts index 4840b5877c6..bd2a9cda0de 100644 --- a/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-controls/edit-field-controls.component.ts @@ -27,7 +27,7 @@ //++ import { - Component, EventEmitter, Input, Output, + ChangeDetectionStrategy, Component, EventEmitter, Input, Output, } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @@ -35,6 +35,10 @@ import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit- selector: 'edit-field-controls', templateUrl: './edit-field-controls.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class EditFieldControlsComponent { @Input() public cancelTitle:string; diff --git a/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts b/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts index e041baa53b5..1983e828048 100644 --- a/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts +++ b/frontend/src/app/shared/components/fields/edit/field-handler/hal-resource-edit-field-handler.ts @@ -34,7 +34,7 @@ import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing import { setPosition } from 'core-app/shared/helpers/set-click-position/set-click-position'; import { debugLog } from 'core-app/shared/helpers/debug_output'; import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; -import { Subject } from 'rxjs'; +import { Subject, Observable } from 'rxjs'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { EditForm } from 'core-app/shared/components/fields/edit/edit-form/edit-form'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; @@ -54,6 +54,14 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { // Current errors of the field public errors:string[]; + // Fires when errors are updated, so portals can trigger their own CD + private readonly _errorsChanged$ = new Subject(); + public readonly errorsChanged$:Observable = this._errorsChanged$.asObservable(); + + // Fires when handler-derived state such as inFlight changes. + private readonly _stateChanged$ = new Subject(); + public readonly stateChanged$:Observable = this._stateChanged$.asObservable(); + constructor( public injector:Injector, public form:EditForm, @@ -109,6 +117,11 @@ export class HalResourceEditFieldHandler extends EditFieldHandler { public setErrors(newErrors:string[]) { this.errors = newErrors; this.element.classList.toggle('-error', this.isErrorenous); + this._errorsChanged$.next(); + } + + public notifyStateChanged() { + this._stateChanged$.next(); } /** diff --git a/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts index a03049d046c..3e2e3eb9373 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/boolean-edit-field/boolean-edit-field.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ @@ -41,6 +41,10 @@ import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit- [id]="handler.htmlId" /> `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class BooleanEditFieldComponent extends EditFieldComponent { public updateValue(newValue:boolean) { diff --git a/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts index 97dfb80021a..85b07bf6c1d 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/combined-date-edit-field.component.ts @@ -27,6 +27,7 @@ //++ import { + ChangeDetectionStrategy, Component, OnInit, } from '@angular/core'; @@ -36,6 +37,10 @@ import { WorkPackageResource } from 'core-app/features/hal/resources/work-packag @Component({ templateUrl: './combined-date-edit-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class CombinedDateEditFieldComponent extends DatePickerEditFieldComponent implements OnInit { dates = ''; diff --git a/frontend/src/app/shared/components/fields/edit/field-types/days-duration-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/days-duration-edit-field.component.ts index cdc2bc06463..8a1ddd8709a 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/days-duration-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/days-duration-edit-field.component.ts @@ -27,6 +27,7 @@ */ import { + ChangeDetectionStrategy, Component, OnInit, } from '@angular/core'; @@ -36,6 +37,10 @@ import moment from 'moment-timezone'; @Component({ templateUrl: './days-duration-edit-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class DaysDurationEditFieldComponent extends DatePickerEditFieldComponent implements OnInit { public get formattedValue():number { diff --git a/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts index 97d1beacc24..060f63a2f3b 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/float-edit-field.component.ts @@ -25,7 +25,7 @@ // See COPYRIGHT and LICENSE files for more details. // ++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ @@ -42,6 +42,10 @@ import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit- [id]="handler.htmlId" /> `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class FloatEditFieldComponent extends EditFieldComponent { public locale = I18n.locale; diff --git a/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts index 433a516ee83..74f9081c71d 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/integer-edit-field/integer-edit-field.component.ts @@ -25,7 +25,7 @@ // See COPYRIGHT and LICENSE files for more details. // ++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ @@ -41,6 +41,10 @@ import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit- [id]="handler.htmlId" /> `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class IntegerEditFieldComponent extends EditFieldComponent { public locale = I18n.locale; diff --git a/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts index bae64c61653..b97cd3d81c3 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/plain-formattable-edit-field.component.ts @@ -26,12 +26,16 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ templateUrl: './text-edit-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class PlainFormattableEditFieldComponent extends EditFieldComponent { // only exists because the template is reused and the property is required there. diff --git a/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts index 8cd45f22712..2bdafe0fe5f 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/project-status-edit-field.component.ts @@ -27,7 +27,7 @@ //++ import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { Component, OnInit, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; import { NgSelectComponent } from '@ng-select/ng-select'; import { @@ -38,6 +38,7 @@ import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decora import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { repositionDropdownBugfix } from 'core-app/shared/components/autocompleter/op-autocompleter/autocompleter.helper'; import { target } from 'core-app/shared/helpers/event-helpers'; +import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; interface ProjectStatusOption { href:string @@ -45,10 +46,18 @@ interface ProjectStatusOption { colorClass:string } +interface ProjectStatusResource extends HalResource { + status:{ href:string }|null; +} + @Component({ templateUrl: './project-status-edit-field.component.html', styleUrls: ['./project-status-edit-field.component.sass'], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class ProjectStatusEditFieldComponent extends EditFieldComponent implements OnInit { @ViewChild(NgSelectComponent, { static: true }) public ngSelectComponent:NgSelectComponent; @@ -68,30 +77,13 @@ export class ProjectStatusEditFieldComponent extends EditFieldComponent implemen public appendToContainer = 'body'; ngOnInit() { - this.currentStatusCode = this.resource.status === null ? this.availableStatuses[0].href : this.resource.status.href; - - this.change.getForm().then((form) => { - form.schema.status.allowedValues.forEach((status:HalResource) => { - this.availableStatuses = [...this.availableStatuses, - { - href: status.href!, - name: status.name, - colorClass: projectStatusCodeCssClass(status.id), - }]; - }); - - // The timeout takes care that the opening is added to the end of the current call stack. - // Thus we can be sure that the select box is rendered and ready to be opened. - const that = this; - window.setTimeout(() => { - that.ngSelectComponent.open(); - }, 0); - }); + this.currentStatusCode = this.projectStatusResource.status?.href ?? this.availableStatuses[0].href; + void this.loadAvailableStatuses(); } public onChange() { - this.resource.status = this.currentStatusCode === this.availableStatuses[0].href ? null : { href: this.currentStatusCode }; - this.handler.handleUserSubmit(); + this.projectStatusResource.status = this.currentStatusCode === this.availableStatuses[0].href ? null : { href: this.currentStatusCode }; + void this.handler.handleUserSubmit(); } public onOpen() { @@ -105,4 +97,30 @@ export class ProjectStatusEditFieldComponent extends EditFieldComponent implemen public onClose() { target(document.querySelector(this.hiddenOverflowContainer)!).off('scroll.autocompleteContainer'); } + + private get projectStatusResource():ProjectStatusResource { + return this.resource as ProjectStatusResource; + } + + private async loadAvailableStatuses():Promise { + await this.change.getForm(); + const statusSchema = (this.change.schema as { status?:IFieldSchema }).status; + const allowedValues = (statusSchema?.allowedValues ?? []) as HalResource[]; + + this.availableStatuses = [ + ...this.availableStatuses, + ...allowedValues.map((status:HalResource) => ({ + href: status.href!, + name: status.name, + colorClass: projectStatusCodeCssClass(status.id), + })), + ]; + this.cdRef.markForCheck(); + + // The timeout takes care that the opening is added to the end of the current call stack. + // Thus we can be sure that the select box is rendered and ready to be opened. + window.setTimeout(() => { + this.ngSelectComponent.open(); + }, 0); + } } diff --git a/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts index fba620a7026..e48a96c1f37 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/text-edit-field/text-edit-field.component.ts @@ -26,12 +26,16 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { EditFieldComponent } from 'core-app/shared/components/fields/edit/edit-field.component'; @Component({ templateUrl: '../text-edit-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class TextEditFieldComponent extends EditFieldComponent { // ToDo: Work package specific diff --git a/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts b/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts index 0c1af435d7d..3c3b162ac1a 100644 --- a/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts +++ b/frontend/src/app/shared/components/fields/edit/field-types/work-package-edit-field.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { HalResource } from 'core-app/features/hal/resources/hal-resource'; import { DebouncedRequestSwitchmap, @@ -40,6 +40,10 @@ import { CollectionResource } from 'core-app/features/hal/resources/collection-r @Component({ templateUrl: './work-package-edit-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageEditFieldComponent extends SelectEditFieldComponent { /** Keep a switchmap for search term and loading state */ diff --git a/frontend/src/app/shared/components/grids/grid/area.service.ts b/frontend/src/app/shared/components/grids/grid/area.service.ts index 4875ebeb5d2..fb080c15c5b 100644 --- a/frontend/src/app/shared/components/grids/grid/area.service.ts +++ b/frontend/src/app/shared/components/grids/grid/area.service.ts @@ -13,6 +13,17 @@ import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { ApiV3GridForm } from 'core-app/core/apiv3/endpoints/grids/apiv3-grid-form'; import { map } from 'rxjs/operators'; +interface GridWidgetPayload { + id:string; + [key:string]:unknown; +} + +interface GridPatchPayload { + id:string; + widgets?:GridWidgetPayload[]; + [key:string]:unknown; +} + @Injectable() export class GridAreaService { private resource:GridResource; @@ -117,16 +128,26 @@ export class GridAreaService { return this.saveGrid(this.resource, this.schema); } - public saveWidgetChangeset(changeset:WidgetChangeset) { - const payload:any = ApiV3GridForm.extractPayload(this.resource, this.schema); + public async saveWidgetChangeset(changeset:WidgetChangeset):Promise { + const payload = ApiV3GridForm.extractPayload(this.resource, this.schema) as GridPatchPayload; + const gridId = this.resource.id; + + const payloadWidget = payload.widgets?.find((widget) => widget.id === changeset.pristineResource.id); + + if (!payloadWidget) { + throw new Error(`Missing widget payload for ${changeset.pristineResource.id}`); + } + + if (!gridId) { + throw new Error('Missing grid id'); + } - const payloadWidget = payload.widgets.find((w:any) => w.id === changeset.pristineResource.id); Object.assign(payloadWidget, changeset.changes); // Adding the id so that the url can be deduced - payload.id = this.resource.id; + payload.id = gridId; - this.saveGrid(payload); + return this.saveGrid(payload); } public isGap(area:GridArea) { @@ -157,7 +178,7 @@ export class GridAreaService { } } - private async saveGrid(resource:GridResource, schema?:SchemaResource):Promise { + private async saveGrid(resource:GridResource|GridPatchPayload, schema?:SchemaResource):Promise { const subscription = this .apiV3Service .grids @@ -172,7 +193,11 @@ export class GridAreaService { }), ); - return firstValueFrom(subscription); + return firstValueFrom(subscription) + .catch((error:unknown) => { + this.toastService.addError(error instanceof Error ? error.message : String(error)); + throw error; + }); } private assignAreasWidget(newGrid:GridResource) { @@ -397,15 +422,14 @@ export class GridAreaService { }); } - public removeWidget(removedWidget:GridWidgetResource) { + public removeWidget(removedWidget:GridWidgetResource):Promise { let index = this.resource.widgets.findIndex((widget) => widget.id === removedWidget.id); this.resource.widgets.splice(index, 1); index = this.widgetAreas.findIndex((area) => area.widget.id === removedWidget.id); this.widgetAreas.splice(index, 1); this.cleanupUnusedAreas(); - - this.rebuildAndPersist(); + return this.rebuildAndPersist(); } public get widgetResources() { diff --git a/frontend/src/app/shared/components/grids/grid/grid.component.html b/frontend/src/app/shared/components/grids/grid/grid.component.html index 8e47165da89..8ca16c59cc7 100644 --- a/frontend/src/app/shared/components/grids/grid/grid.component.html +++ b/frontend/src/app/shared/components/grids/grid/grid.component.html @@ -31,7 +31,7 @@ @if (resize.isResizable) {
diff --git a/frontend/src/app/shared/components/grids/grid/grid.component.ts b/frontend/src/app/shared/components/grids/grid/grid.component.ts index 400f1d0e071..868383dabc6 100644 --- a/frontend/src/app/shared/components/grids/grid/grid.component.ts +++ b/frontend/src/app/shared/components/grids/grid/grid.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectorRef, Component, ComponentRef, HostListener, Input, OnDestroy, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ComponentRef, HostListener, Input, OnDestroy, OnInit } from '@angular/core'; import { GridResource } from 'core-app/features/hal/resources/grid-resource'; import { DomSanitizer } from '@angular/platform-browser'; import { GridWidgetsService } from 'core-app/shared/components/grids/widgets/widgets.service'; @@ -13,6 +13,7 @@ import { GridRemoveWidgetService } from 'core-app/shared/components/grids/grid/r import { WidgetWpGraphComponent } from 'core-app/shared/components/grids/widgets/wp-graph/wp-graph.component'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { BrowserDetector } from 'core-app/core/browser/browser-detector.service'; +import { WidgetChangeset } from 'core-app/shared/components/grids/widgets/widget-changeset'; export interface WidgetRegistration { identifier:string; @@ -34,6 +35,10 @@ export const GRID_PROVIDERS = [ templateUrl: './grid.component.html', selector: 'grid', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class GridComponent implements OnDestroy, OnInit { public uiWidgets:ComponentRef[] = []; @@ -78,10 +83,11 @@ export class GridComponent implements OnDestroy, OnInit { } public addWidget(area:GridWidgetArea|GridArea) { - void this - .add - .widget(area) - .then(() => this.cdRef.detectChanges()); + this.detectChangesAfter(this.add.widget(area)); + } + + public resizeEnd(area:GridWidgetArea) { + this.detectChangesAfter(this.resize.end(area)); } public widgetComponent(area:GridWidgetArea) { @@ -105,8 +111,12 @@ export class GridComponent implements OnDestroy, OnInit { return { resource: area.widget }; } - public widgetComponentOutput(area:GridWidgetArea) { - return { resourceChanged: this.layout.saveWidgetChangeset.bind(this.layout) }; + public widgetComponentOutput(_area:GridWidgetArea) { + return { + resourceChanged: (changeset:WidgetChangeset) => { + this.detectChangesAfter(this.layout.saveWidgetChangeset(changeset)); + }, + }; } public get gridColumnStyle() { @@ -159,4 +169,10 @@ export class GridComponent implements OnDestroy, OnInit { return this.sanitization.bypassSecurityTrustStyle(style); } + + private detectChangesAfter(promise:Promise|undefined) { + void promise + ?.finally(() => this.cdRef.detectChanges()) + .catch(() => undefined); + } } diff --git a/frontend/src/app/shared/components/grids/grid/remove-widget.service.ts b/frontend/src/app/shared/components/grids/grid/remove-widget.service.ts index ad7eb411e70..adabb32c08c 100644 --- a/frontend/src/app/shared/components/grids/grid/remove-widget.service.ts +++ b/frontend/src/app/shared/components/grids/grid/remove-widget.service.ts @@ -1,18 +1,21 @@ -import { Injectable } from '@angular/core'; +import { ChangeDetectorRef, Injectable, inject } from '@angular/core'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; @Injectable() export class GridRemoveWidgetService { - constructor(readonly layout:GridAreaService) { - } + readonly cdRef = inject(ChangeDetectorRef); + readonly layout = inject(GridAreaService); public area(area:GridWidgetArea) { - this.widget(area.widget); + return this.widget(area.widget); } - public widget(widget:GridWidgetResource) { - this.layout.removeWidget(widget); + public widget(widget:GridWidgetResource):Promise { + return this.layout + .removeWidget(widget) + .finally(() => this.cdRef.detectChanges()); } } diff --git a/frontend/src/app/shared/components/grids/grid/resize.service.ts b/frontend/src/app/shared/components/grids/grid/resize.service.ts index a27f2f8e78a..621a7596a56 100644 --- a/frontend/src/app/shared/components/grids/grid/resize.service.ts +++ b/frontend/src/app/shared/components/grids/grid/resize.service.ts @@ -2,6 +2,7 @@ import { Injectable } from '@angular/core'; import { GridWidgetArea } from 'core-app/shared/components/grids/areas/grid-widget-area'; import { GridArea } from 'core-app/shared/components/grids/areas/grid-area'; import { GridAreaService } from 'core-app/shared/components/grids/grid/area.service'; +import { GridResource } from 'core-app/features/hal/resources/grid-resource'; import { GridMoveService } from 'core-app/shared/components/grids/grid/move.service'; import { GridDragAndDropService } from 'core-app/shared/components/grids/grid/drag-and-drop.service'; @@ -15,22 +16,22 @@ export class GridResizeService { readonly move:GridMoveService, readonly drag:GridDragAndDropService) { } - public end(area:GridWidgetArea) { + public end(area:GridWidgetArea):Promise|undefined { if (!this.resizedArea) { - return; + return undefined; } this.resizedArea = null; // user aborted resizing if (area.unchangedSize) { - return; + return undefined; } this.layout.writeAreaChangesToWidgets(); this.layout.cleanupUnusedAreas(); - this.layout.rebuildAndPersist(); + return this.layout.rebuildAndPersist(); } public abort() { diff --git a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts index d7fe4f2149f..7b852cc5c27 100644 --- a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts +++ b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text-edit-field.service.ts @@ -1,7 +1,7 @@ import { EditFieldHandler } from 'core-app/shared/components/fields/edit/editing-portal/edit-field-handler'; import { ElementRef, Injectable, Injector } from '@angular/core'; import { IFieldSchema } from 'core-app/shared/components/fields/field.base'; -import { BehaviorSubject } from 'rxjs'; +import { BehaviorSubject, Subject } from 'rxjs'; import { GridWidgetResource } from 'core-app/features/hal/resources/grid-widget-resource'; import { SchemaResource } from 'core-app/features/hal/resources/schema-resource'; import { HalResourceService } from 'core-app/features/hal/services/hal-resource.service'; @@ -17,6 +17,8 @@ export class CustomTextEditFieldService extends EditFieldHandler { public valueChanged$:BehaviorSubject; + public readonly stateChanged$ = new Subject(); + public changeset:ResourceChangeset; public active:boolean; @@ -93,10 +95,12 @@ export class CustomTextEditFieldService extends EditFieldHandler { deactivate():void { this.changeset.clear(); this.active = false; + this.stateChanged$.next(); } activate() { this.active = true; + this.stateChanged$.next(); } get inEditMode():boolean { diff --git a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts index dda9fb18544..a1e0608f778 100644 --- a/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/custom-text/custom-text.component.ts @@ -65,6 +65,12 @@ export class WidgetCustomTextComponent extends AbstractWidgetComponent implement const changeset = this.setChangesetOptions({ text: { raw: newText } }); this.resourceChanged.emit(changeset); }); + + this + .handler + .stateChanged$ + .pipe(this.untilDestroyed()) + .subscribe(() => this.cdr.markForCheck()); } ngOnChanges(changes:SimpleChanges):void { diff --git a/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.ts b/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.ts index bb1085a044a..20f3dc0c9a7 100644 --- a/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/menu/widget-menu.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { WidgetAbstractMenuComponent } from 'core-app/shared/components/grids/widgets/menu/widget-abstract-menu.component'; @Component({ @@ -34,6 +34,10 @@ import { WidgetAbstractMenuComponent } from 'core-app/shared/components/grids/wi templateUrl: './widget-menu.component.html', styleUrls: ['./widget-menu.component.css'], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetMenuComponent extends WidgetAbstractMenuComponent { } diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts index 10120b4e2a7..f86c0ab9b6e 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/configuration-modal/configuration.modal.ts @@ -1,5 +1,5 @@ import { - ApplicationRef, ChangeDetectorRef, Component, ElementRef, Inject, Injector, OnInit, + ApplicationRef, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, Injector, OnInit, } from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; @@ -14,6 +14,10 @@ import { TimeEntriesCurrentUserConfigurationModalService } from 'core-app/shared templateUrl: './configuration.modal.html', providers: [TimeEntriesCurrentUserConfigurationModalService], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class TimeEntriesCurrentUserConfigurationModalComponent extends OpModalComponent implements OnInit { public text = { diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user-menu.component.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user-menu.component.ts index 51e9d4ed733..18903ac2782 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user-menu.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/current-user/time-entries-current-user-menu.component.ts @@ -27,7 +27,7 @@ //++ import { - Component, EventEmitter, Output, + ChangeDetectionStrategy, Component, EventEmitter, Output, } from '@angular/core'; import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { @@ -43,6 +43,10 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op selector: 'widget-time-entries-current-user-menu', templateUrl: '../../menu/widget-menu.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetTimeEntriesCurrentUserMenuComponent extends WidgetAbstractMenuComponent { @InjectField() opModalService:OpModalService; diff --git a/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts b/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts index 8efa3edd301..bcd54ca1384 100644 --- a/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/time-entries/project/time-entries-project.component.ts @@ -1,4 +1,5 @@ import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, Injector, @@ -19,6 +20,10 @@ import { HalResourceEditingService } from 'core-app/shared/components/fields/edi HalResourceEditingService, ], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetTimeEntriesProjectComponent extends WidgetTimeEntriesListComponent implements OnInit { constructor(readonly injector:Injector, diff --git a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph-menu.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph-menu.component.ts index fce97b4a5df..136cd771021 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph-menu.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-graph/wp-graph-menu.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { WpGraphConfigurationModalComponent } from 'core-app/shared/components/work-package-graphs/configuration-modal/wp-graph-configuration.modal'; import { WidgetWpSetMenuComponent } from 'core-app/shared/components/grids/widgets/menu/wp-set-menu.component'; @@ -34,6 +34,10 @@ import { WidgetWpSetMenuComponent } from 'core-app/shared/components/grids/widge selector: 'widget-wp-graph-menu', templateUrl: '../menu/widget-menu.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetWpGraphMenuComponent extends WidgetWpSetMenuComponent { protected configurationComponent = WpGraphConfigurationModalComponent; diff --git a/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-menu.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-menu.component.ts index 4f51e313ab3..71f38deb577 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-menu.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-menu.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { WpTableConfigurationModalComponent, } from 'core-app/features/work-packages/components/wp-table/configuration-modal/wp-table-configuration.modal'; @@ -39,6 +39,10 @@ import { firstValueFrom } from 'rxjs'; selector: 'widget-wp-table-menu', templateUrl: '../menu/widget-menu.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetWpTableMenuComponent extends WidgetWpSetMenuComponent { @InjectField() currentUser:CurrentUserService; diff --git a/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-qs.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-qs.component.ts index 85c1d4b1bf8..7d2599eb6f7 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-qs.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table-qs.component.ts @@ -1,4 +1,4 @@ -import { Component } from '@angular/core'; +import { ChangeDetectionStrategy, Component } from '@angular/core'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; import { WidgetChangeset } from 'core-app/shared/components/grids/widgets/widget-changeset'; @@ -6,6 +6,10 @@ import { WidgetChangeset } from 'core-app/shared/components/grids/widgets/widget templateUrl: './wp-table-qs.component.html', styleUrls: ['./wp-table-qs.component.sass'], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WidgetWpTableQuerySpaceComponent extends AbstractWidgetComponent { public onResourceChanged(changeset:WidgetChangeset) { diff --git a/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table.component.ts b/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table.component.ts index e807a7cf0eb..14c323fec79 100644 --- a/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table.component.ts +++ b/frontend/src/app/shared/components/grids/widgets/wp-table/wp-table.component.ts @@ -1,4 +1,4 @@ -import { ChangeDetectionStrategy, Component, inject, Injector, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, inject, Injector, OnInit } from '@angular/core'; import { AbstractWidgetComponent } from 'core-app/shared/components/grids/widgets/abstract-widget.component'; import { QueryFormResource } from 'core-app/features/hal/resources/query-form-resource'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @@ -39,6 +39,7 @@ export class WidgetWpTableComponent extends AbstractWidgetComponent implements O contextMenuEnabled: false, }; + protected cdRef = inject(ChangeDetectorRef); protected i18n = inject(I18nService); protected injector = inject(Injector); protected querySpace = inject(IsolatedQuerySpace); @@ -55,6 +56,7 @@ export class WidgetWpTableComponent extends AbstractWidgetComponent implements O this.resourceChanged.emit(changeset); this.queryId = query.id; + this.cdRef.markForCheck(); }); } else { this.queryId = this.resource.options.queryId as string; diff --git a/frontend/src/app/shared/components/icon/icon.component.ts b/frontend/src/app/shared/components/icon/icon.component.ts index 0f41a7ec501..0d5befe8115 100644 --- a/frontend/src/app/shared/components/icon/icon.component.ts +++ b/frontend/src/app/shared/components/icon/icon.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, Input } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; @Component({ selector: 'op-icon', @@ -43,6 +43,10 @@ import { Component, Input } from '@angular/core'; } `, standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class OpIconComponent { @Input('icon-classes') iconClasses:string; diff --git a/frontend/src/app/shared/components/icon/op-icon.spec.ts b/frontend/src/app/shared/components/icon/op-icon.spec.ts index ae6cca5282d..77c55c49c7c 100644 --- a/frontend/src/app/shared/components/icon/op-icon.spec.ts +++ b/frontend/src/app/shared/components/icon/op-icon.spec.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { DebugElement } from '@angular/core'; import { OpIconComponent } from './icon.component'; @@ -36,14 +36,13 @@ describe('opIcon Directive', () => { let fixture:ComponentFixture; let element:DebugElement; - beforeEach(waitForAsync(() => { - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ declarations: [ OpIconComponent, ], }).compileComponents(); - })); + }); beforeEach(() => { fixture = TestBed.createComponent(OpIconComponent); diff --git a/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts index 9e724d4570f..145ae4b9cdf 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-child-pages-modal/child-pages-macro.modal.ts @@ -27,7 +27,7 @@ //++ import { - AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; @@ -37,6 +37,10 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ templateUrl: './child-pages-macro.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class ChildPagesMacroModalComponent extends OpModalComponent implements AfterViewInit { public changed = false; diff --git a/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts index 032779851c2..252982f4b81 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-code-block-modal/code-block-macro.modal.ts @@ -27,7 +27,7 @@ //++ import { - AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, } from '@angular/core'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; @@ -37,6 +37,10 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ templateUrl: './code-block-macro.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class CodeBlockMacroModalComponent extends OpModalComponent implements AfterViewInit { public changed = false; diff --git a/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts b/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts index 0d121a6c293..ebc108054ec 100644 --- a/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts +++ b/frontend/src/app/shared/components/modals/editor/macro-wiki-include-page-modal/wiki-include-page-macro.modal.ts @@ -27,7 +27,7 @@ //++ import { - AfterViewInit, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, + AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, } from '@angular/core'; import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; @@ -37,6 +37,10 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; @Component({ templateUrl: './wiki-include-page-macro.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WikiIncludePageMacroModalComponent extends OpModalComponent implements AfterViewInit { public changed = false; diff --git a/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts b/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts index 80a7dc743ed..4d995a893c5 100644 --- a/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts +++ b/frontend/src/app/shared/components/modals/save-modal/save-query.modal.ts @@ -27,7 +27,7 @@ //++ import { - ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild, } from '@angular/core'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; @@ -44,6 +44,10 @@ import { States } from 'core-app/core/states/states.service'; @Component({ templateUrl: './save-query.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class SaveQueryModalComponent extends OpModalComponent { public queryName = ''; @@ -94,7 +98,7 @@ export class SaveQueryModalComponent extends OpModalComponent { return document.getElementById('work-packages-settings-button')!; } - public saveQueryAs($event:Event):void { + public async saveQueryAs($event:Event):Promise { $event.preventDefault(); if (this.isBusy || !this.queryName) { @@ -102,20 +106,23 @@ export class SaveQueryModalComponent extends OpModalComponent { } this.isBusy = true; + this.cdRef.markForCheck(); const query = this.querySpace.query.value!; query.public = this.isPublic; - this.wpListService - .create(query, this.queryName) - .then((savedQuery:QueryResource):Promise => { - if (this.isStarred && !savedQuery.starred) { - return this.wpListService.toggleStarred(savedQuery).then(() => this.closeMe($event)); - } + try { + const savedQuery:QueryResource = await this.wpListService.create(query, this.queryName); - this.closeMe($event); - return Promise.resolve(true); - }) - .catch((error:any) => this.halNotification.handleRawError(error)) - .then(() => this.isBusy = false); // Same as .finally() + if (this.isStarred && !savedQuery.starred) { + await this.wpListService.toggleStarred(savedQuery); + } + + this.closeMe($event); + } catch (error:unknown) { + this.halNotification.handleRawError(error); + } finally { + this.isBusy = false; + this.cdRef.markForCheck(); + } } } diff --git a/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts b/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts index 91a1e8e2acc..85f4ebb2b8e 100644 --- a/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts +++ b/frontend/src/app/shared/components/modals/share-modal/query-sharing-form.component.ts @@ -1,7 +1,7 @@ import { States } from 'core-app/core/states/states.service'; import { AuthorisationService } from 'core-app/core/model-auth/model-auth.service'; import { - Component, EventEmitter, Input, Output, + ChangeDetectionStrategy, Component, EventEmitter, Input, Output, } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/query-space/isolated-query-space'; @@ -15,6 +15,10 @@ export interface QuerySharingChange { selector: 'query-sharing-form', templateUrl: './query-sharing-form.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class QuerySharingFormComponent { @Input() public isSave:boolean; diff --git a/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts b/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts index 865b40450f8..26f2ba6be32 100644 --- a/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts +++ b/frontend/src/app/shared/components/modals/share-modal/query-sharing.modal.ts @@ -35,7 +35,7 @@ import { OpModalComponent } from 'core-app/shared/components/modal/modal.compone import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; import { - ChangeDetectorRef, Component, ElementRef, Inject, OnInit, + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit, } from '@angular/core'; import { QuerySharingChange } from 'core-app/shared/components/modals/share-modal/query-sharing-form.component'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -44,6 +44,10 @@ import { IsolatedQuerySpace } from 'core-app/features/work-packages/directives/q @Component({ templateUrl: './query-sharing.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class QuerySharingModalComponent extends OpModalComponent implements OnInit { public query:QueryResource; @@ -103,6 +107,7 @@ export class QuerySharingModalComponent extends OpModalComponent implements OnIn } this.isBusy = true; + this.cdRef.markForCheck(); const promises = []; if (this.query.public !== this.isPublic) { @@ -120,10 +125,12 @@ export class QuerySharingModalComponent extends OpModalComponent implements OnIn .then(() => { this.closeMe($event); this.isBusy = false; + this.cdRef.markForCheck(); }) .catch(() => { this.toastService.addError(this.I18n.t('js.error.query_saving')); this.isBusy = false; + this.cdRef.markForCheck(); }); } } diff --git a/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts b/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts index 9fa6c2851dd..5ca35da8f1b 100644 --- a/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts +++ b/frontend/src/app/shared/components/modals/view-settings-modal/view-settings.modal.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Inject, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, ViewChild } from '@angular/core'; import { HalResourceNotificationService } from 'core-app/features/hal/services/hal-resource-notification.service'; import { QueryResource } from 'core-app/features/hal/resources/query-resource'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; @@ -42,6 +42,10 @@ import { WorkPackagesQueryViewService } from 'core-app/features/work-packages/co @Component({ templateUrl: './view-settings.modal.html', + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class ViewSettingsModalComponent extends OpModalComponent { public queryName = ''; @@ -93,7 +97,7 @@ export class ViewSettingsModalComponent extends OpModalComponent { return document.getElementById('work-packages-settings-button')!; } - public saveQueryAs($event:Event):void { + public async saveQueryAs($event:Event):Promise { $event.preventDefault(); if (this.isBusy || !this.queryName) { @@ -101,20 +105,23 @@ export class ViewSettingsModalComponent extends OpModalComponent { } this.isBusy = true; + this.cdRef.markForCheck(); const query = this.querySpace.query.value!; query.public = this.isPublic; - this.wpListService - .create(query, this.queryName) - .then((savedQuery:QueryResource):Promise => { - if (this.isStarred && !savedQuery.starred) { - return this.wpListService.toggleStarred(savedQuery).then(() => this.closeMe($event)); - } + try { + const savedQuery:QueryResource = await this.wpListService.create(query, this.queryName); - this.closeMe($event); - return Promise.resolve(true); - }) - .catch((error:any) => this.halNotification.handleRawError(error)) - .then(() => this.isBusy = false); // Same as .finally() + if (this.isStarred && !savedQuery.starred) { + await this.wpListService.toggleStarred(savedQuery); + } + + this.closeMe($event); + } catch (error:unknown) { + this.halNotification.handleRawError(error); + } finally { + this.isBusy = false; + this.cdRef.markForCheck(); + } } } diff --git a/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.ts b/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.ts index 1b83db1e8e7..fd272e81cb5 100644 --- a/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.ts +++ b/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.ts @@ -29,6 +29,7 @@ import { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; import { States } from 'core-app/core/states/states.service'; import { + ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, @@ -51,6 +52,10 @@ import { BackRoutingService } from 'core-app/features/work-packages/components/b @Component({ templateUrl: './wp-destroy.modal.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpDestroyModalComponent extends OpModalComponent implements OnInit { // When deleting multiple @@ -152,12 +157,14 @@ export class WpDestroyModalComponent extends OpModalComponent implements OnInit } this.busy = true; + this.cdRef.markForCheck(); const ids = this.workPackages .map((el) => el.id) .filter((id) => id !== null); this.workPackageService.performBulkDelete(ids, true) .then(() => { this.busy = false; + this.cdRef.markForCheck(); this.closeMe($event); this.wpTableFocus.clear('Clearing after destroying work packages'); if (this.$state.current.data?.baseRoute) { @@ -169,6 +176,7 @@ export class WpDestroyModalComponent extends OpModalComponent implements OnInit }) .catch(() => { this.busy = false; + this.cdRef.markForCheck(); }); return false; diff --git a/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts b/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts index 726d030a091..e44f1a57d63 100644 --- a/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts +++ b/frontend/src/app/shared/components/op-context-menu/icon-triggered-context-menu/icon-triggered-context-menu.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ChangeDetectorRef, Component, ElementRef, Injector, Input } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Injector, Input } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { OpContextMenuTrigger, @@ -40,6 +40,10 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op templateUrl: './icon-triggered-context-menu.component.html', styleUrls: ['./icon-triggered-context-menu.component.sass'], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class IconTriggeredContextMenuComponent extends OpContextMenuTrigger { override readonly placement = 'bottom-end'; @@ -58,8 +62,13 @@ export class IconTriggeredContextMenuComponent extends OpContextMenuTrigger { @Input() menuItemsFactory:() => Promise; @Input() customAriaLabel:string = this.I18n.t('js.label_open_menu'); - protected async open(evt:Event) { + protected open(evt:Event):void { + void this.openContextMenu(evt); + } + + private async openContextMenu(evt:Event):Promise { this.items = await this.buildItems(); + this.cdRef.markForCheck(); this.opContextMenu.show(this, evt); } diff --git a/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts b/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts index 7e0144c5054..9da13c47753 100644 --- a/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts +++ b/frontend/src/app/shared/components/op-context-menu/op-context-menu.component.ts @@ -1,4 +1,4 @@ -import { Component, Inject } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Inject } from '@angular/core'; import { OpContextMenuItem, OpContextMenuLocalsMap, @@ -9,6 +9,10 @@ import { OPContextMenuService } from 'core-app/shared/components/op-context-menu @Component({ templateUrl: './op-context-menu.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class OPContextMenuComponent { public items:OpContextMenuItem[]; diff --git a/frontend/src/app/shared/components/option-list/option-list.component.ts b/frontend/src/app/shared/components/option-list/option-list.component.ts index a3a6a60df45..c0ec3addba0 100644 --- a/frontend/src/app/shared/components/option-list/option-list.component.ts +++ b/frontend/src/app/shared/components/option-list/option-list.component.ts @@ -1,4 +1,6 @@ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, EventEmitter, forwardRef, @@ -27,10 +29,16 @@ export type IOpOptionListValue = T|null; multi: true, }], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class OpOptionListComponent implements ControlValueAccessor { @HostBinding('class.op-option-list') className = true; + constructor(private cdRef:ChangeDetectorRef) {} + @Input() options:IOpOptionListOption[] = []; @Input() name = `op-option-list-${+(new Date())}`; @@ -62,6 +70,7 @@ export class OpOptionListComponent implements ControlValueAccessor { writeValue(value:IOpOptionListValue) { this._selected = value; + this.cdRef.markForCheck(); } registerOnChange(fn:any) { diff --git a/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts b/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts index 3ac45db3083..7b3d3497f2d 100644 --- a/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts +++ b/frontend/src/app/shared/components/persistent-toggle/persistent-toggle.component.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { Component, ElementRef, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ElementRef, OnInit } from '@angular/core'; import { slideDown, slideUp } from 'es6-slide-up-down'; @@ -34,6 +34,10 @@ import { slideDown, slideUp } from 'es6-slide-up-down'; selector: 'opce-persistent-toggle', template: '', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class PersistentToggleComponent implements OnInit { /** Unique identifier of the toggle */ diff --git a/frontend/src/app/shared/components/primer/icon-button.component.spec.ts b/frontend/src/app/shared/components/primer/icon-button.component.spec.ts index 59bd378835b..105526cb09c 100644 --- a/frontend/src/app/shared/components/primer/icon-button.component.spec.ts +++ b/frontend/src/app/shared/components/primer/icon-button.component.spec.ts @@ -28,16 +28,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { PrimerIconButtonComponent } from './icon-button.component'; describe('PrimerIconButtonComponent', () => { let component:PrimerIconButtonComponent; let fixture:ComponentFixture; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({imports: [PrimerIconButtonComponent]}); - })); + beforeEach(async () => { + await TestBed.configureTestingModule({imports: [PrimerIconButtonComponent]}).compileComponents(); + }); beforeEach(() => { fixture = TestBed.createComponent(PrimerIconButtonComponent); diff --git a/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts new file mode 100644 index 00000000000..a7c056ea4fd --- /dev/null +++ b/frontend/src/app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component.spec.ts @@ -0,0 +1,146 @@ +//-- 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. +//++ + +import { ChangeDetectorRef, ElementRef } from '@angular/core'; +import { Observable, config, throwError } from 'rxjs'; +import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; +import { SortFilesPipe } from 'core-app/shared/components/storages/pipes/sort-files.pipe'; +import { StorageFilesResourceService } from 'core-app/core/state/storage-files/storage-files.service'; +import { IStorageFile } from 'core-app/core/state/storage-files/storage-file.model'; +import { + FilePickerBaseModalComponent, +} from 'core-app/shared/components/storages/file-picker-base-modal/file-picker-base-modal.component'; +import { StorageFileListItem } from 'core-app/shared/components/storages/storage-file-list-item/storage-file-list-item'; + +class TestFilePickerBaseModalComponent extends FilePickerBaseModalComponent { + constructor( + locals:OpModalLocalsMap, + elementRef:ElementRef, + cdRef:ChangeDetectorRef, + sortFilesPipe:SortFilesPipe, + storageFilesResourceService:StorageFilesResourceService, + ) { + super(locals, elementRef, cdRef, sortFilesPipe, storageFilesResourceService); + } + + public loadDirectory(directory:IStorageFile):void { + this.changeLevel(directory); + } + + protected storageFileToListItem(_file:IStorageFile, _index:number):StorageFileListItem { + return {} as StorageFileListItem; + } +} + +describe('FilePickerBaseModalComponent', () => { + interface Spies { + detectChanges:jasmine.Spy; + close:jasmine.Spy; + files:jasmine.Spy; + reset:jasmine.Spy; + } + + function buildComponent(spies:Spies) { + const cdRef = { detectChanges: spies.detectChanges } as unknown as ChangeDetectorRef; + const elementRef = { nativeElement: document.createElement('div') } as ElementRef; + const locals = { + service: { close: spies.close }, + storage: { + name: 'Storage', + _links: { + type: { href: 'urn:openproject:test-storage' }, + self: { href: '/api/v3/storages/1' }, + }, + }, + projectFolderMode: 'inactive', + } as unknown as OpModalLocalsMap; + const sortFilesPipe = { transform: (files:IStorageFile[]) => files } as SortFilesPipe; + const storageFilesResourceService = { + files: spies.files, + reset: spies.reset, + } as unknown as StorageFilesResourceService; + const component = new TestFilePickerBaseModalComponent( + locals, + elementRef, + cdRef, + sortFilesPipe, + storageFilesResourceService, + ); + + component.ngOnInit(); + + return { component, cdRef, storageFilesResourceService }; + } + + it('cancels pending directory loading on destroy', () => { + const teardown = jasmine.createSpy('teardown'); + const files$ = new Observable(() => teardown); + const directory = { location: '/folder', mimeType: 'application/x-op-directory' } as IStorageFile; + const files = jasmine.createSpy('files').and.returnValue(files$); + const { component } = buildComponent({ + detectChanges: jasmine.createSpy('detectChanges'), + close: jasmine.createSpy('close'), + files, + reset: jasmine.createSpy('reset'), + }); + + component.loadDirectory(directory); + + expect(files).toHaveBeenCalledTimes(2); + expect(teardown).not.toHaveBeenCalled(); + + component.ngOnDestroy(); + + expect(teardown).toHaveBeenCalledTimes(1); + }); + + it('does not report directory loading errors as unhandled async exceptions', async () => { + const previousUnhandledError = config.onUnhandledError; + const onUnhandledError = jasmine.createSpy('onUnhandledError'); + const files$ = throwError(() => new Error('boom')); + const detectChanges = jasmine.createSpy('detectChanges'); + const directory = { location: '/folder', mimeType: 'application/x-op-directory' } as IStorageFile; + const { component } = buildComponent({ + detectChanges, + close: jasmine.createSpy('close'), + files: jasmine.createSpy('files').and.returnValue(files$), + reset: jasmine.createSpy('reset'), + }); + + config.onUnhandledError = onUnhandledError; + + component.loadDirectory(directory); + await new Promise((resolve) => window.setTimeout(resolve)); + + expect(component.loading$.getValue()).toBe('error'); + expect(detectChanges).toHaveBeenCalledWith(); + expect(onUnhandledError).not.toHaveBeenCalled(); + + config.onUnhandledError = previousUnhandledError; + }); +}); 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 6bec3bbad27..81a47109c5d 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 @@ -120,10 +120,11 @@ export abstract class FilePickerBaseModalComponent extends OpModalComponent impl this.breadcrumbs = this.makeBreadcrumbs(storageFiles.ancestors, storageFiles.parent); this.storageFiles$.next(storageFiles.files); this.loading$.next('success'); + this.cdRef.detectChanges(); }, - error: (error) => { + error: () => { this.loading$.next('error'); - throw error; + this.cdRef.detectChanges(); }, }); } @@ -131,6 +132,7 @@ export abstract class FilePickerBaseModalComponent extends OpModalComponent impl ngOnDestroy():void { super.ngOnDestroy(); + this.cancelCurrentLoading(); this.storageFilesResourceService.reset(); } @@ -151,6 +153,7 @@ export abstract class FilePickerBaseModalComponent extends OpModalComponent impl protected changeLevel(ancestor:IStorageFile):void { this.cancelCurrentLoading(); this.loading$.next('loading'); + this.cdRef.detectChanges(); this.loadingSubscription = this.storageFilesResourceService .files(makeFilesCollectionLink(this.storage._links.self, ancestor.location)) @@ -160,10 +163,11 @@ export abstract class FilePickerBaseModalComponent extends OpModalComponent impl this.breadcrumbs = this.makeBreadcrumbs(storageFiles.ancestors, storageFiles.parent); this.storageFiles$.next(storageFiles.files); this.loading$.next('success'); + this.cdRef.detectChanges(); }, - error: (error) => { + error: () => { this.loading$.next('error'); - throw error; + this.cdRef.detectChanges(); }, }); } diff --git a/frontend/src/app/shared/components/toaster/toast.service.spec.ts b/frontend/src/app/shared/components/toaster/toast.service.spec.ts index af521143451..850555ce368 100644 --- a/frontend/src/app/shared/components/toaster/toast.service.spec.ts +++ b/frontend/src/app/shared/components/toaster/toast.service.spec.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { TestBed, waitForAsync } from '@angular/core/testing'; +import { TestBed } from '@angular/core/testing'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { provideHttpClientTesting } from '@angular/common/http/testing'; import { ConfigurationService } from 'core-app/core/config/configuration.service'; @@ -38,9 +38,8 @@ import { HttpEvent, provideHttpClient, withInterceptorsFromDi } from '@angular/c describe('ToastService', () => { let toastService:ToastService; - beforeEach(waitForAsync(() => { - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ imports: [OpenprojectHalModule], providers: [ { provide: ConfigurationService, useValue: { autoHidePopups: () => true } }, @@ -49,12 +48,9 @@ describe('ToastService', () => { provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting(), ] -}) - .compileComponents() - .then(() => { - toastService = TestBed.inject(ToastService); - }); - })); +}).compileComponents(); + toastService = TestBed.inject(ToastService); + }); it('should be able to create warnings', () => { const toaster = toastService.addWarning('warning!'); diff --git a/frontend/src/app/shared/components/user-link/user-link.component.spec.ts b/frontend/src/app/shared/components/user-link/user-link.component.spec.ts index cbe77a16317..1022a7bf347 100644 --- a/frontend/src/app/shared/components/user-link/user-link.component.spec.ts +++ b/frontend/src/app/shared/components/user-link/user-link.component.spec.ts @@ -26,7 +26,7 @@ // See COPYRIGHT and LICENSE files for more details. //++ -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { UserResource } from 'core-app/features/hal/resources/user-resource'; @@ -46,9 +46,8 @@ describe('UserLinkComponent component test', () => { let element:HTMLElement; let user:UserResource; - beforeEach(waitForAsync(() => { - // noinspection JSIgnoredPromiseFromCall - TestBed.configureTestingModule({ + beforeEach(async () => { + await TestBed.configureTestingModule({ declarations: [ UserLinkComponent, ], @@ -61,11 +60,11 @@ describe('UserLinkComponent component test', () => { fixture = TestBed.createComponent(UserLinkComponent); app = fixture.debugElement.componentInstance; element = fixture.elementRef.nativeElement; - })); + }); describe('inner element', () => { - describe('with the uer having the showUserPath attribute', () => { - beforeEach(waitForAsync(() => { + describe('with the user having the showUserPath attribute', () => { + beforeEach(() => { user = { name: 'First Last', showUserPath: '/users/1', @@ -73,7 +72,7 @@ describe('UserLinkComponent component test', () => { fixture.componentRef.setInput('user', user); fixture.detectChanges(); - })); + }); it('should render an inner link with specified classes', () => { const link = element.querySelector('a')!; @@ -85,7 +84,7 @@ describe('UserLinkComponent component test', () => { }); describe('with the user not having the showUserPath attribute', () => { - beforeEach(waitForAsync(() => { + beforeEach(() => { user = { name: 'First Last', showUserPath: null, @@ -93,7 +92,7 @@ describe('UserLinkComponent component test', () => { fixture.componentRef.setInput('user', user); fixture.detectChanges(); - })); + }); it('renders only the name', () => { const link = element.querySelector('a'); diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts index 157e1cde494..14e2c569689 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab-inner.component.ts @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageViewFiltersService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-filters.service'; @@ -12,6 +12,10 @@ import { WorkPackageFiltersService } from 'core-app/features/work-packages/compo selector: 'op-filters-tab-inner', templateUrl: './filters-tab-inner.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpGraphConfigurationFiltersTabInnerComponent extends QuerySpacedTabComponent implements TabComponent, OnInit { public filters:QueryFilterInstanceResource[] = []; @@ -24,17 +28,19 @@ export class WpGraphConfigurationFiltersTabInnerComponent extends QuerySpacedTab readonly wpTableFilters:WorkPackageViewFiltersService, readonly wpFiltersService:WorkPackageFiltersService, readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly wpGraphConfiguration:WpGraphConfigurationService) { + readonly wpGraphConfiguration:WpGraphConfigurationService, + private cdRef:ChangeDetectorRef) { super(I18n, wpStatesInitialization, wpGraphConfiguration); } ngOnInit() { - this.initializeQuerySpace() + void this.initializeQuerySpace() .then(() => { - this.wpTableFilters + void this.wpTableFilters .onReady() .then(() => { this.filters = this.wpTableFilters.current; + this.cdRef.markForCheck(); }); }); } diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab.component.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab.component.ts index 7a4ac06522c..255a19d2de7 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/filters-tab.component.ts @@ -1,4 +1,4 @@ -import { Component, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; import { WorkPackageIsolatedQuerySpaceDirective, @@ -9,6 +9,10 @@ import { templateUrl: './filters-tab.component.html', hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], // TODO replace standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpGraphConfigurationFiltersTabComponent implements TabComponent { @ViewChild('tabInner', { static: true }) diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts index 994e786162c..baffe7b4c01 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab-inner.component.ts @@ -1,6 +1,6 @@ import { I18nService } from 'core-app/core/i18n/i18n.service'; import { WorkPackageViewGroupByService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-group-by.service'; -import { Component, OnInit } from '@angular/core'; +import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core'; import { WpGraphConfigurationService } from 'core-app/shared/components/work-package-graphs/configuration/wp-graph-configuration.service'; import { WorkPackageStatesInitializationService } from 'core-app/features/work-packages/components/wp-list/wp-states-initialization.service'; import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; @@ -17,6 +17,10 @@ interface OpChartType { selector: 'op-settings-tab-inner', templateUrl: './settings-tab-inner.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpGraphConfigurationSettingsTabInnerComponent extends QuerySpacedTabComponent implements TabComponent, OnInit { // Grouping @@ -34,7 +38,8 @@ export class WpGraphConfigurationSettingsTabInnerComponent extends QuerySpacedTa constructor(readonly I18n:I18nService, readonly wpTableGroupBy:WorkPackageViewGroupByService, readonly wpStatesInitialization:WorkPackageStatesInitializationService, - readonly wpGraphConfiguration:WpGraphConfigurationService) { + readonly wpGraphConfiguration:WpGraphConfigurationService, + private cdRef:ChangeDetectorRef) { super(I18n, wpStatesInitialization, wpGraphConfiguration); } @@ -54,14 +59,15 @@ export class WpGraphConfigurationSettingsTabInnerComponent extends QuerySpacedTa } ngOnInit() { - this + void this .initializeQuerySpace() .then(() => { - this.wpTableGroupBy + void this.wpTableGroupBy .onReady() .then(() => { this.initializeAvailableGroups(); this.initializeAvailableChartType(); + this.cdRef.markForCheck(); }); }); } diff --git a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab.component.ts b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab.component.ts index ed642e8d336..fa2ec91fc69 100644 --- a/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/configuration-modal/tabs/settings-tab.component.ts @@ -1,5 +1,5 @@ import { TabComponent } from 'core-app/features/work-packages/components/wp-table/configuration-modal/tab-portal-outlet'; -import { Component, ViewChild } from '@angular/core'; +import { ChangeDetectionStrategy, Component, ViewChild } from '@angular/core'; import { WorkPackageIsolatedQuerySpaceDirective, } from 'core-app/features/work-packages/directives/query-space/wp-isolated-query-space.directive'; @@ -9,6 +9,10 @@ import { templateUrl: './settings-tab.component.html', hostDirectives: [WorkPackageIsolatedQuerySpaceDirective], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WpGraphConfigurationSettingsTabComponent implements TabComponent { @ViewChild('tabInner', { static: true }) diff --git a/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts b/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts index 48c43a5d351..1361dd9e7fe 100644 --- a/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts +++ b/frontend/src/app/shared/components/work-package-graphs/embedded/wp-embedded-graph.component.ts @@ -1,4 +1,4 @@ -import { Component, Input, SimpleChanges, OnChanges } from '@angular/core'; +import { ChangeDetectionStrategy, Component, Input, SimpleChanges, OnChanges } from '@angular/core'; import { WorkPackageTableConfiguration } from 'core-app/features/work-packages/components/wp-table/wp-table-configuration'; import { ChartOptions } from 'chart.js'; import { I18nService } from 'core-app/core/i18n/i18n.service'; @@ -28,7 +28,11 @@ interface ChartDataSet { ], providers: [ provideCharts(withDefaultRegisterables(ChartDataLabels, PrimerColorsPlugin)), - ] + ], + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class WorkPackageEmbeddedGraphComponent implements OnChanges { @Input() public datasets:WorkPackageEmbeddedGraphDataset[]; diff --git a/frontend/src/app/spot/components/form-field/form-field.component.ts b/frontend/src/app/spot/components/form-field/form-field.component.ts index 2838db2180b..03bbd08d373 100644 --- a/frontend/src/app/spot/components/form-field/form-field.component.ts +++ b/frontend/src/app/spot/components/form-field/form-field.component.ts @@ -1,14 +1,17 @@ import { - Component, ContentChild, HostBinding, Input, Optional, + ChangeDetectionStrategy, Component, ContentChild, HostBinding, Input, Optional, } from '@angular/core'; import { AbstractControl, FormGroupDirective, NgControl } from '@angular/forms'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -/* eslint-disable-next-line change-detection-strategy/on-push */ @Component({ selector: 'spot-form-field', templateUrl: './form-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class SpotFormFieldComponent { @HostBinding('class.spot-form-field') className = true; diff --git a/frontend/src/app/spot/components/selector-field/selector-field.component.ts b/frontend/src/app/spot/components/selector-field/selector-field.component.ts index e0ae36b12e3..4c5346ebd2b 100644 --- a/frontend/src/app/spot/components/selector-field/selector-field.component.ts +++ b/frontend/src/app/spot/components/selector-field/selector-field.component.ts @@ -1,4 +1,5 @@ import { + ChangeDetectionStrategy, Component, ContentChild, HostBinding, @@ -15,6 +16,10 @@ import { selector: 'spot-selector-field', templateUrl: './selector-field.component.html', standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class SpotSelectorFieldComponent { @HostBinding('class.spot-form-field') className = true; diff --git a/frontend/src/app/spot/components/text-field/text-field.component.ts b/frontend/src/app/spot/components/text-field/text-field.component.ts index 42f81f70f04..1b28eb903f2 100644 --- a/frontend/src/app/spot/components/text-field/text-field.component.ts +++ b/frontend/src/app/spot/components/text-field/text-field.component.ts @@ -1,14 +1,15 @@ import { + ChangeDetectionStrategy, + ChangeDetectorRef, Component, ElementRef, - ViewChild, - forwardRef, + EventEmitter, HostBinding, HostListener, Input, Output, - EventEmitter, - ChangeDetectorRef, + ViewChild, + forwardRef, } from '@angular/core'; import { ControlValueAccessor, @@ -24,6 +25,10 @@ import { multi: true, }], standalone: false, + // TODO: This component has been partially migrated to be zoneless-compatible. + // After testing, this should be updated to ChangeDetectionStrategy.OnPush. + // eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection + changeDetection: ChangeDetectionStrategy.Default, }) export class SpotTextFieldComponent implements ControlValueAccessor { @HostBinding('class.spot-text-field') public className = true; @@ -100,11 +105,13 @@ export class SpotTextFieldComponent implements ControlValueAccessor { onInputFocus(event:FocusEvent):void { this.focused = true; + this.cdRef.markForCheck(); this.inputFocus.next(event); } onInputBlur(event:FocusEvent):void { this.focused = false; + this.cdRef.markForCheck(); this.inputBlur.next(event); } diff --git a/frontend/src/assets/sass/backlogs/_master_backlog.sass b/frontend/src/assets/sass/backlogs/_master_backlog.sass index 62b6a0a15b3..6df37ec24e3 100644 --- a/frontend/src/assets/sass/backlogs/_master_backlog.sass +++ b/frontend/src/assets/sass/backlogs/_master_backlog.sass @@ -32,21 +32,38 @@ $op-backlogs-header--points-min-width-narrow: 2rem .action-sprint_planning @include extended-content--bottom -.op-backlogs-header +.op-backlogs-header, +.op-sprint-header display: grid grid-template-columns: 1fr minmax($op-backlogs-header--points-min-width, max-content) auto - grid-template-areas: "collapsible points menu" align-items: center -.op-backlogs-header--collapsible - margin-left: calc(var(--stack-padding-normal) / 2) + &--collapsible + margin-left: calc(var(--stack-padding-normal) / 2) -.op-backlogs-header--points - margin-left: var(--stack-gap-normal) - font-variant-numeric: tabular-nums + &--actions + margin-left: var(--stack-gap-normal) + + &--menu + margin-left: var(--stack-gap-normal) + +.op-backlogs-header + grid-template-areas: "collapsible points menu" + + &--points + margin-left: var(--stack-gap-normal) + font-variant-numeric: tabular-nums + +.op-sprint-header + grid-template-areas: "collapsible actions menu" + + &--actions, + &--menu + margin-left: var(--stack-gap-normal) + align-self: flex-start + // Unfortunately, the invisible button style bites us here again. + margin-top: -6px -.op-backlogs-header--menu - margin-left: var(--stack-gap-normal) .op-backlogs-story display: grid diff --git a/frontend/src/elements/block-note-element.ts b/frontend/src/elements/block-note-element.ts index 8ee18f9937a..e9ceb8c65b9 100644 --- a/frontend/src/elements/block-note-element.ts +++ b/frontend/src/elements/block-note-element.ts @@ -30,8 +30,6 @@ import { User } from '@blocknote/core/comments'; import { HocuspocusProvider } from '@hocuspocus/provider'; -import { Application } from '@hotwired/stimulus'; -import FlashController from 'core-stimulus/controllers/flash.controller'; import { LiveCollaborationManager } from 'core-stimulus/helpers/live-collaboration-helpers'; import { ShadowDomWrapper } from 'op-blocknote-extensions'; import React from 'react'; @@ -40,42 +38,32 @@ import { createRoot } from 'react-dom/client'; import OpBlockNoteContainer from '../react/OpBlockNoteContainer'; class BlockNoteElement extends HTMLElement { - private stimulusRoot:HTMLDivElement; + private editorRoot:HTMLDivElement; private editorMount:HTMLDivElement; - private errorContainer:HTMLDivElement; private reactRoot:Root|null = null; - private stimulusApp:Application|null = null; - private renderCallback:((provider?:HocuspocusProvider) => void) | null = null; + private renderCallback:((provider:HocuspocusProvider) => void) | null = null; constructor() { super(); const shadowRoot = this.attachShadow({ mode: 'open' }); - // Wrapper div as Stimulus root so both errorContainer and editorMount are in scope - this.stimulusRoot = document.createElement('div'); + this.editorRoot = document.createElement('div'); const browserSpecificClasses = this.getAttribute('browser-specific-classes')?.split(' ') ?? []; if (browserSpecificClasses.length > 0) { - this.stimulusRoot.classList.add(...browserSpecificClasses); + this.editorRoot.classList.add(...browserSpecificClasses); } // Clone the blank-target link description into the shadow DOM // so aria-describedby references resolve for links inside the editor const blankLinkDesc = document.getElementById('open-blank-target-link-description'); if (blankLinkDesc) { - this.stimulusRoot.appendChild(blankLinkDesc.cloneNode(true)); + this.editorRoot.appendChild(blankLinkDesc.cloneNode(true)); } - // Container for connection error/recovery messages (rendered by React via fetchConnectionTemplate) - this.errorContainer = document.createElement('div'); - this.errorContainer.id = 'documents-show-edit-view-connection-error-notice-component'; - this.errorContainer.dataset.controller = 'flash'; - this.errorContainer.dataset.flashAutohideValue = 'true'; - this.editorMount = document.createElement('div'); - this.stimulusRoot.appendChild(this.errorContainer); - this.stimulusRoot.appendChild(this.editorMount); - shadowRoot.appendChild(this.stimulusRoot); + this.editorRoot.appendChild(this.editorMount); + shadowRoot.appendChild(this.editorRoot); const blockNoteStylesheetUrl = this.getAttribute('blocknote-stylesheet-url'); if (blockNoteStylesheetUrl) { @@ -95,26 +83,18 @@ class BlockNoteElement extends HTMLElement { } connectedCallback() { - // Initialize Stimulus application within shadow DOM - this.stimulusApp = Application.start(this.stimulusRoot); - this.stimulusApp.register('flash', FlashController); + const collaborationEnabled = this.getAttribute('collaboration-enabled') === 'true'; + if (!collaborationEnabled) return; - // Initialize React application within shadow DOM this.reactRoot = createRoot(this.editorMount); - const collaborationEnabled = this.getAttribute('collaboration-enabled') === 'true'; - - this.renderCallback = (provider?:HocuspocusProvider) => { + this.renderCallback = (provider:HocuspocusProvider) => { this.reactRoot?.render( React.createElement(React.StrictMode, null, this.BlockNoteReactContainer(provider)) ); }; - if (collaborationEnabled) { - LiveCollaborationManager.onReady(this.renderCallback); - } else { - this.renderCallback(); - } + LiveCollaborationManager.onReady(this.renderCallback); } disconnectedCallback() { @@ -128,28 +108,21 @@ class BlockNoteElement extends HTMLElement { this.reactRoot.unmount(); this.reactRoot = null; } - - if (this.stimulusApp) { - this.stimulusApp.stop(); - this.stimulusApp = null; - } } - private BlockNoteReactContainer = (hocuspocusProvider?:HocuspocusProvider) => { + private BlockNoteReactContainer = (hocuspocusProvider:HocuspocusProvider) => { return React.createElement( ShadowDomWrapper, { target: this.editorMount }, React.createElement( OpBlockNoteContainer, { - inputField: document.createElement('input'), activeUser: this.parseActiveUser()!, readOnly: this.getAttribute('read-only') === 'true', openProjectUrl: this.getAttribute('open-project-url') ?? '', attachmentsUploadUrl: this.getAttribute('attachments-upload-url') ?? '', attachmentsCollectionKey: this.getAttribute('attachments-collection-key') ?? '', - hocuspocusProvider: hocuspocusProvider, - errorContainer: this.errorContainer, + hocuspocusProvider, } ) ); @@ -167,7 +140,6 @@ class BlockNoteElement extends HTMLElement { } return null; } - } if (!customElements.get('op-block-note')) { diff --git a/frontend/src/global_styles/content/_hover_cards.sass b/frontend/src/global_styles/content/_hover_cards.sass index f381e94fd02..6487295d793 100644 --- a/frontend/src/global_styles/content/_hover_cards.sass +++ b/frontend/src/global_styles/content/_hover_cards.sass @@ -52,6 +52,3 @@ &:popover-open opacity: 1 - - &--hidden-container - display: none diff --git a/frontend/src/global_styles/openproject.sass b/frontend/src/global_styles/openproject.sass index 826e78810c4..2e2eeea80ab 100644 --- a/frontend/src/global_styles/openproject.sass +++ b/frontend/src/global_styles/openproject.sass @@ -28,6 +28,7 @@ @import "../../../modules/meeting/app/components/_index.sass" @import "../../../modules/overviews/app/components/_index.sass" @import "../../../modules/storages/app/components/_index.sass" +@import "../../../modules/wikis/app/components/_index.sass" // Component specific Styles @import "../../../app/components/_index.sass" diff --git a/frontend/src/global_styles/primer/_overrides.sass b/frontend/src/global_styles/primer/_overrides.sass index 0446465a5cd..b7d2c98e92d 100644 --- a/frontend/src/global_styles/primer/_overrides.sass +++ b/frontend/src/global_styles/primer/_overrides.sass @@ -75,6 +75,9 @@ ul.SegmentedControl, &-body_autocomplete_height min-height: 320px + &-body_autocomplete_height--lg + min-height: 480px + &--size-xlarge-maximized-empty-footer min-height: 534px diff --git a/frontend/src/main.ts b/frontend/src/main.ts index d15f24fb937..30459af0604 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -1,5 +1,5 @@ import { OpenProjectModule } from 'core-app/app.module'; -import { enableProdMode, provideZoneChangeDetection } from '@angular/core'; +import { enableProdMode, provideZonelessChangeDetection } from '@angular/core'; import 'core-app/core/setup/init-jquery'; import 'core-app/core/setup/init-js-patches'; @@ -46,5 +46,5 @@ void initializeLocale() initializeGlobalListeners(); // Due to the behaviour of the Edge browser we need to wait for 'DOM ready' - void platformBrowser().bootstrapModule(OpenProjectModule, { applicationProviders: [provideZoneChangeDetection()], }); + void platformBrowser().bootstrapModule(OpenProjectModule, { applicationProviders: [provideZonelessChangeDetection()], }); }); diff --git a/frontend/src/polyfills.ts b/frontend/src/polyfills.ts index 71c224c339b..8bfe64db969 100644 --- a/frontend/src/polyfills.ts +++ b/frontend/src/polyfills.ts @@ -1,54 +1,3 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/** ************************************************************************************************* -* BROWSER POLYFILLS -*/ - // Ensure global is set for ng2-dragula and others -// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access no-explicit-any +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access (window as any).global = window; - -/* - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - */ -// (window as any).__Zone_enable_cross_context_check = true; - -/** ************************************************************************************************* - * Zone JS is required by default for Angular itself. - */ -import 'zone.js'; - -/** IE10 and IE11 requires the following for the Reflect API. */ -// import 'core-js/es6/reflect'; - -/** Evergreen browsers require these. * */ -// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove. -// import 'core-js/es7/reflect'; - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - */ - -// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame -// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick -(window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove', 'mouseover', 'mouseout', 'mousewheel']; // Included with Angular CLI. - -/** ************************************************************************************************* - * APPLICATION IMPORTS - */ diff --git a/frontend/src/react/OpBlockNoteContainer.tsx b/frontend/src/react/OpBlockNoteContainer.tsx index 3da7c64d167..c4b42cf5931 100644 --- a/frontend/src/react/OpBlockNoteContainer.tsx +++ b/frontend/src/react/OpBlockNoteContainer.tsx @@ -34,69 +34,46 @@ import { useEffect, useRef } from 'react'; import * as Y from 'yjs'; import { DocumentLoadingSkeleton } from './components/DocumentLoadingSkeleton'; import { OpBlockNoteEditor } from './components/OpBlockNoteEditor'; -import { fetchConnectionTemplate } from './helpers/connection-template-fetcher'; import { useCollaboration } from './hooks/useCollaboration'; export interface OpBlockNoteContainerProps { - inputField:HTMLInputElement; - inputText?:string; activeUser:User; readOnly:boolean; openProjectUrl:string; attachmentsUploadUrl:string; attachmentsCollectionKey:string; - hocuspocusProvider?:HocuspocusProvider; - errorContainer?:HTMLElement; + hocuspocusProvider:HocuspocusProvider; } -export default function OpBlockNoteContainer({ inputField, - inputText, - activeUser, - readOnly, - openProjectUrl, - attachmentsUploadUrl, - attachmentsCollectionKey, - hocuspocusProvider, - errorContainer }:OpBlockNoteContainerProps) { - const doc:Y.Doc = hocuspocusProvider - ? hocuspocusProvider.document - : (() => { - // NOTE: This should only be used in TEST environments where there is no provider. - const newDoc = new Y.Doc(); - if (inputText) { - try { - const update = Uint8Array.from(atob(inputText), c => c.charCodeAt(0)); - Y.applyUpdate(newDoc, update); - } catch (e) { - console.error('Failed to load document binary', e); - return new Y.Doc(); - } - } - return newDoc; - })(); - - const { isLoading, connectionError } = useCollaboration(hocuspocusProvider, doc, inputField); +export default function OpBlockNoteContainer({ + activeUser, + readOnly, + openProjectUrl, + attachmentsUploadUrl, + attachmentsCollectionKey, + hocuspocusProvider, +}:OpBlockNoteContainerProps) { + const doc:Y.Doc = hocuspocusProvider.document; + const { isLoading, offlineMode } = useCollaboration(hocuspocusProvider); const hadErrorRef = useRef(false); - // Fetch error/recovery template based on connection state useEffect(() => { - if (!errorContainer) return; - - if (connectionError) { + if (offlineMode) { hadErrorRef.current = true; - void fetchConnectionTemplate('error', errorContainer); + window.dispatchEvent(new CustomEvent('documents:connection-error')); } else if (hadErrorRef.current) { - // Only fetch recovery if we previously had an error (avoid fetching on initial render) - void fetchConnectionTemplate('recovery', errorContainer); + window.dispatchEvent(new CustomEvent('documents:connection-recovery')); } - }, [connectionError, errorContainer]); + }, [offlineMode]); if (isLoading) { return ; } - if (connectionError) { - // Error UI is rendered in errorContainer via fetchConnectionTemplate (outside React tree) + // Without IndexedDB offline persistence, all offline is blocking — hide the + // editor entirely to prevent a fresh empty Y.Doc from being synced as + // authoritative server state on reconnect. + if (offlineMode) { return null; } @@ -112,4 +89,3 @@ export default function OpBlockNoteContainer({ inputField, /> ); } - diff --git a/frontend/src/react/helpers/connection-template-fetcher.ts b/frontend/src/react/helpers/connection-template-fetcher.ts deleted file mode 100644 index 090bc6df265..00000000000 --- a/frontend/src/react/helpers/connection-template-fetcher.ts +++ /dev/null @@ -1,105 +0,0 @@ -/* - * -- 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. - * ++ - */ - -/** - * Fetches connection error/recovery templates from the server. - * Used by the BlockNote editor to display server-rendered error messages - * when the collaboration connection fails. - */ - -function getDocumentIdFromUrl():string|null { - const match = /\/documents\/(\d+)/.exec(window.location.pathname); - return match ? match[1] : null; -} - -/** - * Parses Turbo Stream response and extracts the template content. - * Standard Turbo.renderStreamMessage() uses document.getElementById() - * which can't find Shadow DOM elements, so we parse manually. - */ -function parseTurboStreamContent(html:string):string|null { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, 'text/html'); - const turboStream = doc.querySelector('turbo-stream'); - - if (!turboStream) { - console.error('No turbo-stream element found in response'); - return null; - } - - const template = turboStream.querySelector('template'); - if (!template) { - console.error('No template element found in turbo-stream'); - return null; - } - - return template.innerHTML; -} - -export async function fetchConnectionTemplate( - type:'error'|'recovery', - targetElement:HTMLElement, -):Promise { - const documentId = getDocumentIdFromUrl(); - if (!documentId) { - console.error('Could not extract document ID from URL'); - return; - } - - const url = `/documents/${documentId}/render_connection_${type}`; - - try { - const response = await fetch(url, { - method: 'GET', - headers: { - Accept: 'text/vnd.turbo-stream.html', - }, - }); - - if (!response.ok) { - throw new Error(`Failed to fetch ${url}: ${response.status}`); - } - - const html = await response.text(); - const content = parseTurboStreamContent(html); - - if (content !== null) { - targetElement.innerHTML = content; - - // Attach reload handler to the error button (Stimulus not available in Shadow DOM) - const reloadButton = targetElement.querySelector('#connection-error-reload-button'); - if (reloadButton) { - reloadButton.addEventListener('click', () => window.location.reload()); - } - } - } catch (error) { - console.error('Error fetching connection template:', error); - } -} diff --git a/frontend/src/react/hooks/useCollaboration.ts b/frontend/src/react/hooks/useCollaboration.ts index fd7639621e4..a4c16a29eaa 100644 --- a/frontend/src/react/hooks/useCollaboration.ts +++ b/frontend/src/react/hooks/useCollaboration.ts @@ -30,55 +30,70 @@ import { HocuspocusProvider } from '@hocuspocus/provider'; import { debugLog } from 'core-app/shared/helpers/debug_output'; -import { PROVIDER_AUTH_ERROR_EVENT, ProviderAuthErrorKind } from 'core-stimulus/services/documents/token-refresh.service'; +import { + PROVIDER_AUTH_ERROR_EVENT, + ProviderAuthErrorKind, +} from 'core-stimulus/services/documents/token-refresh.service'; import { useCallback, useEffect, useRef, useState } from 'react'; -import * as Y from 'yjs'; -function useConnectionTimeout(provider:HocuspocusProvider | undefined, timeoutMs = 5000) { - const [hasTimedOut, setHasTimedOut] = useState(false); - const timeoutRef = useRef | null>(null); +/** + * Calls `onTimeout` if the provider has not synced within `timeoutMs`. + * The timer is cancelled proactively when the provider emits 'synced', + * so it never fires after a successful connection. + */ +function useConnectionTimeout(provider:HocuspocusProvider, onTimeout:() => void, timeoutMs = 5000) { + const timeoutRef = useRef|null>(null); useEffect(() => { - setHasTimedOut(false); - if (!provider) return; - if (provider.synced) { - setHasTimedOut(false); return; } - timeoutRef.current = setTimeout(() => { - if (!provider.synced) { - setHasTimedOut(true); - } - }, timeoutMs); - - return () => { - if (timeoutRef.current) { + const cancel = () => { + if (timeoutRef.current !== null) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } }; - }, [provider, timeoutMs]); - return hasTimedOut; + timeoutRef.current = setTimeout(() => { + timeoutRef.current = null; + onTimeout(); + }, timeoutMs); + + // Cancel the timer as soon as the provider syncs rather than waiting + // for the full timeout to elapse. + provider.on('synced', cancel); + + return () => { + provider.off('synced', cancel); + cancel(); + }; + }, [provider, onTimeout, timeoutMs]); } +/** + * Subscribes to the provider's 'synced' and 'disconnect' events and + * forwards them to the supplied callbacks. + * + * Listeners are registered before the initial synced check so that a + * sync event emitted between registration and the check is never lost. + * If the provider is already synced on mount, `onSynced` is called + * immediately. + */ function useCollaborationProvider( - provider:HocuspocusProvider | undefined, + provider:HocuspocusProvider, onSynced:() => void, onDisconnect:() => void, ) { useEffect(() => { - if (!provider) return; + provider.on('synced', onSynced); + provider.on('disconnect', onDisconnect); if (provider.synced) { onSynced(); } - provider.on('synced', onSynced); - provider.on('disconnect', onDisconnect); - return () => { provider.off('synced', onSynced); provider.off('disconnect', onDisconnect); @@ -86,32 +101,43 @@ function useCollaborationProvider( }, [provider, onSynced, onDisconnect]); } -function useLocalDocumentSync(doc:Y.Doc, inputField:HTMLInputElement, enabled:boolean) { +/** + * Listens for PROVIDER_AUTH_ERROR_EVENT on the document and calls `onAuthError` when it fires. + * The event is dispatched when authentication fails on the Hocuspocus WebSocket connection. + */ +function useProviderAuthError(onAuthError:() => void) { useEffect(() => { - if (!enabled) return; - - const updateInput = () => { - const update = Y.encodeStateAsUpdate(doc); - const b64 = btoa(String.fromCharCode(...update)); - inputField.value = b64; + const handler = (event:Event) => { + const { kind, message } = (event as CustomEvent<{ kind:ProviderAuthErrorKind; message:string }>).detail; + debugLog(`(BlockNote Editor) Provider auth error: ${kind} - ${message}`); + onAuthError(); }; - doc.on('update', updateInput); - - return () => { - doc.off('update', updateInput); - doc.destroy(); - }; - }, [doc, inputField, enabled]); + document.addEventListener(PROVIDER_AUTH_ERROR_EVENT, handler); + return () => document.removeEventListener(PROVIDER_AUTH_ERROR_EVENT, handler); + }, [onAuthError]); } -export function useCollaboration( - provider:HocuspocusProvider | undefined, - doc:Y.Doc, - inputField:HTMLInputElement, -) { +/** + * Tracks the real-time connection state of a HocuspocusProvider and + * exposes it as React state for the BlockNote editor. + * + * Returns: + * - `isLoading` — true while waiting for the first sync after mount. + * - `offlineMode` — true when the connection is lost or timed out; the editor + * is hidden entirely (blocking) because there is no local + * cache to edit from. + * + * Transitions: + * mount → synced : isLoading false, offlineMode false + * mount → timeout (5s) : isLoading false, offlineMode true + * connected → disconnect : offlineMode true (after 5s grace period) + * offline → re-synced : offlineMode false + * any → auth error : isLoading false, offlineMode true + */ +function useCollaboration(provider:HocuspocusProvider) { const [isLoading, setIsLoading] = useState(true); - const [connectionError, setConnectionError] = useState(false); + const [offlineMode, setOfflineMode] = useState(false); const disconnectTimerRef = useRef | null>(null); const clearDisconnectTimer = useCallback(() => { @@ -125,57 +151,43 @@ export function useCollaboration( debugLog('(BlockNote Editor) synced with collaboration server'); clearDisconnectTimer(); setIsLoading(false); - setConnectionError(false); + setOfflineMode(false); }, [clearDisconnectTimer]); const handleDisconnect = useCallback(() => { debugLog('(BlockNote Editor) Disconnected — starting grace period'); - // Don't flag a connection error immediately. Start a grace timer so that - // transient reconnections (idle heartbeat, token-expiry reconnect) never - // surface to the user. If synced fires within the window the timer is - // cancelled. + setIsLoading(false); + // Don't go offline immediately. Start a grace timer so that transient + // reconnections (idle heartbeat, token-expiry reconnect) never surface + // to the user. If synced fires within the window the timer is cancelled. disconnectTimerRef.current ??= setTimeout(() => { disconnectTimerRef.current = null; - debugLog('(BlockNote Editor) Grace period expired — connection error'); - setConnectionError(true); + debugLog('(BlockNote Editor) Grace period expired — offline mode'); + setOfflineMode(true); }, 5_000); }, []); - const hasTimedOut = useConnectionTimeout(provider); - useCollaborationProvider(provider, handleSynced, handleDisconnect); - useLocalDocumentSync(doc, inputField, !provider); + const handleTimeout = useCallback(() => { + debugLog('(BlockNote Editor) Connection to collaboration server timed out - now in offline mode'); + setIsLoading(false); + setOfflineMode(true); + }, []); + + const handleAuthError = useCallback(() => { + // Auth errors are permanent — bypass the grace period. + clearDisconnectTimer(); + setOfflineMode(true); + setIsLoading(false); + }, [clearDisconnectTimer]); // Clean up the grace timer on unmount and when the provider changes. useEffect(() => clearDisconnectTimer, [provider, clearDisconnectTimer]); - useEffect(() => { - if (!provider) { - setIsLoading(false); - } - }, [provider]); + useConnectionTimeout(provider, handleTimeout); + useCollaborationProvider(provider, handleSynced, handleDisconnect); + useProviderAuthError(handleAuthError); - useEffect(() => { - if (hasTimedOut) { - debugLog('(BlockNote Editor) Connection to collaboration server timed out'); - setConnectionError(true); - setIsLoading(false); - } - }, [hasTimedOut]); - - useEffect(() => { - const handleProviderAuthError = (event:Event) => { - const customEvent = event as CustomEvent<{ kind:ProviderAuthErrorKind; message:string }>; - debugLog(`(BlockNote Editor) Provider auth error: ${customEvent.detail.kind} - ${customEvent.detail.message}`); - // Auth errors are permanent — bypass the grace period. - clearDisconnectTimer(); - setConnectionError(true); - }; - - document.addEventListener(PROVIDER_AUTH_ERROR_EVENT, handleProviderAuthError); - return () => document.removeEventListener(PROVIDER_AUTH_ERROR_EVENT, handleProviderAuthError); - }, [clearDisconnectTimer]); - - return { isLoading, connectionError } as const; + return { isLoading, offlineMode } as const; } -export { useCollaborationProvider }; +export { useCollaboration }; diff --git a/frontend/src/stimulus/controllers/dynamic/admin/work-packages-identifier.controller.ts b/frontend/src/stimulus/controllers/dynamic/admin/work-packages-identifier.controller.ts index e40bff2a598..2543efe5e87 100644 --- a/frontend/src/stimulus/controllers/dynamic/admin/work-packages-identifier.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/admin/work-packages-identifier.controller.ts @@ -52,17 +52,17 @@ export default class WorkPackagesIdentifierController extends Controller { } private updateVisibility() { - const showAutofix = this.isAlphanumericSelected() && this.hasProblematicProjectsValue; + const showAutofix = this.isSemanticSelected() && this.hasProblematicProjectsValue; this.autofixSectionTarget.hidden = !showAutofix; this.saveButtonTarget.hidden = showAutofix; this.autofixButtonTarget.hidden = !showAutofix; } - private isAlphanumericSelected():boolean { + private isSemanticSelected():boolean { const checked = this.element.querySelector( 'input[name="settings[work_packages_identifier]"]:checked', ); - return checked?.value === 'alphanumeric'; + return checked?.value === 'semantic'; } } diff --git a/frontend/src/stimulus/controllers/dynamic/documents/connection-status.controller.ts b/frontend/src/stimulus/controllers/dynamic/documents/connection-status.controller.ts new file mode 100644 index 00000000000..5d61e075afd --- /dev/null +++ b/frontend/src/stimulus/controllers/dynamic/documents/connection-status.controller.ts @@ -0,0 +1,72 @@ +/* + * -- 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. + * ++ + */ + +import { Controller } from '@hotwired/stimulus'; + +type ConnectionState = 'live' | 'offline' | 'recovered'; + +export default class ConnectionStatusController extends Controller { + static targets = ['live', 'offline', 'recovered']; + + declare readonly offlineTarget:HTMLElement; + declare readonly recoveredTarget:HTMLElement; + declare readonly liveTargets:HTMLElement[]; + + private recoveryTimeout:ReturnType|null = null; + + showOffline():void { + this.clearRecoveryTimeout(); + this.activateState('offline'); + } + + showRecovered():void { + this.clearRecoveryTimeout(); + this.activateState('recovered'); + + this.recoveryTimeout = setTimeout(() => this.activateState('live'), 5000); + } + + disconnect():void { + this.clearRecoveryTimeout(); + } + + private activateState(state:ConnectionState):void { + this.offlineTarget.hidden = state !== 'offline'; + this.recoveredTarget.hidden = state !== 'recovered'; + this.liveTargets.forEach((el) => { el.hidden = state !== 'live'; }); + } + + private clearRecoveryTimeout():void { + if (this.recoveryTimeout !== null) { + clearTimeout(this.recoveryTimeout); + this.recoveryTimeout = null; + } + } +} diff --git a/frontend/src/stimulus/controllers/dynamic/documents/editor-connection.controller.ts b/frontend/src/stimulus/controllers/dynamic/documents/editor-connection.controller.ts new file mode 100644 index 00000000000..5caba4647f0 --- /dev/null +++ b/frontend/src/stimulus/controllers/dynamic/documents/editor-connection.controller.ts @@ -0,0 +1,48 @@ +/* + * -- 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. + * ++ + */ + +import { Controller } from '@hotwired/stimulus'; + +export default class EditorConnectionController extends Controller { + static targets = ['error', 'editor']; + + declare readonly errorTarget:HTMLElement; + declare readonly editorTarget:HTMLElement; + + showError():void { + this.errorTarget.hidden = false; + this.editorTarget.hidden = true; + } + + showEditor():void { + this.errorTarget.hidden = true; + this.editorTarget.hidden = false; + } +} diff --git a/frontend/src/stimulus/controllers/dynamic/documents/init-yjs-provider.controller.ts b/frontend/src/stimulus/controllers/dynamic/documents/init-yjs-provider.controller.ts index ab450c4342a..e935131f9f8 100644 --- a/frontend/src/stimulus/controllers/dynamic/documents/init-yjs-provider.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/documents/init-yjs-provider.controller.ts @@ -1,37 +1,41 @@ /* * -- copyright - * openproject is an open source project management software. - * copyright (c) the openproject gmbh + * 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. + * 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 + * 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 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. + * 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. + * 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. + * See COPYRIGHT and LICENSE files for more details. * ++ */ import { HocuspocusProvider } from '@hocuspocus/provider'; import { Controller } from '@hotwired/stimulus'; import { LiveCollaborationManager } from 'core-stimulus/helpers/live-collaboration-helpers'; -import { PROVIDER_AUTH_ERROR_EVENT, ProviderAuthErrorKind, TokenRefreshService } from 'core-stimulus/services/documents/token-refresh.service'; +import { + PROVIDER_AUTH_ERROR_EVENT, + ProviderAuthErrorKind, + TokenRefreshService, +} from 'core-stimulus/services/documents/token-refresh.service'; import type { Doc } from 'yjs'; import * as Y from 'yjs'; diff --git a/frontend/src/stimulus/controllers/dynamic/my/daily-reminders.controller.ts b/frontend/src/stimulus/controllers/dynamic/my/daily-reminders.controller.ts new file mode 100644 index 00000000000..b63ce5f1cab --- /dev/null +++ b/frontend/src/stimulus/controllers/dynamic/my/daily-reminders.controller.ts @@ -0,0 +1,88 @@ +/* + * -- 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. + * ++ + */ + +import { Controller } from '@hotwired/stimulus'; + +export default class DailyRemindersController extends Controller { + static targets = ['list', 'row', 'rowTemplate']; + + declare readonly listTarget:HTMLElement; + declare readonly rowTargets:HTMLElement[]; + declare readonly rowTemplateTarget:HTMLTemplateElement; + declare readonly hasRowTemplateTarget:boolean; + + private readonly handleChange = ():void => { this.syncDisabledOptions(); }; + + connect():void { + this.listTarget.addEventListener('change', this.handleChange); + this.syncDisabledOptions(); + this.updateRemoveButtons(); + } + + disconnect():void { + this.listTarget.removeEventListener('change', this.handleChange); + } + + addTime():void { + if (!this.hasRowTemplateTarget) return; + + const clone = this.rowTemplateTarget.content.cloneNode(true) as DocumentFragment; + this.listTarget.appendChild(clone); + this.syncDisabledOptions(); + this.updateRemoveButtons(); + } + + removeTime(event:Event):void { + const button = event.currentTarget as HTMLElement; + button.closest('[data-my--daily-reminders-target="row"]')?.remove(); + this.syncDisabledOptions(); + this.updateRemoveButtons(); + } + + private updateRemoveButtons():void { + const rows = this.rowTargets; + const showRemove = rows.length > 1; + rows.forEach((row) => { + const btn = row.querySelector('[data-action="my--daily-reminders#removeTime"]'); + if (btn) btn.hidden = !showRemove; + }); + } + + private syncDisabledOptions():void { + const selects = Array.from(this.listTarget.querySelectorAll('select')); + const selectedValues = new Set(selects.map((s) => s.value)); + + selects.forEach((select) => { + Array.from(select.options).forEach((option) => { + option.disabled = selectedValues.has(option.value) && option.value !== select.value; + }); + }); + } +} diff --git a/frontend/src/stimulus/controllers/dynamic/my/look-and-feel.controller.ts b/frontend/src/stimulus/controllers/dynamic/my/look-and-feel.controller.ts index 2ac5a1a9e79..aea43c361f8 100644 --- a/frontend/src/stimulus/controllers/dynamic/my/look-and-feel.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/my/look-and-feel.controller.ts @@ -38,7 +38,6 @@ export default class LookAndFeelController extends Controller { declare singleThemeContrastTarget:HTMLElement; private syncWithOsValue = 'sync_with_os'; - private viewComponentSelector = '[data-view-component="true"]'; connect() { this.toggleOptionGroups(); @@ -56,9 +55,10 @@ export default class LookAndFeelController extends Controller { } private toggleElementVisibility(targetElement:HTMLElement, shouldShow:boolean) { - const viewComponentContainer = targetElement.closest(this.viewComponentSelector); - if (viewComponentContainer) { - (viewComponentContainer as HTMLElement).hidden = !shouldShow; + const container = targetElement.closest('.FormControl-check-group-wrap') + ?? targetElement.closest('[data-view-component="true"]'); + if (container) { + container.hidden = !shouldShow; } } } diff --git a/frontend/src/stimulus/controllers/dynamic/projects/identifier-suggestion.controller.ts b/frontend/src/stimulus/controllers/dynamic/projects/identifier-suggestion.controller.ts index 8c3ea979c1e..0310832ca7d 100644 --- a/frontend/src/stimulus/controllers/dynamic/projects/identifier-suggestion.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/projects/identifier-suggestion.controller.ts @@ -32,7 +32,7 @@ import {ApplicationController, useDebounce} from 'stimulus-use'; const ALLOWED_CHARS:Record = { semantic: /[^A-Z0-9_]/g, - legacy: /[^a-z0-9\-_]/g, + classic: /[^a-z0-9\-_]/g, }; export default class extends ApplicationController { @@ -42,7 +42,7 @@ export default class extends ApplicationController { static values = { url: String, debounce: {type: Number, default: 300}, - mode: {type: String, default: 'legacy'}, + mode: {type: String, default: 'classic'}, setNameFirst: {type: String, default: ''}, }; @@ -88,7 +88,7 @@ export default class extends ApplicationController { private filterInput():void { if (!this.hasIdentifierTarget) return; - const pattern = ALLOWED_CHARS[this.modeValue] ?? ALLOWED_CHARS.legacy; + const pattern = ALLOWED_CHARS[this.modeValue] ?? ALLOWED_CHARS.classic; const current = this.identifierTarget.value; const filtered = current.replace(pattern, ''); diff --git a/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/editor.controller.ts b/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/editor.controller.ts index 77194f0d7c6..2436608f2ef 100644 --- a/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/editor.controller.ts +++ b/frontend/src/stimulus/controllers/dynamic/work-packages/activities-tab/editor.controller.ts @@ -65,6 +65,8 @@ export default class EditorController extends BaseController { private rescuedEditorDataKey:string; private abortController = new AbortController(); private ckEditorAbortController = new AbortController(); + private editorDataObserver?:MutationObserver; + private editorDataTimer?:number; connect() { super.connect(); @@ -75,6 +77,7 @@ export default class EditorController extends BaseController { } disconnect() { + this.clearPendingEditorDataSetup(); this.rescueEditorContent(); this.removeCkEditorEventListeners(); this.removeEventListeners(); @@ -109,9 +112,7 @@ export default class EditorController extends BaseController { openEditorWithInitialData(quotedText:string) { this.showForm(); - if (this.isEditorEmpty()) { - this.ckEditorInstance!.setData(quotedText); - } + this.setEditorDataWhenReady(quotedText); } clearEditor() { @@ -210,6 +211,59 @@ export default class EditorController extends BaseController { this.ckEditorAbortController = new AbortController(); } + /** + * Sets the editor data once CKEditor is initialized. If CKEditor is already + * available and empty, sets the data immediately. Otherwise, watches for CKEditor + * readiness via MutationObserver. This handles the case where the Stimulus + * controller connects before CKEditor has finished its async initialization + * (e.g., after a Turbo navigation). + * + * A setTimeout deferral is used to ensure Angular's CKEditor initialization + * Promise chain has fully completed before we interact with the editor. + */ + private setEditorDataWhenReady(data:string) { + this.clearPendingEditorDataSetup(); + + if (this.ckEditorInstance) { + if (this.isEditorEmpty()) { + this.ckEditorInstance.setData(data); + } + return; + } + + const observer = new MutationObserver(() => { + if (this.ckEditorInstance) { + observer.disconnect(); + if (this.editorDataObserver === observer) { + this.editorDataObserver = undefined; + } + // Defer to the next macrotask so that Angular's CKEditor initialization + // Promise chain completes and the component's `initialized` flag is set. + // This prevents "Tried to access CKEditor instance before initialization" + // errors when the form is subsequently submitted. + this.editorDataTimer = window.setTimeout(() => { + this.editorDataTimer = undefined; + if (this.isEditorEmpty()) { + this.ckEditorInstance?.setData(data); + } + }); + } + }); + + this.editorDataObserver = observer; + observer.observe(this.element, { childList: true, subtree: true }); + } + + private clearPendingEditorDataSetup() { + this.editorDataObserver?.disconnect(); + this.editorDataObserver = undefined; + + if (this.editorDataTimer !== undefined) { + window.clearTimeout(this.editorDataTimer); + this.editorDataTimer = undefined; + } + } + private rescueEditorContent() { const data = this.ckEditorInstance?.getData({ trim: false }); if (data) { diff --git a/frontend/src/stimulus/controllers/hover-card-trigger.controller.ts b/frontend/src/stimulus/controllers/hover-card-trigger.controller.ts index 249bf9a8b21..ad718686a14 100644 --- a/frontend/src/stimulus/controllers/hover-card-trigger.controller.ts +++ b/frontend/src/stimulus/controllers/hover-card-trigger.controller.ts @@ -146,8 +146,8 @@ export default class HoverCardTriggerController extends ApplicationController { this.close(true); const turboFrameUrl = this.parseHoverCardUrl(el); - const popoverEl = this.getPopoverFromId(el); - if (!turboFrameUrl && !popoverEl) { return; } + const popoverTemplate = this.getPopoverTemplateFromId(el); + if (!turboFrameUrl && !popoverTemplate) { return; } // Reset close timer for when hovering over multiple triggers in quick succession. // A timer from a previous hover card might still be running. We do not want it to @@ -156,11 +156,11 @@ export default class HoverCardTriggerController extends ApplicationController { // Set a delay before showing the hover card this.hoverTimeout = window.setTimeout(() => { - this.showHoverCard(el, turboFrameUrl, popoverEl); + this.showHoverCard(el, turboFrameUrl, popoverTemplate); }, this.OPEN_DELAY_IN_MS); } - private showHoverCard(el:HTMLElement, turboFrameUrl:string, popoverElement:HTMLElement|null) { + private showHoverCard(el:HTMLElement, turboFrameUrl:string, popoverTemplate:HTMLTemplateElement|null) { // Abort if the trigger element is no longer present in the DOM. This can happen when this method is called after a delay. if (!this.element.contains(el)) { return; } // Do not try to show two hover cards at the same time. @@ -168,8 +168,8 @@ export default class HoverCardTriggerController extends ApplicationController { // The mouse might have left the trigger while we were waiting for the hover delay. if (!this.mouseIsHoveringOverTrigger) { return; } - if (popoverElement) { - this.showHoverCardViaExistingElement(el, popoverElement); + if (popoverTemplate) { + this.showHoverCardViaExistingElement(el, popoverTemplate); } else { this.loadAndShowHoverCardViaTurboFrame(el, turboFrameUrl); } @@ -194,13 +194,13 @@ export default class HoverCardTriggerController extends ApplicationController { }); } - private showHoverCardViaExistingElement(targetEl:HTMLElement, protoPopover:HTMLElement) { + private showHoverCardViaExistingElement(targetEl:HTMLElement, popoverTemplate:HTMLTemplateElement) { const overlay = this.getAndResetOverlay(); if (!overlay) { return; } this.moveOverlayToAppropriateParent(overlay, targetEl); - const popover = this.cloneStaticPopover(overlay, protoPopover); + const popover = this.popoverFromTemplate(overlay, popoverTemplate); this.isShowingHoverCard = true; this.previousTarget = targetEl; @@ -262,7 +262,7 @@ export default class HoverCardTriggerController extends ApplicationController { * When there is no URL or if the URL is invalid, will return an empty string. */ private parseHoverCardUrl(el:HTMLElement) { - let url = el.getAttribute('data-hover-card-url'); + let url = el.dataset.hoverCardUrl; if (!url) { return ''; } url = sanitizeUrl(url); @@ -272,11 +272,14 @@ export default class HoverCardTriggerController extends ApplicationController { return url === 'about:blank' ? '' : url; } - private getPopoverFromId(el:HTMLElement):HTMLElement|null { - const id = el.getAttribute('data-hover-card-popover-id'); + private getPopoverTemplateFromId(el:HTMLElement):HTMLTemplateElement|null { + const id = el.dataset.hoverCardPopoverTemplateId; if (!id) { return null; } - return document.getElementById(id); + const element = document.getElementById(id); + if (!(element instanceof HTMLTemplateElement)) { return null; } + + return element; } private async reposition(element:HTMLElement, target:HTMLElement) { @@ -317,12 +320,13 @@ export default class HoverCardTriggerController extends ApplicationController { return { turboFrame, popover }; } - private cloneStaticPopover(overlay:HTMLElement, staticPopover:HTMLElement):HTMLElement { - const popover = staticPopover.cloneNode(true) as HTMLElement; - - popover.removeAttribute('id'); + private popoverFromTemplate(overlay:HTMLElement, template:HTMLTemplateElement):HTMLElement { + const popover = document.createElement('div'); this.setPopoverAttributes(popover); + const popoverFragment = document.importNode(template.content, true); + popover.appendChild(popoverFragment); + overlay.appendChild(popover); return popover; @@ -331,7 +335,7 @@ export default class HoverCardTriggerController extends ApplicationController { private setPopoverAttributes(popover:HTMLElement) { popover.classList.add('op-hover-card'); popover.setAttribute('popover', 'auto'); - popover.setAttribute('data-hover-card-trigger-target', 'card'); + popover.dataset.hoverCardTriggerTarget = 'card'; } /* diff --git a/frontend/src/stimulus/setup.ts b/frontend/src/stimulus/setup.ts index be2be48ec2d..a373d2d9b48 100644 --- a/frontend/src/stimulus/setup.ts +++ b/frontend/src/stimulus/setup.ts @@ -26,6 +26,7 @@ import EditorController from './controllers/dynamic/work-packages/activities-tab import LazyPageController from './controllers/dynamic/work-packages/activities-tab/lazy-page.controller'; import EditablePageHeaderTitleController from './controllers/dynamic/editable-page-header-title.controller'; import WorkingHoursFormController from './controllers/dynamic/users/working-hours-form.controller'; +import DailyRemindersController from './controllers/dynamic/my/daily-reminders.controller'; import NonWorkingTimesController from './controllers/dynamic/users/non-working-times.controller'; import NonWorkingTimesFormController from './controllers/dynamic/users/non-working-times-form.controller'; @@ -86,6 +87,7 @@ OpenProjectStimulusApplication.preregister('highlight-target-element', Highlight OpenProjectStimulusApplication.preregister('select-autosize', SelectAutosizeController); OpenProjectStimulusApplication.preregister('editable-page-header-title', EditablePageHeaderTitleController); OpenProjectStimulusApplication.preregister('users--working-hours-form', WorkingHoursFormController); +OpenProjectStimulusApplication.preregister('my--daily-reminders', DailyRemindersController); OpenProjectStimulusApplication.preregister('users--non-working-times', NonWorkingTimesController); OpenProjectStimulusApplication.preregister('users--non-working-times-form', NonWorkingTimesFormController); OpenProjectStimulusApplication.preregister('check-all', CheckAllController); diff --git a/frontend/src/test.ts b/frontend/src/test.ts index 4519f147ad5..c1296c28a42 100644 --- a/frontend/src/test.ts +++ b/frontend/src/test.ts @@ -1,11 +1,7 @@ // This file is required by karma.conf.js and loads recursively all the .spec and framework files -// Require the reflect ES7 polyfill for JIT -import 'zone.js'; // Included with Angular CLI. -import 'core-js/es/reflect'; - -import 'zone.js/testing'; import { getTestBed } from '@angular/core/testing'; +import { NgModule, provideZonelessChangeDetection } from '@angular/core'; import { BrowserDynamicTestingModule, platformBrowserDynamicTesting, @@ -15,15 +11,22 @@ import { registerDialogStreamAction } from 'core-turbo/dialog-stream-action'; registerDialogStreamAction(); -// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access no-explicit-any +// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-member-access (window as any).global = window; // Declare global I18n shim window.I18n = new I18n(); +@NgModule({ + imports: [BrowserDynamicTestingModule], + exports: [BrowserDynamicTestingModule], + providers: [provideZonelessChangeDetection()], +}) +class ZonelessBrowserDynamicTestingModule {} + // First, initialize the Angular testing environment. getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, + ZonelessBrowserDynamicTestingModule, platformBrowserDynamicTesting(), { teardown: { destroyAfterEach: false }, diff --git a/lib/api/decorators/simple_form.rb b/lib/api/decorators/simple_form.rb index f25826d1d7a..11854028596 100644 --- a/lib/api/decorators/simple_form.rb +++ b/lib/api/decorators/simple_form.rb @@ -53,15 +53,15 @@ module API end def commit_method - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def form_url - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def resource_url - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def payload_representer @@ -79,11 +79,11 @@ module API end def contract_class - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def model - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end private diff --git a/lib/api/root_api.rb b/lib/api/root_api.rb index abf986df567..ea028b4a116 100644 --- a/lib/api/root_api.rb +++ b/lib/api/root_api.rb @@ -303,6 +303,8 @@ module API error_response ActiveRecord::RecordNotFound, ::API::Errors::NotFound, log: false error_response ActiveRecord::StaleObjectError, ::API::Errors::Conflict, log: false + + # TODO: Where do we expect this to be raised and **not** be a programming error? error_response NotImplementedError, ::API::Errors::NotImplemented, log: false error_response MultiJson::ParseError, ::API::Errors::ParseError diff --git a/lib/api/utilities/endpoints/bodied.rb b/lib/api/utilities/endpoints/bodied.rb index 185ab430e2d..dc9d29911ff 100644 --- a/lib/api/utilities/endpoints/bodied.rb +++ b/lib/api/utilities/endpoints/bodied.rb @@ -33,7 +33,7 @@ module API include NamespacedLookup def default_instance_generator(_model) - raise NotImplementedError + raise SubclassResponsibilityError end def default_params_modifier @@ -146,11 +146,11 @@ module API private def present_success(_request, _call) - raise NotImplementedError + raise SubclassResponsibilityError end def present_error(_call) - raise NotImplementedError + raise SubclassResponsibilityError end def success?(call) @@ -166,15 +166,15 @@ module API end def deduce_parse_representer - raise NotImplementedError + raise SubclassResponsibilityError end def deduce_parse_service - raise NotImplementedError + raise SubclassResponsibilityError end def deduce_render_representer - raise NotImplementedError + raise SubclassResponsibilityError end def deduce_api_namespace @@ -182,7 +182,7 @@ module API end def update_or_create - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/lib/api/utilities/endpoints/index.rb b/lib/api/utilities/endpoints/index.rb index 80b58e1d556..780a765b241 100644 --- a/lib/api/utilities/endpoints/index.rb +++ b/lib/api/utilities/endpoints/index.rb @@ -44,7 +44,7 @@ module API end def mount - raise NotImplementedError + raise SubclassResponsibilityError end attr_accessor :model, @@ -55,7 +55,7 @@ module API private def deduce_render_representer - raise NotImplementedError + raise SubclassResponsibilityError end def deduce_api_namespace diff --git a/lib/api/utilities/endpoints/modify.rb b/lib/api/utilities/endpoints/modify.rb index d3d721a9b44..774399b320d 100644 --- a/lib/api/utilities/endpoints/modify.rb +++ b/lib/api/utilities/endpoints/modify.rb @@ -39,7 +39,7 @@ module API private def present_success(_request, _call) - raise NotImplementedError + raise SubclassResponsibilityError end def present_error(call) diff --git a/lib/api/utilities/endpoints/show.rb b/lib/api/utilities/endpoints/show.rb index 174c61c2fcd..65528e93f1a 100644 --- a/lib/api/utilities/endpoints/show.rb +++ b/lib/api/utilities/endpoints/show.rb @@ -74,7 +74,7 @@ module API private def deduce_render_representer - raise NotImplementedError + raise SubclassResponsibilityError end def deduce_api_namespace diff --git a/lib/api/utilities/meta_property.rb b/lib/api/utilities/meta_property.rb index 07b6d4068ef..f3ecb50594a 100644 --- a/lib/api/utilities/meta_property.rb +++ b/lib/api/utilities/meta_property.rb @@ -60,7 +60,7 @@ module API end def meta_representer_class - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/lib/api/utilities/representer_to_json_cache.rb b/lib/api/utilities/representer_to_json_cache.rb index 12a058b19da..09002ea4d0f 100644 --- a/lib/api/utilities/representer_to_json_cache.rb +++ b/lib/api/utilities/representer_to_json_cache.rb @@ -44,7 +44,7 @@ module API end def json_cache_key - raise NotImplementedError + raise SubclassResponsibilityError end private diff --git a/lib/api/v3/queries/form_representer.rb b/lib/api/v3/queries/form_representer.rb index 011b67746d6..0c5dec11c6d 100644 --- a/lib/api/v3/queries/form_representer.rb +++ b/lib/api/v3/queries/form_representer.rb @@ -63,19 +63,19 @@ module API end def commit_action - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def commit_method - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def form_url - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def resource_url - raise NotImplementedError, "subclass responsibility" + raise SubclassResponsibilityError end def payload_representer diff --git a/lib/api/v3/queries/schemas/filter_dependency_representer.rb b/lib/api/v3/queries/schemas/filter_dependency_representer.rb index eb8be59f7d8..794c08e6cc9 100644 --- a/lib/api/v3/queries/schemas/filter_dependency_representer.rb +++ b/lib/api/v3/queries/schemas/filter_dependency_representer.rb @@ -82,11 +82,11 @@ module API end def type - raise NotImplementedError, "Subclass has to implement #type" + raise SubclassResponsibilityError, "Subclass has to implement #type" end def href_callback - raise NotImplementedError, "Subclass has to implement #href_callback" + raise SubclassResponsibilityError, "Subclass has to implement #href_callback" end attr_accessor :operator diff --git a/lib/api/v3/queries/schemas/principal_filter_dependency_representer.rb b/lib/api/v3/queries/schemas/principal_filter_dependency_representer.rb index 95b3f301109..ff4f572f099 100644 --- a/lib/api/v3/queries/schemas/principal_filter_dependency_representer.rb +++ b/lib/api/v3/queries/schemas/principal_filter_dependency_representer.rb @@ -47,7 +47,7 @@ module API private def filter_query - raise NotImplementedError, "Subclasses need to implement #filter_query" + raise SubclassResponsibilityError, "Subclasses need to implement #filter_query" end end end diff --git a/lib/api/v3/schemas/schema_collection_representer.rb b/lib/api/v3/schemas/schema_collection_representer.rb index cc1cd6cdf54..221c9219a94 100644 --- a/lib/api/v3/schemas/schema_collection_representer.rb +++ b/lib/api/v3/schemas/schema_collection_representer.rb @@ -56,7 +56,7 @@ module API attr_accessor :form_embedded def model_self_link(_model) - raise NotImplementedError, "Subclass has to implement this" + raise SubclassResponsibilityError end end end diff --git a/lib/api/v3/work_packages/eager_loading/base.rb b/lib/api/v3/work_packages/eager_loading/base.rb index 874f7dcc80c..f7aaddd4e58 100644 --- a/lib/api/v3/work_packages/eager_loading/base.rb +++ b/lib/api/v3/work_packages/eager_loading/base.rb @@ -39,7 +39,7 @@ module API end def apply(_work_package) - raise NotImplementedError + raise SubclassResponsibilityError end def self.module diff --git a/lib/api/v3/work_packages/schema/base_work_package_schema.rb b/lib/api/v3/work_packages/schema/base_work_package_schema.rb index a0308918e3b..b9425124322 100644 --- a/lib/api/v3/work_packages/schema/base_work_package_schema.rb +++ b/lib/api/v3/work_packages/schema/base_work_package_schema.rb @@ -80,7 +80,7 @@ module API private def contract - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/lib/api/v3/work_packages/schema/work_package_schema_representer.rb b/lib/api/v3/work_packages/schema/work_package_schema_representer.rb index bef356e401c..76511e52e97 100644 --- a/lib/api/v3/work_packages/schema/work_package_schema_representer.rb +++ b/lib/api/v3/work_packages/schema/work_package_schema_representer.rb @@ -385,7 +385,7 @@ module API end def form_config_attribute_representation(group) - OpenProject::Cache.fetch(*form_config_attribute_cache_key(group)) do + OpenProject::Cache.fetch_request_cached(*form_config_attribute_cache_key(group)) do ::JSON::parse(::API::V3::WorkPackages::Schema::FormConfigurations::AttributeRepresenter .new(group, current_user:, project: represented.project, embed_links: true) .to_json) diff --git a/lib/open_project/cache.rb b/lib/open_project/cache.rb index edb508408b9..50ddfcdf738 100644 --- a/lib/open_project/cache.rb +++ b/lib/open_project/cache.rb @@ -34,6 +34,18 @@ module OpenProject Rails.cache.fetch(CacheKey.key(*), **, &) end + # Like .fetch, but caches the result in RequestStore for the + # lifetime of the current request. + # Useful when accessing many times during a request to avoid + # multiple cache round-trips. + def self.fetch_request_cached(*, **, &) + key = CacheKey.key(*) + + RequestStore.fetch(key) do + Rails.cache.fetch(key, **, &) + end + end + def self.read(name, **, &) Rails.cache.read(CacheKey.key(name), **, &) end diff --git a/lib/open_project/rate_limiting/base.rb b/lib/open_project/rate_limiting/base.rb index 2d97759e14a..fc0b784244e 100644 --- a/lib/open_project/rate_limiting/base.rb +++ b/lib/open_project/rate_limiting/base.rb @@ -92,15 +92,15 @@ module OpenProject end def discriminator(request) - raise NotImplementedError + raise SubclassResponsibilityError end def default_limit - raise NotImplementedError + raise SubclassResponsibilityError end def default_period - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/lib/open_project/scm/adapters/checkout_instructions.rb b/lib/open_project/scm/adapters/checkout_instructions.rb index e8f6f282ea7..3bbbafa2b18 100644 --- a/lib/open_project/scm/adapters/checkout_instructions.rb +++ b/lib/open_project/scm/adapters/checkout_instructions.rb @@ -59,7 +59,7 @@ module OpenProject ## # Returns the checkout command for this vendor def checkout_command - raise NotImplementedError + raise SubclassResponsibilityError end private diff --git a/lib/open_project/text_formatting/formats/base_format.rb b/lib/open_project/text_formatting/formats/base_format.rb index bfb082e301f..737858c8fce 100644 --- a/lib/open_project/text_formatting/formats/base_format.rb +++ b/lib/open_project/text_formatting/formats/base_format.rb @@ -32,11 +32,11 @@ module OpenProject::TextFormatting::Formats class BaseFormat class << self def format - raise NotImplementedError + raise SubclassResponsibilityError end def priority - raise NotImplementedError + raise SubclassResponsibilityError end def helper diff --git a/lib/open_project/text_formatting/formats/base_formatter.rb b/lib/open_project/text_formatting/formats/base_formatter.rb index 55fe0093f1f..b9308ed8db4 100644 --- a/lib/open_project/text_formatting/formats/base_formatter.rb +++ b/lib/open_project/text_formatting/formats/base_formatter.rb @@ -39,7 +39,7 @@ module OpenProject::TextFormatting::Formats end def to_html(text) - raise NotImplementedError + raise SubclassResponsibilityError end protected diff --git a/lib/open_project/text_formatting/matchers/link_handlers/base.rb b/lib/open_project/text_formatting/matchers/link_handlers/base.rb index 40178cbd16b..6dc2d2484e5 100644 --- a/lib/open_project/text_formatting/matchers/link_handlers/base.rb +++ b/lib/open_project/text_formatting/matchers/link_handlers/base.rb @@ -62,7 +62,7 @@ module OpenProject::TextFormatting::Matchers ## # Test whether we should try to resolve the given link def applicable? - raise NotImplementedError + raise SubclassResponsibilityError end ## @@ -70,7 +70,7 @@ module OpenProject::TextFormatting::Matchers # and matchers. # If nil is returned, the link remains as-is. def call - raise NotImplementedError + raise SubclassResponsibilityError end def oid diff --git a/lib/open_project/text_formatting/matchers/regex_matcher.rb b/lib/open_project/text_formatting/matchers/regex_matcher.rb index a2cc6dea7ef..d23fd3b2caf 100644 --- a/lib/open_project/text_formatting/matchers/regex_matcher.rb +++ b/lib/open_project/text_formatting/matchers/regex_matcher.rb @@ -63,13 +63,13 @@ module OpenProject::TextFormatting ## # Get the regexp that matches the content def self.regexp - raise NotImplementedError + raise SubclassResponsibilityError end ## # Called with a match from the regexp on the node's content def self.process_match(matchdata, matched_string, context) - raise NotImplementedError + raise SubclassResponsibilityError end ## diff --git a/lib/open_project/ui/extensible_tabs.rb b/lib/open_project/ui/extensible_tabs.rb index 7363a462453..b8c17de2d0f 100644 --- a/lib/open_project/ui/extensible_tabs.rb +++ b/lib/open_project/ui/extensible_tabs.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH @@ -105,13 +107,13 @@ module OpenProject name: "notifications", partial: "users/notifications", path: ->(params) { edit_user_path(params[:user], tab: :notifications) }, - label: :"js.notifications.settings.title" + label: :"my_account.notifications_and_email.tabs.notifications" }, { name: "reminders", partial: "users/reminders", path: ->(params) { edit_user_path(params[:user], tab: :reminders) }, - label: :"js.reminders.settings.title" + label: :"my_account.notifications_and_email.tabs.email_reminders" } ] end diff --git a/lib/open_project/version.rb b/lib/open_project/version.rb index 338b47873cb..00cc8051491 100644 --- a/lib/open_project/version.rb +++ b/lib/open_project/version.rb @@ -32,7 +32,7 @@ require "open3" module OpenProject module VERSION # :nodoc: MAJOR = 17 - MINOR = 3 + MINOR = 4 PATCH = 0 class << self diff --git a/lib/primer/open_project/forms/block_note_editor.rb b/lib/primer/open_project/forms/block_note_editor.rb index 226d9cb5d90..c89a7127ef0 100644 --- a/lib/primer/open_project/forms/block_note_editor.rb +++ b/lib/primer/open_project/forms/block_note_editor.rb @@ -63,7 +63,7 @@ module Primer @blocknote_stylesheet_url = variable_asset_path("blocknote.css") @shadow_dom_stylesheet_url = variable_asset_path("styles.css") - @collaboration_enabled = true + @collaboration_enabled = Setting.real_time_text_collaboration_enabled? end end end diff --git a/lib/primer/open_project/forms/dsl/input_methods.rb b/lib/primer/open_project/forms/dsl/input_methods.rb index 23526dc2c1e..1ed63ce8168 100644 --- a/lib/primer/open_project/forms/dsl/input_methods.rb +++ b/lib/primer/open_project/forms/dsl/input_methods.rb @@ -98,7 +98,8 @@ module Primer def supports_help_texts?(model) return @supports_help_texts if defined?(@supports_help_texts) - @supports_help_texts = model && ::AttributeHelpText.available_types.include?(model.model_name) + @supports_help_texts = model.respond_to?(:model_name) && + ::AttributeHelpText.available_types.include?(model.model_name) end end end diff --git a/lib/redmine/menu_manager/menu_item.rb b/lib/redmine/menu_manager/menu_item.rb index cd508a1356f..1fb511f446a 100644 --- a/lib/redmine/menu_manager/menu_item.rb +++ b/lib/redmine/menu_manager/menu_item.rb @@ -39,7 +39,8 @@ class Redmine::MenuManager::MenuItem < Redmine::MenuManager::TreeNode :partial, :scheme, :engine, - :enterprise_feature + :enterprise_feature, + :show_divider_before # rubocop:disable Metrics/AbcSize # rubocop:disable Metrics/PerceivedComplexity @@ -60,6 +61,7 @@ class Redmine::MenuManager::MenuItem < Redmine::MenuManager::TreeNode @icon = options[:icon] @icon_after = options[:icon_after] @enterprise_feature = options[:enterprise_feature] + @show_divider_before = options[:show_divider_before] @caption = options[:caption] @context = options[:context] @html_options = options[:html].nil? ? {} : options[:html].dup @@ -178,4 +180,8 @@ class Redmine::MenuManager::MenuItem < Redmine::MenuManager::TreeNode def enterprise_feature_missing? @enterprise_feature.present? && !EnterpriseToken.allows_to?(@enterprise_feature) end + + def show_divider_before? + @show_divider_before || false + end end diff --git a/lib/redmine/menu_manager/top_menu/user_menu.rb b/lib/redmine/menu_manager/top_menu/user_menu.rb index ca0370ffb58..0806991d25f 100644 --- a/lib/redmine/menu_manager/top_menu/user_menu.rb +++ b/lib/redmine/menu_manager/top_menu/user_menu.rb @@ -141,6 +141,8 @@ module Redmine::MenuManager::TopMenu::UserMenu def add_lateral_user_menu_items(list, link_items) link_items.each do |item| + list.with_divider if item.show_divider_before? + list.with_item( href: allowed_node_url(item, nil), label: item.caption, diff --git a/lib/subclass_responsibility_error.rb b/lib/subclass_responsibility_error.rb new file mode 100644 index 00000000000..6175114ac26 --- /dev/null +++ b/lib/subclass_responsibility_error.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. +#++ +class SubclassResponsibilityError < StandardError + def initialize(message = nil) + message ||= "The subclass needs to implement this method." + + super + end +end diff --git a/lookbook/docs/components/hover-cards.md.erb b/lookbook/docs/components/hover-cards.md.erb index 3d5df90ded2..e31079736cd 100644 --- a/lookbook/docs/components/hover-cards.md.erb +++ b/lookbook/docs/components/hover-cards.md.erb @@ -59,8 +59,8 @@ The content of a hover card can be defined in two ways: 1. Via a turbo frame that is fetched from a URL. This is the preferred way if you want to lazily load the contents of the hover card when needed. -2. Via an existing DOM element that is present (but hidden) in the document on page load. This is useful when the - content is already present and no additional fetching is necessary. +2. Via an existing `