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..0b9fbdce4c6 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -76,6 +76,7 @@ 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 }} 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..511d0d88a1c 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? @@ -158,10 +182,25 @@ class MyController < ApplicationController end def user_params - # The Users::UpdateService updates the user's pref using the UserPreferences::UpdateService - # which has a contract/schema applied to the values which is why it is ok - # to blindly allow all scalar values in pref. - permitted_params.user.to_h.merge(params.permit(pref: {})) + permitted_params.my_account_settings.to_h + 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 @@ -175,6 +214,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/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..e814a1c8b2d 100644 --- a/app/models/permitted_params.rb +++ b/app/models/permitted_params.rb @@ -199,6 +199,10 @@ class PermittedParams params.require(:placeholder_user).permit(*self.class.permitted_attributes[:placeholder_user]) end + def my_account_settings + user.merge(pref:) + end + def user_register_via_omniauth permitted_params = params .require(:user) @@ -274,7 +278,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/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/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..1eaca470ccf 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"], 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 60bf671233b..228c2e069db 100644 --- a/config/locales/crowdin/af.yml +++ b/config/locales/crowdin/af.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ af:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ af: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/ar.yml b/config/locales/crowdin/ar.yml index 09a4709cf78..5c05d9fff96 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -406,20 +406,24 @@ ar:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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: zero: "... %{count} more projects" one: "... 1 more project" @@ -437,7 +441,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" @@ -1578,6 +1582,9 @@ ar: dependencies: "الاعتماديات" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1586,7 +1593,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: @@ -3595,6 +3602,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: @@ -3657,6 +3669,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: "المُسند إليه" @@ -3688,6 +3766,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" @@ -3728,6 +3807,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" @@ -3978,7 +4058,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: "آخر نشاط" @@ -5224,11 +5304,11 @@ 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: > + 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/crowdin/az.yml b/config/locales/crowdin/az.yml index 3560bd4e167..f4a911d3c7e 100644 --- a/config/locales/crowdin/az.yml +++ b/config/locales/crowdin/az.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ az:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ az: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/be.yml b/config/locales/crowdin/be.yml index 75eb8248ce6..2506edc0163 100644 --- a/config/locales/crowdin/be.yml +++ b/config/locales/crowdin/be.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -394,20 +394,24 @@ be:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" few: "... %{count} more projects" @@ -423,7 +427,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" @@ -1542,6 +1546,9 @@ be: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1550,7 +1557,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: @@ -3477,6 +3484,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: @@ -3539,6 +3551,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: "Прызначаная асоба" @@ -3570,6 +3648,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" @@ -3610,6 +3689,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" @@ -3860,7 +3940,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" @@ -5098,11 +5178,11 @@ 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: > + 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/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 6c54ec4ee18..23f6b795092 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ bg:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ bg: dependencies: "Зависимости" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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: "Изпълнител" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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: "Последна активност" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/ca.yml b/config/locales/crowdin/ca.yml index e781c4ccdaa..23fa99cb7c9 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ ca:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1503,6 +1507,9 @@ ca: dependencies: "Dependències" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1511,7 +1518,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: @@ -3356,6 +3363,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: @@ -3418,6 +3430,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" @@ -3449,6 +3527,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" @@ -3489,6 +3568,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" @@ -3739,7 +3819,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" @@ -4962,11 +5042,11 @@ 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: > + 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: "Mètode de destacament per defecte" setting_work_package_list_default_highlighted_attributes: "Atributs de destacament en línia per defecte" diff --git a/config/locales/crowdin/ckb-IR.yml b/config/locales/crowdin/ckb-IR.yml index cf8aa0166b6..eee8dbc269c 100644 --- a/config/locales/crowdin/ckb-IR.yml +++ b/config/locales/crowdin/ckb-IR.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ ckb-IR:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ ckb-IR: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/cs.yml b/config/locales/crowdin/cs.yml index d0ba4c6e09f..bc5b7186ba3 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -112,7 +112,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" @@ -123,8 +123,8 @@ cs: 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" @@ -160,7 +160,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: @@ -394,20 +394,24 @@ cs:

Hello,

A new project has been created: projectValue:name

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. 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 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: label_project: Projekt label_previous_identifier: Předchozí identifikátor 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" @@ -423,7 +427,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" @@ -1542,6 +1546,9 @@ cs: dependencies: "Závislosti" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projekty" "import/jira": @@ -1550,7 +1557,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: @@ -1665,7 +1672,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" @@ -2069,7 +2076,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í " @@ -2377,11 +2384,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" @@ -3471,12 +3478,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: @@ -3539,6 +3551,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" @@ -3557,7 +3635,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." @@ -3570,6 +3648,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" @@ -3610,6 +3689,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" @@ -3860,7 +3940,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" @@ -4008,9 +4088,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" @@ -4035,7 +4115,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" @@ -4196,7 +4276,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" @@ -4334,28 +4414,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" @@ -4364,7 +4444,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" @@ -4672,7 +4752,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" @@ -5098,12 +5178,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_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_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_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í" setting_work_package_list_default_highlighted_attributes: "Výchozí inline zvýrazněné atributy" setting_working_days: "Pracovní dny" @@ -5165,7 +5245,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" @@ -5348,7 +5428,7 @@ cs: text_destroy_with_associated: "There are additional objects associated with the work package(s) that are to be deleted. Those objects are of the following types:" text_destroy_what_to_do: "Co chcete udělat?" text_diff_truncated: "... Toto rozlišení bylo zkráceno, protože přesahuje maximální velikost, kterou lze zobrazit." - text_email_delivery_not_configured: "Doručení e-mailu není nakonfigurováno a notifikace jsou zakázány.\nNakonfigurujte váš SMTP server pro jejich povolení." + text_email_delivery_not_configured: "Doručení e-mailu není nakonfigurováno a oznámení jsou zakázána.\nNakonfigurujte váš SMTP server pro jejich povolení." text_enumeration_category_reassign_to: "Přiřadit je k této hodnotě:" text_enumeration_destroy_question: "%{count} objektů je přiřazeno k této hodnotě." text_file_repository_writable: "Do adresáře příloh lze zapisovat" diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index 91efacdff4e..206f897d9d2 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ da:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1504,6 +1508,9 @@ da: dependencies: "Aflæggere" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1512,7 +1519,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: @@ -3357,6 +3364,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: @@ -3419,6 +3431,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" @@ -3450,6 +3528,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" @@ -3490,6 +3569,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" @@ -3740,7 +3820,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" @@ -4966,11 +5046,11 @@ 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: > + 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/crowdin/de.yml b/config/locales/crowdin/de.yml index e9258eae405..350500ce99c 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -86,11 +86,11 @@ de: type_token_text: "Ihr Enterprise-Token-Text" token_placeholder: "Enterprise-Token Text hier einfügen" token_caption: "Weitere Informationen über die Aktivierung der Enterprise Edition finden Sie in unserer [Dokumentation](docs_url)." - add_token: "Enterprise-Edition Support Token hochladen" + add_token: "Enterprise edition Support Token hochladen" replace_token: "Aktuellen Enterprise edition Support Token ersetzen" order: "Enterprise on-premises bestellen" - paste: "Enterprise-Edition Support Token hier einfügen" - required_for_feature: "Dieses Add-on ist nur mit einem aktiven Enterprise-Edition Support-Token verfügbar." + paste: "Enterprise edition Support Token hier einfügen" + required_for_feature: "Dieses Add-on ist nur mit einem aktiven Enterprise edition Support-Token verfügbar." enterprise_link: "Klicken Sie hier für weitere Informationen." start_trial: "Kostenlose Testversion starten" book_now: "Jetzt buchen" @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ de:

Hallo,

Ein neues Projekt wurde erstellt: projectValue:name

Vielen Dank

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 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: 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" @@ -409,7 +413,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" @@ -1265,10 +1269,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: @@ -1322,7 +1326,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." @@ -1424,7 +1428,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" @@ -1499,6 +1503,9 @@ de: dependencies: "Abhängigkeiten" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projekte" "import/jira": @@ -1507,7 +1514,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: @@ -1677,7 +1684,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" @@ -2212,7 +2219,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: @@ -3028,7 +3035,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}" @@ -3315,7 +3322,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" @@ -3352,6 +3359,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: @@ -3414,6 +3426,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" @@ -3445,6 +3523,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" @@ -3485,6 +3564,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" @@ -3642,7 +3722,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" @@ -3735,7 +3815,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" @@ -4734,7 +4814,7 @@ de: update_timeout: "Speichere die Informationen bzgl. des genutzten Festplattenspeichers eines Projektarchivs für N Minuten.\nErhöhen Sie diesen Wert zur Verbesserung der Performance, da die Erfassung des genutzten Festplattenspeichers Ressourcen-intensiv ist." oauth_application_details_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: @@ -4963,11 +5043,11 @@ 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: > + 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: "Standard Hervorhebung" setting_work_package_list_default_highlighted_attributes: "Voreinstellung Inline Hervorherbung" @@ -5387,7 +5467,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. + Du hast dein Nutzerlimit erreicht (%{current}/%{max} active users). Bitte kontaktiere sales@openproject.com um deinen Enterprise edition Plan upzugraden und weitere Nutzer hinzuzufügen. warning_protocol_mismatch_html: > warning_bar: diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index dbf6d27eff6..e814d5cd5c9 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ el:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1502,6 +1506,9 @@ el: dependencies: "Εξαρτήσεις" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1510,7 +1517,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: @@ -3355,6 +3362,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: @@ -3417,6 +3429,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: "Ανάθεση σε" @@ -3448,6 +3526,7 @@ el: invalid_filter: "Invalid notification filter" label_accessibility: "Προσβασιμότητα" label_account: "Λογαριασμός" + label_actions: "Ενέργειες" label_active: "Ενεργό" label_activate_user: "Ενεργοποίηση χρήστη" label_active_in_new_projects: "Ενεργό σε καινούργια έργα" @@ -3488,6 +3567,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: "Αρχειοθέτηση έργου" @@ -3738,7 +3818,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: "Τελευταία δραστηριότητα" @@ -4966,11 +5046,11 @@ 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: > + 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: "Προεπιλεγμένη λειτουργία επισήμανσης" setting_work_package_list_default_highlighted_attributes: "Προεπιλεγμένα inline χαρακτηριστικά με επισήμανση" diff --git a/config/locales/crowdin/eo.yml b/config/locales/crowdin/eo.yml index bf3a859e90b..80d4ec0e9c9 100644 --- a/config/locales/crowdin/eo.yml +++ b/config/locales/crowdin/eo.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ eo:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ eo: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/es.yml b/config/locales/crowdin/es.yml index e92d928252d..8796e09ba72 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ es:

Hola,

Se ha creado un nuevo proyecto: projectValue:name

Muchas gracias

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 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: 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" @@ -409,7 +413,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" @@ -1329,7 +1333,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." @@ -1503,6 +1507,9 @@ es: dependencies: "Dependencias" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Proyectos" "import/jira": @@ -1511,7 +1518,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: @@ -3356,6 +3363,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: @@ -3418,6 +3430,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" @@ -3449,6 +3527,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" @@ -3489,6 +3568,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" @@ -3739,7 +3819,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" @@ -4966,11 +5046,11 @@ 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: > + 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: "Modo de resaltado predeterminado" setting_work_package_list_default_highlighted_attributes: "Atributos resaltados en línea predeterminados" diff --git a/config/locales/crowdin/et.yml b/config/locales/crowdin/et.yml index 47db71aa20c..911cc362cb2 100644 --- a/config/locales/crowdin/et.yml +++ b/config/locales/crowdin/et.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ et:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ et: dependencies: "Sõltuvused" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/eu.yml b/config/locales/crowdin/eu.yml index 9b8a0ae8be0..3ee838b35c7 100644 --- a/config/locales/crowdin/eu.yml +++ b/config/locales/crowdin/eu.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ eu:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ eu: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/fa.yml b/config/locales/crowdin/fa.yml index 0bb33344e69..361a6da1f86 100644 --- a/config/locales/crowdin/fa.yml +++ b/config/locales/crowdin/fa.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ fa:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ fa: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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: "نماینده" @@ -3452,6 +3530,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: "فعال در پروژه های جدید" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/fi.yml b/config/locales/crowdin/fi.yml index 50a3d08b364..0616b2a1a8a 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ fi:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ fi: dependencies: "Riippuvuudet" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/fil.yml b/config/locales/crowdin/fil.yml index 1002b73bfa5..b268f96ae76 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ fil:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ fil: dependencies: "Dependencia" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4968,11 +5048,11 @@ 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: > + 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/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 8354bbb6fe3..537f348d987 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -112,7 +112,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" @@ -123,8 +123,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: "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: "Nom" @@ -160,7 +160,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: "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: @@ -382,20 +382,24 @@ fr:

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.

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: 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: > - 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 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: label_project: Projet label_previous_identifier: Identifiant précédent 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: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Nécessite une vérification manuelle remaining_projects: one: "... 1 projet supplémentaire" other: "... %{count} projets supplémentaires" @@ -409,7 +413,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: 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: "Transitions par défaut" @@ -1505,6 +1509,9 @@ fr: dependencies: "Dépendances" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifiant" + work_package: "Lot de travaux" jira_import: projects: "Projets" "import/jira": @@ -1513,7 +1520,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: @@ -3358,6 +3365,11 @@ fr: quick_add: label: "Ajouter…" my_account: + notifications_and_email: + title: "Notification and email" + tabs: + notifications: "Notification settings" + email_reminders: "Email reminders" 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: @@ -3420,6 +3432,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: "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: "Personne assignée" @@ -3451,6 +3529,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" @@ -3491,6 +3570,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" @@ -3741,7 +3821,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é" @@ -4969,11 +5049,11 @@ 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: > + 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: Identifiants alphanumériques basés sur le projet - 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: "Mode de surbrillance par défaut" setting_work_package_list_default_highlighted_attributes: "Attributs en ligne mis en surbrillance par défaut" diff --git a/config/locales/crowdin/he.yml b/config/locales/crowdin/he.yml index 87327b599b0..bb81ecf0dc6 100644 --- a/config/locales/crowdin/he.yml +++ b/config/locales/crowdin/he.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -394,20 +394,24 @@ he:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" two: "... %{count} more projects" @@ -423,7 +427,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" @@ -1542,6 +1546,9 @@ he: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1550,7 +1557,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: @@ -3477,6 +3484,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: @@ -3539,6 +3551,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: "משויך אל" @@ -3570,6 +3648,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" @@ -3610,6 +3689,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" @@ -3860,7 +3940,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" @@ -5098,11 +5178,11 @@ 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: > + 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/crowdin/hi.yml b/config/locales/crowdin/hi.yml index cc6409f9f4f..bf706eb3aa7 100644 --- a/config/locales/crowdin/hi.yml +++ b/config/locales/crowdin/hi.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ hi:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1504,6 +1508,9 @@ hi: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1512,7 +1519,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: @@ -3357,6 +3364,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: @@ -3419,6 +3431,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: "अनुदिष्ट" @@ -3450,6 +3528,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" @@ -3490,6 +3569,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" @@ -3740,7 +3820,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" @@ -4968,11 +5048,11 @@ 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: > + 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: "डिफ़ॉल्ट हाइलाइटिंग तरीका" setting_work_package_list_default_highlighted_attributes: "डिफ़ॉल्ट पंक्ति ही में हाइलाइट किए गए गुण" diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index 07440bd359c..d42dee3d642 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -388,20 +388,24 @@ hr:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" few: "... %{count} more projects" @@ -416,7 +420,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" @@ -1524,6 +1528,9 @@ hr: dependencies: "Ovisnosti" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1532,7 +1539,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: @@ -3418,6 +3425,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: @@ -3480,6 +3492,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" @@ -3511,6 +3589,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" @@ -3551,6 +3630,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" @@ -3801,7 +3881,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" @@ -5034,11 +5114,11 @@ 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: > + 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/crowdin/hu.yml b/config/locales/crowdin/hu.yml index ca3a8849f76..bf9fcd83109 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ hu:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1505,6 +1509,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": @@ -1513,7 +1520,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: @@ -3358,6 +3365,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: @@ -3420,6 +3432,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" @@ -3451,6 +3529,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" @@ -3491,6 +3570,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" @@ -3741,7 +3821,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" @@ -4969,11 +5049,11 @@ 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: > + 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: "Alapértelmezett kiemelési mód" setting_work_package_list_default_highlighted_attributes: "Alapértelmezett kiemelt attribútumok sorai" diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index 8c28b78bb00..c7eb8baf367 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -376,20 +376,24 @@ id:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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" @@ -1484,6 +1488,9 @@ id: dependencies: "Dependensi" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1492,7 +1499,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: @@ -3296,6 +3303,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: @@ -3358,6 +3370,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" @@ -3389,6 +3467,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" @@ -3429,6 +3508,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" @@ -3679,7 +3759,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" @@ -4899,11 +4979,11 @@ 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: > + 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/crowdin/it.yml b/config/locales/crowdin/it.yml index e6215fbd137..5ee91b06a5e 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ it:

Ciao,

È stato creato un nuovo progetto: projectValue:name

Grazie

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 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: 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" @@ -409,7 +413,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" @@ -1504,6 +1508,9 @@ it: dependencies: "Dipendenze" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Progetti" "import/jira": @@ -1512,7 +1519,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: @@ -3357,6 +3364,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: @@ -3419,6 +3431,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" @@ -3450,6 +3528,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" @@ -3490,6 +3569,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" @@ -3740,7 +3820,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à" @@ -4968,11 +5048,11 @@ 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: > + 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: "Modalità evidenziazione predefinita" setting_work_package_list_default_highlighted_attributes: "Attributi evidenziati in linea predefiniti" diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index 8f5799ece5e..70117ee3736 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -112,7 +112,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" @@ -123,8 +123,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: "名前" @@ -160,7 +160,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: @@ -318,13 +318,13 @@ ja: success: "MCP configuration was updated successfully." scim_clients: authentication_methods: - sso: "IDプロバイダーからのJWT" - oauth2_client: "OAuth 2.0クライアント認証情報" + sso: "アイデンティティプロバイダからのJWT" + oauth2_client: "OAuth 2.0 クライアント資格情報" oauth2_token: "静的アクセストークン" created_client_credentials_dialog_component: - title: "クライアント認証情報の作成" - heading: "クライアント認証情報が生成されました" - one_time_hint: "クライアント・シークレットが表示されるのはこの時だけです。必ずコピーしてください。" + title: "クライアントの資格情報が作成されました" + heading: "クライアントの資格情報が生成されました" + one_time_hint: "クライアントのシークレットが表示される唯一の時間です。今すぐコピーしてください。" created_token_dialog_component: title: "トークンを作成しました" heading: "トークンが生成されました" @@ -337,21 +337,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クライアントがまだ設定されていません" @@ -376,20 +376,24 @@ ja:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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" @@ -973,26 +977,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: @@ -1016,7 +1020,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" @@ -1037,7 +1041,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: "ブラウザ" @@ -1051,17 +1055,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: @@ -1175,7 +1179,7 @@ ja: placeholder_users: right_to_manage_members_missing: > プレースホルダーユーザを削除する権限がありません。 プレースホルダー ユーザーがメンバーであるすべてのプロジェクトのメンバーを管理する権利はありません。 - delete_tooltip: "プレースホルダー・ユーザーの削除" + delete_tooltip: "プレースホルダー ユーザーを削除" deletion_info: heading_html: "Delete placeholder user %{name}" data_consequences: > @@ -1193,11 +1197,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: 現在、ステータス報告はありません。 @@ -1208,19 +1212,20 @@ ja: このステータスの色を割り当てたり変更する場合にクリックします。 ステータスボタンに表示され、テーブル内のワークパッケージを強調表示するために使用できます。 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: 新しいステータスを追加 @@ -1230,7 +1235,7 @@ ja: is_readonly: "読み取り専用" excluded_from_totals: "合計から除外" themes: - dark: "暗い" + dark: "ダーク" light: "ライト" sync_with_os: "自動(OSのテーマ設定に追従)" types: @@ -1348,15 +1353,15 @@ ja: could_not_be_saved: "次のワークパッケージを保存できませんでした:" none_could_be_saved: "%{total} ワークパッケージのどれも更新できませんでした。" x_out_of_y_could_be_saved: "%{failing} の %{total} ワークパッケージのうち、 %{success} を更新できませんでした。" - selected_because_descendants: "%{selected} のワークパッケージが選択されたが、合計 %{total} のワークパッケージが影響を受け、その中には子孫も含まれる。" - descendant: "選択された子孫" + selected_because_descendants: "%{selected} ワークパッケージが選択されている間、合計で %{total} ワークパッケージが子孫を含む影響を受けます。" + descendant: "選択された子孫です" move: no_common_statuses_exists: "選択されたすべてのワークパッケージに利用できるステータスはありません。 それらの状態は変更できません。" unsupported_for_multiple_projects: "複数のプロジェクトからのワークパッケージの一括移動 / コピーはサポートされていません" current_type_not_available_in_target_project: > - ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + ターゲット プロジェクトで現在のワークパッケージのタイプが有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 bulk_current_type_not_available_in_target_project: > - ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + 現在のタイプのワークパッケージはターゲット プロジェクトで有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 sharing: missing_workflow_warning: title: "ワークパッケージの共有のためのワークフローがありません" @@ -1380,9 +1385,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} を追加" @@ -1486,6 +1491,9 @@ ja: dependencies: "依存関係" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1494,7 +1502,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: @@ -3298,6 +3306,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: @@ -3360,6 +3373,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: "担当者" @@ -3391,6 +3470,7 @@ ja: invalid_filter: "無効な通知フィルター" label_accessibility: "アクセシビリティ" label_account: "アカウント" + label_actions: "操作" label_active: "アクティブ" label_activate_user: "ユーザーのアクティベート" label_active_in_new_projects: "新しいプロジェクトでアクティブ" @@ -3431,6 +3511,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: "プロジェクトをアーカイブ" @@ -3681,7 +3762,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: "最新の活動" @@ -4904,11 +4985,11 @@ 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: > + 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: "デフォルトの強調表示モード" setting_work_package_list_default_highlighted_attributes: "デフォルトのインライン強調表示属性" diff --git a/config/locales/crowdin/js-af.yml b/config/locales/crowdin/js-af.yml index 32f1cd5671e..2f82f988975 100644 --- a/config/locales/crowdin/js-af.yml +++ b/config/locales/crowdin/js-af.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ af: not_available: "Project N/A" 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 6df9b3ae33c..8b92ff87fb5 100644 --- a/config/locales/crowdin/js-ar.yml +++ b/config/locales/crowdin/js-ar.yml @@ -592,49 +592,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." @@ -657,38 +614,6 @@ ar: not_available: "Project N/A" 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 90156f18d91..583fff523fb 100644 --- a/config/locales/crowdin/js-az.yml +++ b/config/locales/crowdin/js-az.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ az: not_available: "Project N/A" 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 3ae6b6e3ad4..e1b854f69e7 100644 --- a/config/locales/crowdin/js-be.yml +++ b/config/locales/crowdin/js-be.yml @@ -590,49 +590,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." @@ -655,38 +612,6 @@ be: not_available: "Project N/A" 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 7df0ca14e47..7e97fa8b70f 100644 --- a/config/locales/crowdin/js-bg.yml +++ b/config/locales/crowdin/js-bg.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ bg: not_available: "Project N/A" 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 23765668e4e..7b88f129974 100644 --- a/config/locales/crowdin/js-ca.yml +++ b/config/locales/crowdin/js-ca.yml @@ -104,7 +104,7 @@ ca: button_save: "Desa" button_settings: "Configuració" button_uncheck_all: "Desmarca-ho tot" - button_update: "Actualitzar" + button_update: "Actualitza" button_export-atom: "Descarregar Atom" button_generate_pdf: "Generate PDF" button_create: "Crear" @@ -588,49 +588,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." @@ -653,38 +610,6 @@ ca: not_available: "Project N/A" 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 604aad76159..80671042092 100644 --- a/config/locales/crowdin/js-ckb-IR.yml +++ b/config/locales/crowdin/js-ckb-IR.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ ckb-IR: not_available: "Project N/A" 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 fc220381ef1..b1a3348278a 100644 --- a/config/locales/crowdin/js-cs.yml +++ b/config/locales/crowdin/js-cs.yml @@ -590,49 +590,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\n" - 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." @@ -655,38 +612,6 @@ cs: not_available: "Project N/A" 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 de4cf6c4050..ad433cd0e2c 100644 --- a/config/locales/crowdin/js-da.yml +++ b/config/locales/crowdin/js-da.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ da: not_available: "Project N/A" 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 36bcffb30a0..942276b0f95 100644 --- a/config/locales/crowdin/js-de.yml +++ b/config/locales/crowdin/js-de.yml @@ -138,7 +138,7 @@ de: description_available_columns: "Verfügbare Spalten" description_current_position: "Sie sind hier: " description_select_work_package: "Arbeitspaket #%{id} auswählen" - description_subwork_package: "Unteraufgabe von Arbeitspaket #%{id}" + description_subwork_package: "Kind von Arbeitspaket #%{id}" editor: revisions: "Lokale Änderungen anzeigen" no_revisions: "Keine lokalen Änderungen gefunden" @@ -453,7 +453,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" @@ -587,49 +587,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." @@ -652,38 +609,6 @@ de: not_available: "Projekt N/A" 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" @@ -862,7 +787,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" @@ -1059,7 +984,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 bc3010a0afd..d59c246708f 100644 --- a/config/locales/crowdin/js-el.yml +++ b/config/locales/crowdin/js-el.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ el: not_available: "Project N/A" 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 abc2969b64b..dd346bbde91 100644 --- a/config/locales/crowdin/js-eo.yml +++ b/config/locales/crowdin/js-eo.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ eo: not_available: "Project N/A" 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 fd659d12f13..27c5225b7f1 100644 --- a/config/locales/crowdin/js-es.yml +++ b/config/locales/crowdin/js-es.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ es: not_available: "Proyecto N/D" 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 0769c3f3d6c..3512be7649f 100644 --- a/config/locales/crowdin/js-et.yml +++ b/config/locales/crowdin/js-et.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ et: not_available: "Project N/A" 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 6e1e0cdbebc..31449b2a89f 100644 --- a/config/locales/crowdin/js-eu.yml +++ b/config/locales/crowdin/js-eu.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ eu: not_available: "Project N/A" 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 0b97be2ec4c..94c77743f8d 100644 --- a/config/locales/crowdin/js-fa.yml +++ b/config/locales/crowdin/js-fa.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ fa: not_available: "Project N/A" 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 12f7a8d40f7..969399c44be 100644 --- a/config/locales/crowdin/js-fi.yml +++ b/config/locales/crowdin/js-fi.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ fi: not_available: "Project N/A" 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 6f266923b9f..75a67955871 100644 --- a/config/locales/crowdin/js-fil.yml +++ b/config/locales/crowdin/js-fil.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ fil: not_available: "Project N/A" 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 0b60b2b9135..a5f7938228f 100644 --- a/config/locales/crowdin/js-fr.yml +++ b/config/locales/crowdin/js-fr.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ fr: not_available: "Projet N/D" 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 29386d34f63..bc9d1873953 100644 --- a/config/locales/crowdin/js-he.yml +++ b/config/locales/crowdin/js-he.yml @@ -590,49 +590,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." @@ -655,38 +612,6 @@ he: not_available: "Project N/A" 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 de7f267039c..2e0a25100e8 100644 --- a/config/locales/crowdin/js-hi.yml +++ b/config/locales/crowdin/js-hi.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ hi: not_available: "Project N/A" 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 469d2a66fcd..0ed437aea23 100644 --- a/config/locales/crowdin/js-hr.yml +++ b/config/locales/crowdin/js-hr.yml @@ -589,49 +589,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." @@ -654,38 +611,6 @@ hr: not_available: "Project N/A" 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 d3813a705d8..3f44f2ff60c 100644 --- a/config/locales/crowdin/js-hu.yml +++ b/config/locales/crowdin/js-hu.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ hu: not_available: "Project N/A" 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\n" - enable: "Napi e-mail emlékeztetők engedélyezése\n" - 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}\n" - 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\n" - 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 a96ffa1760f..9daf90c61cc 100644 --- a/config/locales/crowdin/js-id.yml +++ b/config/locales/crowdin/js-id.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ id: not_available: "Project N/A" 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 01a95429adc..2236101e5b5 100644 --- a/config/locales/crowdin/js-it.yml +++ b/config/locales/crowdin/js-it.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ it: not_available: "Progetto N/A" 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 e05de41967b..5844f6f2e7d 100644 --- a/config/locales/crowdin/js-ja.yml +++ b/config/locales/crowdin/js-ja.yml @@ -32,12 +32,12 @@ ja: draggable_hint: | 埋め込み画像または添付ファイルをエディタにドラッグします。 ドラッグしつづけると閉じているエディタ領域が開きます。 - quarantined_hint: "ウイルスが発見されたように、ファイルは隔離されています。ダウンロードできません。" + quarantined_hint: "ウイルスが発見されたため,ファイルは隔離されています。ダウンロードできません。" autocomplete_ng_select: - add_tag: "アイテムを追加" + add_tag: "項目を追加" clear_all: "すべてクリア" loading: "読み込み中..." - not_found: "アイテムが見つかりません" + not_found: "見つかりませんでした" type_to_search: "検索キーワードを入力" autocomplete_select: placeholder: @@ -67,7 +67,7 @@ ja: button_back_to_list_view: "リスト表示に戻る" button_cancel: "キャンセル" button_close: "閉じる" - button_change_project: "別のプロジェクトに移動" + button_change_project: "他のプロジェクトに移る" button_check_all: "全てを選択" button_configure-form: "フォームを設定" button_confirm: "確認" @@ -75,7 +75,7 @@ ja: button_copy: "コピー" button_copy_to_clipboard: "クリップボードにコピー" button_copy_link_to_clipboard: "クリップボードにリンクをコピー" - button_copy_to_other_project: "別のプロジェクトで複製" + button_copy_to_other_project: "別のプロジェクトで複製する" button_custom-fields: "カスタムフィールド" button_delete: "削除" button_delete_watcher: "ウォッチャーを削除" @@ -97,7 +97,7 @@ ja: button_open_fullscreen: "全画面表示を開く" button_show_cards: "カードビュー表示" button_show_list: "リストビュー表示" - button_show_table: "テーブルビューを表示" + button_show_table: "テーブル表示" button_show_gantt: "ガントビューを表示" button_show_fullscreen: "全画面表示" button_more_actions: "その他の操作" @@ -107,7 +107,7 @@ ja: button_uncheck_all: "全てを選択解除" button_update: "更新" button_export-atom: "Atomをダウンロード" - button_generate_pdf: "PDFを生成" + button_generate_pdf: "PDF作成" button_create: "作成" card: add_new: "新規カード追加" @@ -141,8 +141,8 @@ ja: description_select_work_package: "ワークパッケージを選択 #%{id}" description_subwork_package: "ワークパッケージの子 #%{id}" editor: - revisions: "ローカルの変更を表示" - no_revisions: "ローカルの変更は見つかりませんでした" + revisions: "ローカルの修正を表示" + no_revisions: "ローカルでの修正は見つからず" preview: "プレビューモードの切り替え" source_code: "Markdown ソースモードの切り替え" error_saving_failed: "次のエラーで文書を保存するのに失敗しました: %{error}" @@ -155,7 +155,7 @@ ja: attribute_reference: macro_help_tooltip: "このテキストセグメントはマクロによって動的にレンダリングされています。" not_found: "要求されたリソースが見つかりませんでした" - nested_macro: "このマクロは %{model} %{id} を再帰的に参照しています。" + nested_macro: "このマクロは %{model} %{id}を再帰的に参照している。" invalid_attribute: "選択した属性 '%{name}' は存在しません。" child_pages: button: "子ページへのリンク" @@ -211,10 +211,10 @@ ja: calendar: empty_state_header: "休業日" empty_state_description: '休業日が定義されていません。「休業日を追加」ボタンをクリックして日付を追加してください。' - new_date: "(新規)" + new_date: "(新)" add_non_working_day: "休業日を追加" - already_added_error: "この日付の非作業日はすでに存在します。それぞれの日付に1つの非作業日が作成されます。" - change_button: "保存してスケジュールを変更" + already_added_error: "この日付の非営業日はすでに存在します。一意の日付に対して作成できる非営業日は1つだけです。" + change_button: "保存して再スケジュール" change_title: "営業日を変更する" removed_title: "以下の日を非稼働日リストから削除します:" change_description: "営業日とみなす曜日を変更すると、このサイト内のすべてのプロジェクトのすべてのワークパッケージの開始日と終了日に影響を与える可能性があります。" @@ -296,14 +296,14 @@ ja: ical_sharing_modal: title: "カレンダーを購読する" inital_setup_error_message: "データ取得中にエラーが発生しました。" - description: "URL(iCalendar)を使って外部クライアントでこのカレンダーを購読し、そこから最新のワークパッケージ情報を見ることができます。" - warning: "このURLを他のユーザーと共有しないでください。このリンクがあれば、誰でもアカウントやパスワードなしでワークパッケージの詳細を見ることができます。" - token_name_label: "どこで使うのですか?" + description: "URL(iCalendar)を使用して、外部クライアントでこのカレンダーを購読し、そこから最新のワークパッケージ情報を表示することができます。" + warning: "このURLを他のユーザーと共有しないでください。このリンクを持つ誰でもアカウントやパスワードなしでワークパッケージの詳細を表示することができます。" + token_name_label: "どこで使うのですか??" token_name_placeholder: '名前を入力してください。例:"電話"' token_name_description_text: 'If you subscribe to this calendar from multiple devices, this name will help you distinguish between them in your access tokens list.' copy_url_label: "URLをコピー" - ical_generation_error_text: "カレンダー URL の生成中にエラーが発生しました。" - success_message: 'URL "%{name}" は正常にクリップボードにコピーされました。サブスクリプションを完了するためにカレンダークライアントに貼り付けてください。' + ical_generation_error_text: "カレンダーのURL生成時にエラーが発生しました。" + success_message: 'URL "%{name}" がクリップボードにコピーされました。カレンダークライアントに貼り付けて購読を完了してください。' label_activate: "有効にする" label_assignee: "担当者" label_assignee_alt_text: "This work package is assigned to %{name}" @@ -316,7 +316,7 @@ ja: label_add_row_before: "前に行を追加" label_add_selected_columns: "選択した列を追加" label_added_by: "追加した人" - label_added_time_by: '%{author} が %{age} に追加しました' + label_added_time_by: '追加 %{author} %{age}' label_ago: "○日前" label_all: "全て" label_all_projects: "すべてのプロジェクト" @@ -427,7 +427,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: "すべての属性を表示" @@ -465,8 +465,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: "アップロードした人" @@ -497,7 +497,7 @@ ja: label_version_plural: "バージョン" label_view_has_changed: "このビューには未保存の変更があります。 クリックすると保存します。" help_texts: - show_modal: "ヘルプテキストを表示" + show_modal: "ヘルプテキストを表示する" onboarding: buttons: skip: "スキップ" @@ -505,7 +505,7 @@ ja: got_it: "了承" steps: help_menu: "ヘルプ(?)メニューは、その他のヘルプリソースを提供します。ここでは、ユーザーガイド、役立つハウツービデオなどを見つけることができます。
OpenProjectでの作業をお楽しみください!" - members: "新しい メンバー をプロジェクトに招待します。" + members: "新しいメンバーをプロジェクトに招待する。" quick_add_button: "ヘッダーナビゲーションにあるプラス(+)アイコンをクリックして、新規プロジェクトを作成したり、同僚を招待したりできます。" sidebar_arrow: "プロジェクトのメインメニューに戻るには、左上の矢印を使います。" welcome: "3分間のイントロダクションツアーで、最も重要な機能を学びましょう。
最後までステップを完了することをお勧めします。ツアーはいつでも再開できます。" @@ -588,57 +588,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: "ここにタイトルを入力します" @@ -648,43 +605,11 @@ 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: "パンくず" @@ -1170,7 +1095,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 daeeac344b8..f992f2eaccd 100644 --- a/config/locales/crowdin/js-ka.yml +++ b/config/locales/crowdin/js-ka.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ ka: not_available: "Project N/A" 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 4852f0cefa9..f09f1e5af13 100644 --- a/config/locales/crowdin/js-kk.yml +++ b/config/locales/crowdin/js-kk.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ kk: not_available: "Project N/A" 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 d6676dbc681..3d5cc8af95a 100644 --- a/config/locales/crowdin/js-ko.yml +++ b/config/locales/crowdin/js-ko.yml @@ -587,49 +587,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: "페이지를 건너뛰었습니다." @@ -652,38 +609,6 @@ ko: not_available: "프로젝트 N/A" 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 d94f00a971d..288c17ea640 100644 --- a/config/locales/crowdin/js-lt.yml +++ b/config/locales/crowdin/js-lt.yml @@ -590,49 +590,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." @@ -655,38 +612,6 @@ lt: not_available: "Project N/A" 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 4462f015f45..1d085d57b1c 100644 --- a/config/locales/crowdin/js-lv.yml +++ b/config/locales/crowdin/js-lv.yml @@ -589,49 +589,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." @@ -654,38 +611,6 @@ lv: not_available: "Project N/A" 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 f43a994899b..d5d1529f01b 100644 --- a/config/locales/crowdin/js-mn.yml +++ b/config/locales/crowdin/js-mn.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ mn: not_available: "Project N/A" 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 548de987633..f174cea6720 100644 --- a/config/locales/crowdin/js-ms.yml +++ b/config/locales/crowdin/js-ms.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ ms: not_available: "Project N/A" 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 5ebcd8c9b1b..7d422cbac0b 100644 --- a/config/locales/crowdin/js-ne.yml +++ b/config/locales/crowdin/js-ne.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ ne: not_available: "Project N/A" 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 4fa85ea507b..2c1009c5f72 100644 --- a/config/locales/crowdin/js-nl.yml +++ b/config/locales/crowdin/js-nl.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ nl: not_available: "Project N/A" 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 b195361642a..4da1e88b61c 100644 --- a/config/locales/crowdin/js-no.yml +++ b/config/locales/crowdin/js-no.yml @@ -104,7 +104,7 @@ button_save: "Lagre" button_settings: "Innstillinger" button_uncheck_all: "Avmerk alle" - button_update: "Oppdater" + button_update: "Oppdatèr" button_export-atom: "Last ned Atom" button_generate_pdf: "Generate PDF" button_create: "Opprett" @@ -588,49 +588,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." @@ -653,38 +610,6 @@ not_available: "Project N/A" 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 9f16f80a828..cca8b1eeb2c 100644 --- a/config/locales/crowdin/js-pl.yml +++ b/config/locales/crowdin/js-pl.yml @@ -590,49 +590,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." @@ -655,38 +612,6 @@ pl: not_available: "Projekt niedostępny" 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 aa4e0d896a2..a035b3046fc 100644 --- a/config/locales/crowdin/js-pt-BR.yml +++ b/config/locales/crowdin/js-pt-BR.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ pt-BR: not_available: "Projeto N/A" 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 3a13e68f925..e50705348ea 100644 --- a/config/locales/crowdin/js-pt-PT.yml +++ b/config/locales/crowdin/js-pt-PT.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ pt-PT: not_available: "Projeto N/A" 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 62b7e6f93bc..804aee401db 100644 --- a/config/locales/crowdin/js-ro.yml +++ b/config/locales/crowdin/js-ro.yml @@ -104,7 +104,7 @@ ro: button_save: "Salvează" button_settings: "Setări" button_uncheck_all: "Deselectează tot" - button_update: "Actualizează" + button_update: "Actualizare" button_export-atom: "Descarcă Atom" button_generate_pdf: "Generează PDF" button_create: "Creează" @@ -588,49 +588,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." @@ -653,38 +610,6 @@ ro: not_available: "Proiect N/A" 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 5fb5c7fae12..98252a75862 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -104,7 +104,7 @@ ru: button_save: "Сохранить" button_settings: "Настройки" button_uncheck_all: "Снять все отметки" - button_update: "Обновить" + button_update: "Обновление" button_export-atom: "Скачать Atom" button_generate_pdf: "Создать PDF" button_create: "Создать" @@ -589,49 +589,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: "Страницы пропущены." @@ -654,38 +611,6 @@ ru: not_available: "Проект не существует" 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 4ec1e8f9fa8..2ffb952161a 100644 --- a/config/locales/crowdin/js-rw.yml +++ b/config/locales/crowdin/js-rw.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ rw: not_available: "Project N/A" 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 4b6f3561a33..777c5e50799 100644 --- a/config/locales/crowdin/js-si.yml +++ b/config/locales/crowdin/js-si.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ si: not_available: "Project N/A" 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 6157233870e..0e6a5224ebb 100644 --- a/config/locales/crowdin/js-sk.yml +++ b/config/locales/crowdin/js-sk.yml @@ -590,49 +590,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." @@ -655,38 +612,6 @@ sk: not_available: "Project N/A" 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 ed7dfa7410d..c00d21132ff 100644 --- a/config/locales/crowdin/js-sl.yml +++ b/config/locales/crowdin/js-sl.yml @@ -589,49 +589,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." @@ -654,38 +611,6 @@ sl: not_available: "Project N/A" 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 8dc44039cb2..b51aa79b3a0 100644 --- a/config/locales/crowdin/js-sr.yml +++ b/config/locales/crowdin/js-sr.yml @@ -589,49 +589,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." @@ -654,38 +611,6 @@ sr: not_available: "Project N/A" 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 262b845d5a6..467e3cec729 100644 --- a/config/locales/crowdin/js-sv.yml +++ b/config/locales/crowdin/js-sv.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ sv: not_available: "Projekt N/A" 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 959a7fc3144..98b4b9c53bf 100644 --- a/config/locales/crowdin/js-th.yml +++ b/config/locales/crowdin/js-th.yml @@ -587,49 +587,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." @@ -652,38 +609,6 @@ th: not_available: "Project N/A" 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 b46df40217b..e0912a3b3f4 100644 --- a/config/locales/crowdin/js-tr.yml +++ b/config/locales/crowdin/js-tr.yml @@ -588,49 +588,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ı." @@ -653,38 +610,6 @@ tr: not_available: "Proje mevcut değil" 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 17502467190..f6e9468ccac 100644 --- a/config/locales/crowdin/js-uk.yml +++ b/config/locales/crowdin/js-uk.yml @@ -590,49 +590,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: "Сторінки пропущено." @@ -655,38 +612,6 @@ uk: not_available: "Проєкт Н/Д" 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 7c4010397cc..5f4fc71f146 100644 --- a/config/locales/crowdin/js-uz.yml +++ b/config/locales/crowdin/js-uz.yml @@ -588,49 +588,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." @@ -653,38 +610,6 @@ uz: not_available: "Project N/A" 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 9bf4fac4c1e..263debd5bdf 100644 --- a/config/locales/crowdin/js-vi.yml +++ b/config/locales/crowdin/js-vi.yml @@ -104,7 +104,7 @@ vi: button_save: "lưu lại" button_settings: "cài đặt" button_uncheck_all: "Bỏ chọn tất cả" - button_update: "cập nhật" + button_update: "Cập Nhật" button_export-atom: "Tải xuống nguyên tử" button_generate_pdf: "Tạo PDF" button_create: "Tạo mới" @@ -587,49 +587,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." @@ -652,38 +609,6 @@ vi: not_available: "Dự án N/A" 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 279ff61f683..de9077e0a6c 100644 --- a/config/locales/crowdin/js-zh-CN.yml +++ b/config/locales/crowdin/js-zh-CN.yml @@ -587,49 +587,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: "这些特定于项目的设置会覆盖上面的默认设置。\n" - add: "为项目添加设置" - already_selected: "此项目已经选定" - remove: "移除项目设置" pagination: no_other_page: "您位于唯一页面上。" pages_skipped: "跳过的页面。" @@ -652,38 +609,6 @@ zh-CN: not_available: "项目 N/A" 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 f4eb8029b3c..8c497dc8d1a 100644 --- a/config/locales/crowdin/js-zh-TW.yml +++ b/config/locales/crowdin/js-zh-TW.yml @@ -586,49 +586,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: "監看者\n" - 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: "跳過的頁面。" @@ -651,38 +608,6 @@ zh-TW: not_available: "無專案" 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 ac8679ae53c..e0d6d451682 100644 --- a/config/locales/crowdin/ka.yml +++ b/config/locales/crowdin/ka.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ ka:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ ka: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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: "დამსაქმებელი" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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: "ბოლო აქტივობა" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/kk.yml b/config/locales/crowdin/kk.yml index f2bf96ea2b3..aa5d4d39233 100644 --- a/config/locales/crowdin/kk.yml +++ b/config/locales/crowdin/kk.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ kk:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ kk: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/ko.yml b/config/locales/crowdin/ko.yml index d9c5bc1fa4e..a12a64d9c2f 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -112,7 +112,7 @@ ko: import: title: "가져오기" jira: - title: "Jira 가져오기" + title: "Jira Migrator" description: "이 도구를 사용하여 Jira 인스턴스에서 데이터를 가져옵니다. 여러 Jira 호스트를 구성하고 가져오기 실행 각각에서 가져올 항목을 선택할 수 있습니다." errors: cannot_delete_with_imports: "기존 가져오기가 있는 Jira 호스트는 삭제할 수 없습니다" @@ -123,8 +123,8 @@ ko: title: "Jira 구성" new: "새 구성" banner: - title: "제한된 가져오기" - description: "이 가져오기 도구는 현재 베타 버전이며 프로젝트, 이슈(이름, 제목, 설명, 첨부 파일), 사용자(이름, 이메일, 프로젝트 멤버십), 상태 및 유형과 같은 기본 데이터만 가져올 수 있습니다. 워크플로, 사용자 지정 필드, 이슈 관계 또는 권한은 가져올 수 없습니다. Jira Server/Data Center 버전 10.x 및 11.x만 현재 지원합니다. 클라우드 인스턴스는 현재 지원되지 않습니다." + 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: "이름" @@ -160,7 +160,7 @@ ko: 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: @@ -376,20 +376,24 @@ ko:

안녕하세요,

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

감사합니다.

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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,7 @@ ko: 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: "기본 전환" @@ -1488,6 +1492,9 @@ ko: dependencies: "종속성" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "프로젝트" "import/jira": @@ -1496,7 +1503,7 @@ ko: personal_access_token: "개인 액세스 토큰" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira 가져오기" + jira_import: "Jira Migrator" announcements: show_until: "표시 기한" attachment: @@ -3300,6 +3307,11 @@ ko: 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: @@ -3362,6 +3374,72 @@ ko: 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: "담당자" @@ -3393,6 +3471,7 @@ ko: invalid_filter: "잘못된 알림 필터" label_accessibility: "접근성" label_account: "계정" + label_actions: "작업" label_active: "활성" label_activate_user: "사용자 활성화" label_active_in_new_projects: "새로운 프로젝트에서 활성" @@ -3433,6 +3512,7 @@ ko: 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: "프로젝트 아카이브하기" @@ -3683,7 +3763,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: "마지막 활동" @@ -4903,11 +4983,11 @@ ko: 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: > + 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: "기본 강조 표시 모드" setting_work_package_list_default_highlighted_attributes: "기본 인라인 강조 표시 특성" diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index 9fa9577b502..0068b1839b1 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -394,20 +394,24 @@ lt:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" few: "... %{count} more projects" @@ -423,7 +427,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" @@ -1539,6 +1543,9 @@ lt: dependencies: "Priklausomybės" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1547,7 +1554,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: @@ -3474,6 +3481,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: @@ -3536,6 +3548,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" @@ -3567,6 +3645,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" @@ -3607,6 +3686,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ą" @@ -3857,7 +3937,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" @@ -5092,11 +5172,11 @@ 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: > + 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: "Numatytasis paryškinimo būdas" setting_work_package_list_default_highlighted_attributes: "Atributai, kuriuos numatyta paryškinti" diff --git a/config/locales/crowdin/lv.yml b/config/locales/crowdin/lv.yml index a962d41cbd5..9d5ec918d4d 100644 --- a/config/locales/crowdin/lv.yml +++ b/config/locales/crowdin/lv.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -388,20 +388,24 @@ lv:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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: zero: "... %{count} more projects" one: "... 1 more project" @@ -416,7 +420,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" @@ -1524,6 +1528,9 @@ lv: dependencies: "Saistītie projekti" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1532,7 +1539,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: @@ -3418,6 +3425,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: @@ -3480,6 +3492,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" @@ -3511,6 +3589,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" @@ -3551,6 +3630,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" @@ -3801,7 +3881,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" @@ -5034,11 +5114,11 @@ 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: > + 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/crowdin/mn.yml b/config/locales/crowdin/mn.yml index b162d704e99..00644722e2b 100644 --- a/config/locales/crowdin/mn.yml +++ b/config/locales/crowdin/mn.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ mn:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ mn: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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: "Даалгагч" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/ms.yml b/config/locales/crowdin/ms.yml index bae2e4f5924..7c444c31c41 100644 --- a/config/locales/crowdin/ms.yml +++ b/config/locales/crowdin/ms.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -376,20 +376,24 @@ ms:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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" @@ -1486,6 +1490,9 @@ ms: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1494,7 +1501,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: @@ -3298,6 +3305,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: @@ -3360,6 +3372,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" @@ -3391,6 +3469,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" @@ -3431,6 +3510,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" @@ -3681,7 +3761,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" @@ -4903,11 +4983,11 @@ 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: > + 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: "Mod penyorotan default" setting_work_package_list_default_highlighted_attributes: "Atribut disorot sebaris default" diff --git a/config/locales/crowdin/ne.yml b/config/locales/crowdin/ne.yml index 5d2381b73a4..bb40cacd460 100644 --- a/config/locales/crowdin/ne.yml +++ b/config/locales/crowdin/ne.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ ne:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ ne: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/nl.yml b/config/locales/crowdin/nl.yml index 5701bca1337..699fdab3810 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ nl:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1503,6 +1507,9 @@ nl: dependencies: "Afhankelijkheden" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1511,7 +1518,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: @@ -3356,6 +3363,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: @@ -3418,6 +3430,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" @@ -3449,6 +3527,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" @@ -3489,6 +3568,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" @@ -3739,7 +3819,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" @@ -4967,11 +5047,11 @@ 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: > + 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: "Standaardmodus markeren" setting_work_package_list_default_highlighted_attributes: "Inline gemarkeerd standaardkenmerken" diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index c5c9223a699..0c18ce4b389 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1505,6 +1509,9 @@ dependencies: "Avhengigheter" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1513,7 +1520,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: @@ -3358,6 +3365,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: @@ -3420,6 +3432,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" @@ -3451,6 +3529,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" @@ -3491,6 +3570,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" @@ -3741,7 +3821,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" @@ -4969,11 +5049,11 @@ 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: > + 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: "Standard utheving modus" setting_work_package_list_default_highlighted_attributes: "Standard inline uthevede egenskaper" diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index a8ffeca0663..e2c7d65d139 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -112,7 +112,7 @@ pl: import: title: "Import" jira: - title: "Import Jira" + title: "Jira Migrator" 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" @@ -123,8 +123,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: "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: "Nazwa" @@ -160,7 +160,7 @@ pl: run: title: "Uruchomienie importu" history: "Historia" - remove_error: "Importu Jira nie można usunąć, gdy jest uruchomiony" + remove_error: "A Jira import run cannot be removed while it is running" import_blocked_error: "Inne uruchomienie importu Jira jest obecnie w toku lub oczekuje na sprawdzenie. Przed rozpoczęciem nowego importu zakończ je lub cofnij." 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: @@ -394,20 +394,24 @@ pl:

Witaj,

Utworzono nowy projekt: projectValue:name

Dziękujemy

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 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: 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" few: "... %{count} more projects" @@ -423,7 +427,7 @@ pl: 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: "Przejścia domyślne" @@ -1538,6 +1542,9 @@ pl: dependencies: "Zależności" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projekty" "import/jira": @@ -1546,7 +1553,7 @@ pl: personal_access_token: "Osobisty token dostępu" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira import" + jira_import: "Jira Migrator" announcements: show_until: "Wyświetlaj do" attachment: @@ -3473,6 +3480,11 @@ pl: quick_add: label: "Dodaj…" my_account: + notifications_and_email: + title: "Notification and email" + tabs: + notifications: "Notification settings" + email_reminders: "Email reminders" 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: @@ -3535,6 +3547,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: "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: "Przypisana osoba" @@ -3566,6 +3644,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" @@ -3606,6 +3685,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: "Integrations" label_add_column: "Dodaj kolumnę" label_applied_status: "Nadaj status" label_archive_project: "Archiwum projektów" @@ -3856,7 +3936,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: "Jira Migrator" label_keyword_plural: "Słowa kluczowe" label_language_based: "Na podstawie języka użytkownika" label_last_activity: "Ostatnia aktywność" @@ -5092,11 +5172,11 @@ 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: 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: "Domyślny tryb wyróżniania" setting_work_package_list_default_highlighted_attributes: "Domyślnie wyróżniane atrybuty wyświetlane w treści" diff --git a/config/locales/crowdin/pt-BR.yml b/config/locales/crowdin/pt-BR.yml index 5d3b7e3f0a9..8d339bed90d 100644 --- a/config/locales/crowdin/pt-BR.yml +++ b/config/locales/crowdin/pt-BR.yml @@ -112,7 +112,7 @@ pt-BR: import: title: "Importar" jira: - title: "Importação do Jira" + title: "Jira Migrator" 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" @@ -123,8 +123,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: "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" @@ -160,7 +160,7 @@ pt-BR: run: title: "Execução de importação" history: "Histórico" - remove_error: "Uma importação do Jira não pode ser removida enquanto estiver em execução" + remove_error: "A Jira import run cannot be removed while it is running" import_blocked_error: "Outra execução de importação do Jira está em andamento ou aguardando revisão. Conclua ou reverta-a antes de iniciar uma nova importação." 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: @@ -382,20 +382,24 @@ pt-BR:

Olá,

Um novo projeto foi criado: projectValue:name

Obrigado

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 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: 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" @@ -409,7 +413,7 @@ pt-BR: 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: "Transições padrão" @@ -1504,6 +1508,9 @@ pt-BR: dependencies: "Dependências" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projetos" "import/jira": @@ -1512,7 +1519,7 @@ pt-BR: personal_access_token: "Token de acesso de pessoal" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira import" + jira_import: "Jira Migrator" announcements: show_until: "Exibir até" attachment: @@ -3357,6 +3364,11 @@ pt-BR: quick_add: label: "Adicionar…" my_account: + notifications_and_email: + title: "Notification and email" + tabs: + notifications: "Notification settings" + email_reminders: "Email reminders" 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: @@ -3419,6 +3431,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: "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: "Cessionário" @@ -3450,6 +3528,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" @@ -3490,6 +3569,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: "Integrations" label_add_column: "Adicionar coluna" label_applied_status: "Situação aplicada" label_archive_project: "Arquivar projeto" @@ -3740,7 +3820,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: "Jira Migrator" label_keyword_plural: "Palavras-chave" label_language_based: "Com base no idioma do usuário" label_last_activity: "Última atividade" @@ -4967,11 +5047,11 @@ 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: 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: "Modo de destaque padrão" setting_work_package_list_default_highlighted_attributes: "Atributos embutidos com destaque padrão" diff --git a/config/locales/crowdin/pt-PT.yml b/config/locales/crowdin/pt-PT.yml index f87cd93342a..ea7f0dc52ac 100644 --- a/config/locales/crowdin/pt-PT.yml +++ b/config/locales/crowdin/pt-PT.yml @@ -112,7 +112,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" @@ -123,8 +123,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: "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" @@ -160,7 +160,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: "A Jira import run cannot be removed while it is running" 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: "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: @@ -382,20 +382,24 @@ pt-PT:

Olá,

Foi criado um novo projeto: projectValue:name

Obrigado

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 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: 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" @@ -409,7 +413,7 @@ pt-PT: 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: "Transições padrão" @@ -1504,6 +1508,9 @@ pt-PT: dependencies: "Dependências" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projetos" "import/jira": @@ -1512,7 +1519,7 @@ pt-PT: personal_access_token: "Token de acesso pessoal" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira import" + jira_import: "Jira Migrator" announcements: show_until: "Exibir até" attachment: @@ -3357,6 +3364,11 @@ pt-PT: quick_add: label: "Adicionar…" my_account: + notifications_and_email: + title: "Notification and email" + tabs: + notifications: "Notification settings" + email_reminders: "Email reminders" 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: @@ -3419,6 +3431,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: "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: "Pessoa atribuída" @@ -3450,6 +3528,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" @@ -3490,6 +3569,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: "Integrations" label_add_column: "Adicionar coluna" label_applied_status: "Status aplicado" label_archive_project: "Arquivar projeto" @@ -3740,7 +3820,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" @@ -4965,11 +5045,11 @@ 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: 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: "Modo de destaque padrão" setting_work_package_list_default_highlighted_attributes: "Atributos padrão destacados em linha" diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index 5aa9f87defc..b58ba030148 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -388,20 +388,24 @@ ro:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" few: "... %{count} more projects" @@ -416,7 +420,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" @@ -1524,6 +1528,9 @@ ro: dependencies: "Dependenţe" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1532,7 +1539,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: @@ -3418,6 +3425,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: @@ -3480,6 +3492,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" @@ -3511,6 +3589,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" @@ -3551,6 +3630,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" @@ -3694,7 +3774,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ă" @@ -3751,7 +3831,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}" @@ -3801,7 +3881,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" @@ -5034,11 +5114,11 @@ 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: > + 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: "Mod de evidențiere implicit" setting_work_package_list_default_highlighted_attributes: "Atributele evidențiate implicit în linie" diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index efad76d6241..dda9bb94472 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -112,7 +112,7 @@ ru: import: title: "Импорт" jira: - title: "Импорт из Jira" + title: "Мигратор Jira" description: "Используйте этот инструмент для импорта данных из Вашего экземпляра Jira. Вы можете настроить несколько узлов Jira и выбрать, что импортировать в каждом запуске импорта." errors: cannot_delete_with_imports: "Невозможно удалить хост Jira с существующими импортами" @@ -123,8 +123,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: "Имя" @@ -160,7 +160,7 @@ ru: run: title: "Выполнение импорта" history: "История" - remove_error: "Импорт Jira нельзя удалить во время его выполнения" + remove_error: "Запуск импорта Jira не может быть удалён, пока он запущен" import_blocked_error: "Другая операция импорта Jira находится в процессе выполнения или ожидает рассмотрения. Пожалуйста, завершите или отмените его, прежде чем начинать новый импорт." project_identifier_taken: "Вы пытаетесь импортировать проект с уже использованным идентификатором: %{taken_identifier}. Пожалуйста, обновите идентификатор проекта в Jira, затем нажмите «Повторить»." blank: @@ -394,20 +394,24 @@ ru:

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

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

Спасибо

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. + Существующие идентификаторы для проектов %{project_count} не соответствуют требованиям, предъявляемым к семантическим идентификаторам. OpenProject может автоматически обновить их, чтобы они были действительными, как показано в примерах ниже. Щелкните на 'Исправить и сохранить', чтобы обновить идентификаторы для всех проектов и включить семантические идентификаторы на основе проекта. box_header: label_project: Проект label_previous_identifier: Предыдущий идентификатор 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" @@ -423,7 +427,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: "Переходы по умолчанию" @@ -1541,6 +1545,9 @@ ru: dependencies: "Связи" activerecord: attributes: + work_package_semantic_alias: + identifier: "Идентификатор" + work_package: "Пакет работ" jira_import: projects: "Проекты" "import/jira": @@ -1549,7 +1556,7 @@ ru: personal_access_token: "Персональный токен доступа" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Импорт из Jira" + jira_import: "Мигратор Jira" announcements: show_until: "Отобразить до" attachment: @@ -3476,6 +3483,11 @@ ru: 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: @@ -3538,6 +3550,72 @@ ru: 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: "Исполнитель" @@ -3569,6 +3647,7 @@ ru: invalid_filter: "Неверный фильтр уведомлений" label_accessibility: "Спец. возможности" label_account: "Учетная запись" + label_actions: "Действия" label_active: "Активен" label_activate_user: "Активировать пользователя" label_active_in_new_projects: "Активное участие в новых проектах" @@ -3609,6 +3688,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: "Архивировать проект" @@ -3859,7 +3939,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: "Последняя активность" @@ -5094,11 +5174,11 @@ 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_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: > + setting_work_packages_identifier_classic: Числовая последовательность для всего экземпляра (по умолчанию) + setting_work_packages_identifier_classic_caption: > + Каждый пакет работ получает порядковый номер, начинающийся с 1 и увеличивающийся с каждым новым пакетом. Номера уникальны в пределах данного экземпляра, поэтому они остаются неизменными, даже если пакеты работ перемещаются между проектами. + 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: "Способ выделения по умолчанию" setting_work_package_list_default_highlighted_attributes: "Выделенные встроенные атрибуты по умолчанию" diff --git a/config/locales/crowdin/rw.yml b/config/locales/crowdin/rw.yml index d6bb82d3349..7d04cbd28b4 100644 --- a/config/locales/crowdin/rw.yml +++ b/config/locales/crowdin/rw.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ rw:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ rw: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/si.yml b/config/locales/crowdin/si.yml index de672cdafce..e70211e780a 100644 --- a/config/locales/crowdin/si.yml +++ b/config/locales/crowdin/si.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ si:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ si: dependencies: "පරායත්තතා" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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: "අස්ගිනී" @@ -3452,6 +3530,7 @@ si: invalid_filter: "Invalid notification filter" label_accessibility: "ප්රවේශ්යතාව" label_account: "ගිණුම" + label_actions: "ක්‍රියාමාර්ග" label_active: "ක්රියාකාරී" label_activate_user: "පරිශීලකයා සක්රිය කරන්න" label_active_in_new_projects: "නව ව්යාපෘතිවල ක්රියාකාරී වේ" @@ -3492,6 +3571,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: "සංරක්ෂිත ව්යාපෘතිය" @@ -3742,7 +3822,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: "අවසාන ක්රියාකාරකම්" @@ -4970,11 +5050,11 @@ 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: > + 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: "පෙරනිමි ඉස්මතු මාදිලිය" setting_work_package_list_default_highlighted_attributes: "පෙරනිමි පේළිගත උද්දීපිත ගුණාංග" diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index aa4f9580dd7..b0bcdd8e3bd 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -394,20 +394,24 @@ sk:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" few: "... %{count} more projects" @@ -423,7 +427,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" @@ -1542,6 +1546,9 @@ sk: dependencies: "Závislosti" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1550,7 +1557,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: @@ -3477,6 +3484,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: @@ -3539,6 +3551,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é" @@ -3570,6 +3648,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" @@ -3610,6 +3689,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" @@ -3860,7 +3940,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" @@ -5098,11 +5178,11 @@ 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: > + 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: "Predvolený režim zvýraznenia" setting_work_package_list_default_highlighted_attributes: "Predvolené inline zvýraznené atribúty" diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index d975336868e..ba7b685906c 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -394,20 +394,24 @@ sl:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" two: "... %{count} more projects" @@ -423,7 +427,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" @@ -1541,6 +1545,9 @@ sl: dependencies: "Odvisnosti" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1549,7 +1556,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: @@ -2828,8 +2835,8 @@ sl: - "avgust" - "september" - "oktober" - - "november" - - "december" + - "November" + - "December" order: - :leto - :mesec @@ -3476,6 +3483,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: @@ -3538,6 +3550,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" @@ -3569,6 +3647,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" @@ -3609,6 +3688,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" @@ -3859,7 +3939,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" @@ -5096,11 +5176,11 @@ 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: > + 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: "Privzeti način osvetlitve" setting_work_package_list_default_highlighted_attributes: "Privzeti atributi, označeni s črto" diff --git a/config/locales/crowdin/sr.yml b/config/locales/crowdin/sr.yml index 70c4506a18b..91a836237be 100644 --- a/config/locales/crowdin/sr.yml +++ b/config/locales/crowdin/sr.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -388,20 +388,24 @@ sr:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" few: "... %{count} more projects" @@ -416,7 +420,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" @@ -1524,6 +1528,9 @@ sr: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1532,7 +1539,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: @@ -3418,6 +3425,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: @@ -3480,6 +3492,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" @@ -3511,6 +3589,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" @@ -3551,6 +3630,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" @@ -3801,7 +3881,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" @@ -5034,11 +5114,11 @@ 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: > + 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/crowdin/sv.yml b/config/locales/crowdin/sv.yml index d5c714c63e3..a34495a969b 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ sv:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ sv: dependencies: "Beroenden" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4968,11 +5048,11 @@ 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: > + 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: "Standardläge för markering" setting_work_package_list_default_highlighted_attributes: "Standardattribut för inline-markering" diff --git a/config/locales/crowdin/th.yml b/config/locales/crowdin/th.yml index 76225ff9435..ae51f849b87 100644 --- a/config/locales/crowdin/th.yml +++ b/config/locales/crowdin/th.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -376,20 +376,24 @@ th:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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" @@ -1488,6 +1492,9 @@ th: dependencies: "ส่วนที่อ้างอิง" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1496,7 +1503,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: @@ -3300,6 +3307,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: @@ -3362,6 +3374,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: "ผู้ได้รับมอบหมาย" @@ -3393,6 +3471,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" @@ -3433,6 +3512,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" @@ -3683,7 +3763,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" @@ -4906,11 +4986,11 @@ 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: > + 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/crowdin/tr.yml b/config/locales/crowdin/tr.yml index cded9a019af..2b15d9477b2 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ tr:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1507,6 +1511,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": @@ -1515,7 +1522,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: @@ -3360,6 +3367,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: @@ -3422,6 +3434,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" @@ -3453,6 +3531,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" @@ -3493,6 +3572,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" @@ -3743,7 +3823,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" @@ -4969,11 +5049,11 @@ 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: > + 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: "Varsayılan vurgulama modu" setting_work_package_list_default_highlighted_attributes: "Varsayılan satır içi vurgulanan özellikler" diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index 26a3d406e38..2131c82507f 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -112,7 +112,7 @@ uk: import: title: "Iмпорт" jira: - title: "Імпорт із Jira" + title: "Jira Migrator" description: "Використовуйте цей інструмент для імпорту даних зі свого екземпляра Jira. Ви можете налаштувати кілька хостів Jira й вибрати, що імпортувати в кожному циклі імпорту." errors: cannot_delete_with_imports: "Не вдається видалити хост Jira з наявними процесами імпорту" @@ -123,8 +123,8 @@ uk: title: "Конфігурація Jira" new: "Нова конфігурація" banner: - title: "Обмежений імпорт" - description: "Цей інструмент зараз доступний лише як бета-версія і може імпортувати тільки основні дані: проєкти, задачі (назву, заголовок, опис, вкладення), користувачів (ім’я, електронну адресу, дані про участь у проєктах), статуси й типи. Він не може імпортувати робочі процеси, користувацькі поля, зв’язки між задачами чи дозволи. Зараз ми підтримуємо лише версії Jira Server / Data Center 10.x і 11.x. Хмарні екземпляри поки що не підтримуються." + 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: "Назва" @@ -160,7 +160,7 @@ uk: 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: @@ -394,20 +394,24 @@ uk:

Вітаємо,

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

Дякуємо

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 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: label_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" few: "... %{count} more projects" @@ -423,7 +427,7 @@ uk: 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: "Стандартні переходи" @@ -1537,6 +1541,9 @@ uk: dependencies: "Залежності" activerecord: attributes: + work_package_semantic_alias: + identifier: "Ідентифікатор" + work_package: "Робочий пакет" jira_import: projects: "Проєкти" "import/jira": @@ -1545,7 +1552,7 @@ uk: personal_access_token: "Персональний маркер доступу" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira import" + jira_import: "Jira Migrator" announcements: show_until: "Показувати до" attachment: @@ -3472,6 +3479,11 @@ uk: 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: @@ -3534,6 +3546,72 @@ uk: disabled_text: "Маркери API не ввімкнено адміністратором. Зверніться до нього, якщо вам потрібна ця функція." 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: "Виконавець" @@ -3565,6 +3643,7 @@ uk: invalid_filter: "Недійсний фільтр сповіщень" label_accessibility: "Розробникам також потрібно оплачувати свої рахунки. З доступністю" label_account: "Обліковий запис" + label_actions: "Дії" label_active: "Активні" label_activate_user: "Активні користувачі" label_active_in_new_projects: "Активна участь в нових проектах" @@ -3605,6 +3684,7 @@ uk: 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: "Архівний проект" @@ -3833,7 +3913,7 @@ uk: label_index_by_title: "Індекс за назвою" label_information: "Інформація" label_information_plural: "Інформація" - label_installation_guides: "Інструкції із встановлення" + label_installation_guides: "Інструкції зі встановлення" label_integer: "Ціле число" label_interface: "Інтерфейс" label_internal: "Власне" @@ -3855,7 +3935,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: "Остання активність" @@ -5091,11 +5171,11 @@ uk: 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: > + 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: "Режим виділення за умовчанням" setting_work_package_list_default_highlighted_attributes: "Стандартні вбудовані виділені атрибути" diff --git a/config/locales/crowdin/uz.yml b/config/locales/crowdin/uz.yml index b8fc3692792..b4eb410d708 100644 --- a/config/locales/crowdin/uz.yml +++ b/config/locales/crowdin/uz.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -382,20 +382,24 @@ uz:

Hello,

A new project has been created: projectValue:name

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. 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 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: 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" @@ -409,7 +413,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" @@ -1506,6 +1510,9 @@ uz: dependencies: "Dependencies" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1514,7 +1521,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: @@ -3359,6 +3366,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: @@ -3421,6 +3433,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" @@ -3452,6 +3530,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" @@ -3492,6 +3571,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" @@ -3742,7 +3822,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" @@ -4970,11 +5050,11 @@ 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: > + 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/crowdin/vi.yml b/config/locales/crowdin/vi.yml index b323c85542c..dd624b50e2b 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -376,20 +376,24 @@ vi:

Xin chào,

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

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. 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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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" @@ -1488,6 +1492,9 @@ vi: dependencies: "phụ thuộc" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1496,7 +1503,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: @@ -2398,7 +2405,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" @@ -3300,6 +3307,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: @@ -3362,6 +3374,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" @@ -3393,6 +3471,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" @@ -3433,6 +3512,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" @@ -3683,7 +3763,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" @@ -4004,7 +4084,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" @@ -4906,11 +4986,11 @@ 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: > + 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: "Chế độ đánh dấu mặc định" setting_work_package_list_default_highlighted_attributes: "Thuộc tính được đánh dấu nội tuyến mặc định" diff --git a/config/locales/crowdin/zh-CN.seeders.yml b/config/locales/crowdin/zh-CN.seeders.yml index 1ee133e5684..336ac4fa27d 100644 --- a/config/locales/crowdin/zh-CN.seeders.yml +++ b/config/locales/crowdin/zh-CN.seeders.yml @@ -97,7 +97,7 @@ zh-CN: demo-project: name: 演示项目 status_explanation: 所有任务都按计划进行。相关人员均知晓各自任务。系统已完全建立。 - description: 这是对此演示 Scrum 项目目标的简短摘要。 + description: 这是对此演示项目目标的简短摘要。 news: item_0: title: 欢迎来到您的演示项目 @@ -216,7 +216,7 @@ zh-CN: scrum-project: name: Scrum 项目 status_explanation: 所有任务都按计划进行。相关人员均知晓各自任务。系统已完全建立。 - description: 这是对此演示 Scrum 项目目标的简短摘要。 + description: 这是对此演示Scrum项目目标的简短摘要。 news: item_0: title: 欢迎来到您的 Scrum 演示项目 diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml index e9fbdc05c88..61ff1fadcd7 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -88,7 +88,7 @@ zh-CN: token_caption: "要详细了解如何激活企业版,请查阅我们的[文档](docs_url)。" add_token: "上传企业版支持令牌" replace_token: "替换您当前的支持令牌" - order: "订购本地部署版的 Enterprise edition" + order: "订购本地部署的 Enterprise edition" paste: "粘贴您企业版的支持令牌" required_for_feature: "此功能仅限具激活的企业版支持令牌的订阅者使用。" enterprise_link: "如需了解详细信息,请单击此处。" @@ -112,7 +112,7 @@ zh-CN: import: title: "导入" jira: - title: "Jira 导入" + title: "Jira Migrator" description: "使用此工具从您的 Jira 实例中导入数据。您可以配置多个 Jira 主机,并选择每次导入运行要导入的内容。" errors: cannot_delete_with_imports: "无法删除具有现有导入的 Jira 主机" @@ -123,8 +123,8 @@ zh-CN: title: "Jira 配置" new: "新配置" banner: - title: "受限导入" - description: "此导入工具目前处于测试阶段,只能导入基本数据:项目、问题(名称、标题、描述、附件)、用户(名称、电子邮件地址、项目成员资格)、状态和类型。不能导入工作流、自定义字段、问题关系或权限。我们目前仅支持 Jira Server/Data Center 版本 10.x 和 11.x。目前不支持云实例。" + 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: "名称" @@ -160,7 +160,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: @@ -376,20 +376,24 @@ zh-CN:

您好!

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

谢谢

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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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: "默认转换" @@ -1485,6 +1489,9 @@ zh-CN: dependencies: "依赖项" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "项目" "import/jira": @@ -1493,7 +1500,7 @@ zh-CN: personal_access_token: "个人访问令牌" "import/jira_open_project_reference": jira: "Jira" - jira_import: "Jira 导入" + jira_import: "Jira Migrator" announcements: show_until: "显示截止日期" attachment: @@ -1571,7 +1578,7 @@ zh-CN: page: "页" row_count: "行数" column_count: "列数" - widgets: "微件" + widgets: "小部件" journal: notes: "备注" cause_type: "Cause 类型" @@ -3297,6 +3304,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: @@ -3359,6 +3371,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: "指定人" @@ -3390,6 +3468,7 @@ zh-CN: invalid_filter: "无效的通知过滤器" label_accessibility: "辅助功能" label_account: "帐户" + label_actions: "操作" label_active: "激活" label_activate_user: "激活用户" label_active_in_new_projects: "新项目动态" @@ -3430,6 +3509,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: "归档项目" @@ -3680,7 +3760,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: "最近一次活动" @@ -3919,7 +3999,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} 超时" @@ -4667,7 +4747,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: "如果您在配置新文件存储方面需要帮助,请查看文档:" @@ -4881,7 +4961,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: "计算 完成% 层次结构总数" @@ -4899,11 +4979,11 @@ 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: > + 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: "默认突出显示模式" setting_work_package_list_default_highlighted_attributes: "默认内联突出显示属性" @@ -5320,7 +5400,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 升级您的企业版计划以添加额外用户。 + 您达到了用户限制(%{current}/%{max}活跃用户)。 请联系sales@openproject.com以升级您的Enterprise edition计划并添加其他用户。 warning_protocol_mismatch_html: > warning_bar: diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index 905acd8ce16..4d2fdd77282 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -112,7 +112,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" @@ -123,8 +123,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" @@ -160,7 +160,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: @@ -376,20 +376,24 @@ zh-TW:

您好,

已建立新專案: projectValue:name

謝謝

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 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: 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: other: "... %{count} more projects" button_autofix: Autofix and save @@ -402,7 +406,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: "預設轉換" @@ -1485,6 +1489,9 @@ zh-TW: dependencies: "依賴套件" activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1493,7 +1500,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: @@ -3297,6 +3304,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: @@ -3359,6 +3371,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: "執行者" @@ -3390,6 +3468,7 @@ zh-TW: invalid_filter: "無效的通知過濾器" label_accessibility: "輔助功能" label_account: "帳號" + label_actions: "操作" label_active: "啟用" label_activate_user: "啟動使用者" label_active_in_new_projects: "在新專案中啟用" @@ -3430,6 +3509,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: "封存專案" @@ -3615,7 +3695,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: "資料夾" @@ -3629,8 +3709,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}" @@ -3642,7 +3722,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" @@ -3680,7 +3760,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: "最後的活動" @@ -3694,7 +3774,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: "清單" @@ -4902,11 +4982,11 @@ 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: > + 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: "預設顯示模式" setting_work_package_list_default_highlighted_attributes: "預設顯眼屬性" 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..a8cd0b52cf4 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", @@ -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", @@ -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/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 `