diff --git a/.buildpacks b/.buildpacks index c3c91f1aac5..f5cf87bf7ed 100644 --- a/.buildpacks +++ b/.buildpacks @@ -1,2 +1,2 @@ https://github.com/heroku/heroku-buildpack-nodejs.git#v315 -https://github.com/pkgr/heroku-buildpack-ruby.git#v327-1 +https://github.com/pkgr/heroku-buildpack-ruby.git#v356-1 diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml index bfb56a7c6d3..761aeeb067b 100644 --- a/.github/workflows/cla.yml +++ b/.github/workflows/cla.yml @@ -21,7 +21,10 @@ jobs: if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target' uses: contributor-assistant/github-action@v2.6.1 env: + # https://github.com/contributor-assistant/github-action?tab=readme-ov-file#environmental-variables + # Built-in GitHub token to make the API calls for interacting with GitHub. Does not need to be specified the secrets store. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # Access token for repository where the signatures are stored (see below for remote-repository-name) PERSONAL_ACCESS_TOKEN: ${{ secrets.OPENPROJECTCI_GH_LEGAL_TOKEN }} with: path-to-signatures: "contributor-license-agreement/signatures/version1.json" diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index 861d79fd3dc..03f3c5ae822 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -76,12 +76,14 @@ jobs: crowdin_branch_name: ${{ steps.vars.outputs.crowdin_branch }} # Dont create a PR for the updated translations push_translations: false + user: auto env: OPENPROJECT_CROWDIN_PROJECT: ${{ secrets.OPENPROJECT_CROWDINV2_PROJECT }} OPENPROJECT_CROWDIN_API_KEY: ${{ secrets.OPENPROJECT_CROWDINV2_API_KEY }} - name: "Fix root key in Portuguese crowdin translation files" - run: | - script/i18n/fix_crowdin_pt_language_root_key + run: script/i18n/fix_crowdin_pt_language_root_key + - name: "Rewrite crowdin translation files using ruby yaml library" + run: script/i18n/rewrite_crowdin_yml_files - name: "Commit translations" env: BRANCH: ${{ matrix.branch }} diff --git a/.rubocop.yml b/.rubocop.yml index 1815a2c11d1..ae173ff1fe2 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" @@ -186,6 +190,9 @@ Rails/FindEach: - limit - select - lock + Exclude: + - "spec/**/*" + - "modules/**/spec/**/*" # The http verbs in Rack::Test do not accept named parameters (params: params) Rails/HttpPositionalArguments: @@ -197,11 +204,13 @@ Rails/I18nLocaleAssignment: Enabled: true Exclude: - "spec/**/*.rb" + - "modules/*/spec/**/*.rb" Rails/I18nLocaleTexts: Enabled: true Exclude: - "spec/**/*.rb" + - "modules/*/spec/**/*.rb" # We have config.active_record.belongs_to_required_by_default = false , # which means, we do have to declare presence validators on belongs_to relations. @@ -215,6 +224,9 @@ Rails/RequireDependency: # Require save! to prevent saving without validation when saving outside of a condition. Rails/SaveBang: Enabled: true + Exclude: + - "spec/**/*" + - "modules/**/spec/**/*" # There are valid cases in which to use methods like: # * update_all @@ -321,6 +333,7 @@ RSpec/SpecFilePathFormat: CustomTransform: OpenIDConnect: openid_connect OAuthClients: oauth_clients + XWikiProviders: xwiki_providers EnforcedInflector: active_support IgnoreMethods: true @@ -471,6 +484,11 @@ Style/Proc: Style/RaiseArgs: Enabled: false +Style/RescueModifier: + Exclude: + - "spec/**/*" + - "modules/**/spec/**/*" + Style/RegexpLiteral: Enabled: false 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..fedf29eb877 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" @@ -72,7 +72,7 @@ gem "awesome_nested_set", "~> 3.9.0" gem "closure_tree", "~> 9.6.1" gem "rubytree", "~> 2.2.0" -gem "addressable", "~> 2.8.9" +gem "addressable", "~> 2.9.0" # Remove whitespace from model input gem "auto_strip_attributes", "~> 2.5" @@ -87,7 +87,7 @@ gem "htmldiff" gem "stringex", "~> 2.8.5" # CommonMark markdown parser with GFM extension -gem "commonmarker", "~> 2.7.0" +gem "commonmarker", "~> 2.8.0" # HTML pipeline for transformations on text formatter output # such as sanitization or additional features @@ -123,11 +123,11 @@ gem "sys-filesystem", "~> 1.5.0", require: false gem "bcrypt", "~> 3.1.22" -gem "multi_json", "~> 1.19.0" +gem "multi_json", "~> 1.20.0" gem "oj", "~> 3.16.16" gem "daemons" -gem "good_job", "~> 4.13.3" # update should be done manually in sync with saas-openproject version. +gem "good_job", "~> 4.14.2" # update should be done manually in sync with saas-openproject version. gem "rack-protection", "~> 3.2.0" @@ -161,7 +161,7 @@ gem "ttfunk", "~> 1.7.0" # remove after https://github.com/prawnpdf/prawn/issues # prawn implicitly depends on matrix gem no longer in ruby core with 3.1 gem "matrix", "~> 0.4.3" -gem "mcp", "~> 0.8.0" +gem "mcp", "~> 0.10.0" 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" @@ -227,7 +227,7 @@ gem "dry-validation" gem "store_attribute", "~> 2.0" # Appsignal integration -gem "appsignal", "~> 4.7", require: false +gem "appsignal", "~> 4.8", require: false # Yabeda integration gem "yabeda-activerecord" @@ -236,11 +236,11 @@ gem "yabeda-puma-plugin" gem "yabeda-rails" # opentelemetry -gem "opentelemetry-exporter-otlp", "~> 0.32.0", require: false +gem "opentelemetry-exporter-otlp", "~> 0.33.0", require: false gem "opentelemetry-instrumentation-all", "~> 0.91.0", require: false gem "opentelemetry-sdk", "~> 1.10", require: false -gem "view_component", "~> 4.5.0" +gem "view_component", "~> 4.6.0" # Lookbook gem "lookbook", "2.3.14" @@ -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" @@ -334,9 +334,6 @@ group :development do gem "spring-commands-rubocop" gem "colored2" - - # git hooks manager - gem "lefthook", require: false end group :development, :test do @@ -356,6 +353,9 @@ group :development, :test do # https://github.com/puma/puma/issues/2835#issuecomment-2302133927 gem "byebug" + # Unreleased fix of readline dependency of pry: https://github.com/pry/pry/pull/2366 + # Once this gets released, the specific dev dependency on pry can be removed + gem "pry", github: "pry/pry", ref: "135640262879544c6bfecbf3e78511289bfe956c" gem "pry-byebug", "~> 3.12.0", platforms: [:mri] gem "pry-rails", "~> 0.3.6" gem "pry-rescue", "~> 1.6.0" @@ -387,7 +387,7 @@ end gem "bootsnap", "~> 1.23.0", require: false # API gems -gem "grape", "~> 3.1.1" +gem "grape", "~> 3.2.0" gem "grape_logging", "~> 3.0.0" gem "roar", "~> 1.2.0" diff --git a/Gemfile.lock b/Gemfile.lock index 970a9402eca..cb7087239a1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -61,6 +61,16 @@ GIT parallel_tests (>= 3.3.0, < 5) rspec (>= 3.10) +GIT + remote: https://github.com/pry/pry.git + revision: 135640262879544c6bfecbf3e78511289bfe956c + ref: 135640262879544c6bfecbf3e78511289bfe956c + specs: + pry (0.16.0) + coderay (~> 1.1) + method_source (~> 1.0) + reline (>= 0.6.0) + PATH remote: modules/auth_plugins specs: @@ -77,7 +87,6 @@ PATH remote: modules/avatars specs: openproject-avatars (1.0.0) - fastimage (>= 2.3, < 2.5) gravatar_image_tag (~> 1.2.0) PATH @@ -203,7 +212,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 +237,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 +272,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 +311,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) @@ -326,7 +335,7 @@ GEM activesupport (>= 6.1) acts_as_tree (2.9.1) activerecord (>= 3.0.0) - addressable (2.8.9) + addressable (2.9.0) public_suffix (>= 2.0.2, < 8.0) aes_key_wrap (1.1.0) afm (1.0.0) @@ -337,7 +346,7 @@ GEM android_key_attestation (0.3.0) anyway_config (2.8.0) ruby-next-core (~> 1.0) - appsignal (4.8.3) + appsignal (4.8.4) logger rack (>= 2.0.0) ast (2.4.3) @@ -347,8 +356,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.1238.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 +365,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.219.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 +400,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) @@ -424,7 +433,7 @@ GEM carrierwave_direct (3.0.0) carrierwave (>= 2.2.0) fog-aws - cbor (0.5.10.1) + cbor (0.5.10.2) cgi (0.5.1) childprocess (5.1.0) logger (~> 1.5) @@ -438,13 +447,13 @@ GEM descendants_tracker (~> 0.0.1) color_conversion (0.1.2) colored2 (4.0.3) - commonmarker (2.7.0-aarch64-linux) - commonmarker (2.7.0-aarch64-linux-musl) - commonmarker (2.7.0-arm-linux) - commonmarker (2.7.0-arm64-darwin) - commonmarker (2.7.0-x86_64-darwin) - commonmarker (2.7.0-x86_64-linux) - commonmarker (2.7.0-x86_64-linux-musl) + commonmarker (2.8.1-aarch64-linux) + commonmarker (2.8.1-aarch64-linux-musl) + commonmarker (2.8.1-arm-linux) + commonmarker (2.8.1-arm64-darwin) + commonmarker (2.8.1-x86_64-darwin) + commonmarker (2.8.1-x86_64-linux) + commonmarker (2.8.1-x86_64-linux-musl) compare-xml (0.66) nokogiri (~> 1.8) concurrent-ruby (1.3.6) @@ -453,7 +462,7 @@ GEM cose (1.3.1) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - counter_culture (3.12.2) + counter_culture (3.13.0) activerecord (>= 4.2) activesupport (>= 4.2) crack (1.0.1) @@ -549,7 +558,7 @@ GEM activemodel equivalent-xml (0.6.0) nokogiri (>= 1.4.3) - erb (6.0.2) + erb (6.0.3) erb_lint (0.9.0) activesupport better_html (>= 2.0.1) @@ -564,7 +573,7 @@ GEM tzinfo eventmachine (1.2.7) eventmachine_httpserver (0.2.1) - excon (1.4.0) + excon (1.4.2) logger factory_bot (6.5.6) activesupport (>= 6.1.0) @@ -579,21 +588,20 @@ GEM faraday (>= 1, < 3) faraday-net_http (3.4.2) net-http (~> 0.5) - fastimage (2.4.1) - ferrum (0.17.1) + ferrum (0.17.2) addressable (~> 2.5) base64 (~> 0.2) 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) @@ -625,7 +633,7 @@ GEM glob (0.4.0) globalid (1.3.0) activesupport (>= 6.1) - good_job (4.13.3) + good_job (4.14.2) activejob (>= 6.1.0) activerecord (>= 6.1.0) concurrent-ruby (>= 1.3.1) @@ -646,25 +654,25 @@ GEM base64 (~> 0.2) faraday (>= 1.0, < 3.a) google-logging-utils (0.2.0) - google-protobuf (4.34.0) + google-protobuf (4.34.1) bigdecimal rake (~> 13.3) - google-protobuf (4.34.0-aarch64-linux-gnu) + google-protobuf (4.34.1-aarch64-linux-gnu) bigdecimal rake (~> 13.3) - google-protobuf (4.34.0-aarch64-linux-musl) + google-protobuf (4.34.1-aarch64-linux-musl) bigdecimal rake (~> 13.3) - google-protobuf (4.34.0-arm64-darwin) + google-protobuf (4.34.1-arm64-darwin) bigdecimal rake (~> 13.3) - google-protobuf (4.34.0-x86_64-darwin) + google-protobuf (4.34.1-x86_64-darwin) bigdecimal rake (~> 13.3) - google-protobuf (4.34.0-x86_64-linux-gnu) + google-protobuf (4.34.1-x86_64-linux-gnu) bigdecimal rake (~> 13.3) - google-protobuf (4.34.0-x86_64-linux-musl) + google-protobuf (4.34.1-x86_64-linux-musl) bigdecimal rake (~> 13.3) googleapis-common-protos-types (1.22.0) @@ -677,8 +685,8 @@ GEM multi_json (~> 1.11) os (>= 0.9, < 2.0) signet (>= 0.16, < 2.a) - grape (3.1.1) - activesupport (>= 7.1) + grape (3.2.0) + activesupport (>= 7.2) dry-configurable dry-types (>= 1.1) mustermann-grape (~> 1.1.0) @@ -703,7 +711,7 @@ GEM htmlentities (4.3.4) http-2 (1.1.3) http_parser.rb (0.8.1) - httpx (1.7.4) + httpx (1.7.6) http-2 (>= 1.1.3) i18n (1.14.8) concurrent-ruby (~> 1.0) @@ -744,9 +752,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 @@ -774,7 +782,6 @@ GEM addressable (~> 2.8) childprocess (~> 5.0) logger (~> 1.6) - lefthook (2.1.4) letter_opener (1.10.0) launchy (>= 2.2, < 4) letter_opener_web (3.0.0) @@ -819,9 +826,9 @@ GEM net-pop net-smtp marcel (1.0.4) - markly (0.15.2) + markly (0.16.0) matrix (0.4.3) - mcp (0.8.0) + mcp (0.10.0) json-schema (>= 4.1) messagebird-rest (5.0.0) jwt (< 4) @@ -831,17 +838,16 @@ GEM mime-types (3.7.0) logger mime-types-data (~> 3.2025, >= 3.2025.0507) - mime-types-data (3.2026.0317) + mime-types-data (3.2026.0407) mini_magick (5.3.1) logger mini_mime (1.1.5) - minitest (6.0.2) + minitest (6.0.4) drb (~> 2.0) prism (~> 1.5) msgpack (1.8.0) - multi_json (1.19.1) - mustermann (3.0.4) - ruby2_keywords (~> 0.0.1) + multi_json (1.20.1) + mustermann (3.1.0) mustermann-grape (1.1.0) mustermann (>= 1.0.0) net-http (0.9.1) @@ -875,7 +881,7 @@ GEM racc (~> 1.4) nokogiri (1.19.2-x86_64-linux-musl) racc (~> 1.4) - oj (3.16.16) + oj (3.16.17) bigdecimal (>= 3.0) ostruct (>= 0.2) okcomputer (1.19.1) @@ -912,11 +918,11 @@ GEM openssl (4.0.1) openssl-signature_algorithm (1.3.0) openssl (> 2.0) - opentelemetry-api (1.8.0) + opentelemetry-api (1.9.0) logger - opentelemetry-common (0.23.0) + opentelemetry-common (0.24.0) opentelemetry-api (~> 1.0) - opentelemetry-exporter-otlp (0.32.0) + opentelemetry-exporter-otlp (0.33.0) google-protobuf (>= 3.18) googleapis-common-protos-types (~> 1.3) opentelemetry-api (~> 1.1) @@ -1027,7 +1033,7 @@ GEM opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-lmdb (0.25.0) opentelemetry-instrumentation-base (~> 0.25) - opentelemetry-instrumentation-mongo (0.25.0) + opentelemetry-instrumentation-mongo (0.25.1) opentelemetry-instrumentation-base (~> 0.25) opentelemetry-instrumentation-mysql2 (0.33.0) opentelemetry-helpers-mysql @@ -1077,31 +1083,32 @@ GEM opentelemetry-helpers-sql-processor opentelemetry-instrumentation-base (~> 0.25) opentelemetry-semantic_conventions (>= 1.8.0) - opentelemetry-registry (0.4.0) + opentelemetry-registry (0.5.0) opentelemetry-api (~> 1.1) - opentelemetry-sdk (1.10.0) + opentelemetry-sdk (1.11.0) + logger opentelemetry-api (~> 1.1) opentelemetry-common (~> 0.20) opentelemetry-registry (~> 0.2) opentelemetry-semantic_conventions - opentelemetry-semantic_conventions (1.36.0) + opentelemetry-semantic_conventions (1.37.0) opentelemetry-api (~> 1.0) optimist (3.2.1) os (1.1.4) ostruct (0.6.3) ox (2.14.23) bigdecimal (>= 3.0) - pagy (43.4.1) + pagy (43.5.1) json uri yaml paper_trail (17.0.0) activerecord (>= 7.1) request_store (~> 1.4) - parallel (1.27.0) + parallel (2.0.1) 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) @@ -1168,10 +1175,6 @@ GEM bigdecimal logger rb_sys (~> 0.9.124) - pry (0.16.0) - coderay (~> 1.1) - method_source (~> 1.0) - reline (>= 0.6.0) pry-byebug (3.12.0) byebug (~> 13.0) pry (>= 0.13, < 0.17) @@ -1199,7 +1202,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 +1230,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 +1258,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) @@ -1265,12 +1268,13 @@ GEM tsort (>= 0.2) zeitwerk (~> 2.6) rainbow (3.1.1) - rake (13.3.1) + rake (13.4.2) rake-compiler-dock (1.11.0) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rb_sys (0.9.124) + rb_sys (0.9.126) + json (>= 2) rake-compiler-dock (= 1.11.0) rbtrace (0.5.3) ffi (>= 1.0.6) @@ -1281,13 +1285,13 @@ GEM erb psych (>= 4.0.0) tsort - recaptcha (5.21.1) + recaptcha (5.21.2) redcarpet (3.6.1) redis (5.4.1) 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,12 +1335,11 @@ GEM rspec-support (3.13.7) rspec-wait (1.0.2) rspec (>= 3.4) - rubocop (1.85.1) + rubocop (1.86.1) json (~> 2.3) language_server-protocol (~> 3.17.0.2) lint_roller (~> 1.1.0) - mcp (~> 0.6) - parallel (~> 1.10) + parallel (>= 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 2.9.3, < 3.0) @@ -1352,7 +1355,7 @@ GEM rubocop-factory_bot (2.28.0) lint_roller (~> 1.1) rubocop (~> 1.72, >= 1.72.1) - rubocop-openproject (0.3.0) + rubocop-openproject (0.4.0) rubocop rubocop-performance (1.26.1) lint_roller (~> 1.1) @@ -1388,7 +1391,6 @@ GEM ruby-vips (2.3.0) ffi (~> 1.12) logger - ruby2_keywords (0.0.5) rubytree (2.2.0) json (~> 2.0, > 2.9) rubyzip (2.4.1) @@ -1400,9 +1402,9 @@ GEM scimitar (2.15.0) rails (>= 7.0) securerandom (0.4.1) - selenium-devtools (0.145.0) + selenium-devtools (0.147.0) selenium-webdriver (~> 4.2) - selenium-webdriver (4.41.0) + selenium-webdriver (4.43.0) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -1436,7 +1438,7 @@ GEM actionpack (>= 6.1) activesupport (>= 6.1) sprockets (>= 3.0.0) - ssrf_filter (1.3.0) + ssrf_filter (1.5.0) stackprof (0.2.28) statesman (13.1.0) store_attribute (2.1.1) @@ -1455,11 +1457,11 @@ 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.1) text-hyphen (1.5.0) thor (1.5.0) thread_safe (0.3.6) - timecop (0.9.10) + timecop (0.9.11) timeout (0.6.1) tpm-key_attestation (0.14.1) bindata (~> 2.4) @@ -1491,7 +1493,7 @@ GEM public_suffix vcr (6.4.0) vernier (1.10.0) - view_component (4.5.0) + view_component (4.6.0) actionview (>= 7.1.0) activesupport (>= 7.1.0) concurrent-ruby (~> 1) @@ -1552,7 +1554,7 @@ GEM railties yabeda (~> 0.8) yaml (0.4.0) - yard (0.9.38) + yard (0.9.42) zeitwerk (2.7.5) PLATFORMS @@ -1576,13 +1578,13 @@ DEPENDENCIES activerecord-session_store (~> 2.2.0) acts_as_list (~> 1.2.6) acts_as_tree (~> 2.9.0) - addressable (~> 2.8.9) + addressable (~> 2.9.0) airbrake (~> 13.0.0) - appsignal (~> 4.7) + appsignal (~> 4.8) 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) @@ -1598,7 +1600,7 @@ DEPENDENCIES climate_control closure_tree (~> 9.6.1) colored2 - commonmarker (~> 2.7.0) + commonmarker (~> 2.8.0) compare-xml (~> 0.66) connection_pool (~> 3.0.2) costs! @@ -1623,21 +1625,21 @@ 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) fuubar (~> 2.5.0) globalid (~> 1.3) - good_job (~> 4.13.3) + good_job (~> 4.14.2) google-apis-gmail_v1 googleauth - grape (~> 3.1.1) + grape (~> 3.2.0) grape_logging (~> 3.0.0) 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) @@ -1648,7 +1650,6 @@ DEPENDENCIES json_spec (~> 1.1.4) ladle launchy (~> 3.1.0) - lefthook letter_opener_web listen (~> 3.10.0) lograge (~> 0.14.0) @@ -1656,11 +1657,11 @@ DEPENDENCIES mail (= 2.9.0) markly (~> 0.15) matrix (~> 0.4.3) - mcp (~> 0.8.0) + mcp (~> 0.10.0) md_to_pdf! meta-tags (~> 2.23.0) mini_magick (~> 5.3.0) - multi_json (~> 1.19.0) + multi_json (~> 1.20.0) my_page! net-ldap (~> 0.20.0) nokogiri (~> 1.19.2) @@ -1697,7 +1698,7 @@ DEPENDENCIES openproject-webhooks! openproject-wikis! openproject-xls_export! - opentelemetry-exporter-otlp (~> 0.32.0) + opentelemetry-exporter-otlp (~> 0.33.0) opentelemetry-instrumentation-all (~> 0.91.0) opentelemetry-sdk (~> 1.10) overviews! @@ -1709,6 +1710,7 @@ DEPENDENCIES pg (~> 1.6.2) plaintext (~> 0.3.7) prawn (~> 2.4) + pry! pry-byebug (~> 3.12.0) pry-rails (~> 0.3.6) pry-rescue (~> 1.6.0) @@ -1722,7 +1724,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 +1773,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) @@ -1781,7 +1783,7 @@ DEPENDENCIES validate_url vcr vernier - view_component (~> 4.5.0) + view_component (~> 4.6.0) warden (~> 1.2) warden-basic_auth (~> 0.2.1) webmock (~> 3.26) @@ -1794,44 +1796,44 @@ 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 + addressable (2.9.0) sha256=7fdf6ac3660f7f4e867a0838be3f6cf722ace541dd97767fa42bc6cfa980c7af aes_key_wrap (1.1.0) sha256=b935f4756b37375895db45669e79dfcdc0f7901e12d4e08974d5540c8e0776a5 afm (1.0.0) sha256=5bd4d6f6241e7014ef090985ec6f4c3e9745f6de0828ddd58bc1efdd138f4545 airbrake (13.0.5) sha256=901f5074c25d5ef77ed87f5bde7a28400a7324f5d7013a8a12d07e0099cc31b6 airbrake-ruby (6.2.2) sha256=293e34fb36e763e1b6d67ab584cce7c5b6fe9eea1a70c26d8c13c0f5d7de2fbc android_key_attestation (0.3.0) sha256=467eb01a99d2bb48ef9cf24cc13712669d7056cba5a52d009554ff037560570b anyway_config (2.8.0) sha256=f6797a7231f81202dcd3d0c07284e836e45713e761d320180348b13a5c7c9306 - appsignal (4.8.3) sha256=aa0ea5ffd39fe7530c56a6eb6efda60825ab061ef31376126cae93b009844dd7 + appsignal (4.8.4) sha256=5c708a41c2913383d19ae581140da1904dbf642b974220d6c1bc4c7fdb1cf667 ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383 attr_required (1.0.2) sha256=f0ebfc56b35e874f4d0ae799066dbc1f81efefe2364ca3803dc9ea6a4de6cb99 auto_strip_attributes (2.6.0) sha256=a7e2e0cf744de2bcd947fd68014220702bcc88c81274c1cd9ce6f7316aae39b0 awesome_nested_set (3.9.0) sha256=3ce99e816550f97f4de118e621630070aacf24928b920fe4a68846578a8daaed aws-eventstream (1.4.0) sha256=116bf85c436200d1060811e6f5d2d40c88f65448f2125bc77ffce5121e6e183b - aws-partitions (1.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.1238.0) sha256=fa3d1bdea6d7e7619e8cee22ebce8a569d2119296d3ec8c5f9b9b7c81fb0602c + aws-sdk-core (3.244.0) sha256=3e458c078b0c5bdee95bc370c3a483374b3224cf730c1f9f0faf849a5d9a18ea + aws-sdk-kms (1.123.0) sha256=d405f37e82f8fa32045ca8980be266c0b45b37aaf2012afe0254321a1e811f20 + aws-sdk-s3 (1.219.0) sha256=6a755d7377978525758b3c29185ca6a10128ce2b07555ca37c4549de10c2f1c7 + 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 +1842,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 @@ -1853,7 +1855,7 @@ CHECKSUMS capybara_accessible_selectors (0.15.0) carrierwave (2.2.6) sha256=cd9b6108fc7544e97e7fbcc561bd319a09f23c96816fdd0df8f2f45ffdc0dac3 carrierwave_direct (3.0.0) sha256=da4105ec7beea2687817b95ad95181be3d657248ec1cb5a0e5c35a45b176fc2f - cbor (0.5.10.1) sha256=79cdf79f18dcd9ee97e0b849c6d573e5a2e3ddc1954d180f384d6ed2612b6df0 + cbor (0.5.10.2) sha256=df5104f7a62c881123e6505441b1e276208be1771540c2cc3b1de8a210a7c52c cgi (0.5.1) sha256=e93fcafc69b8a934fe1e6146121fa35430efa8b4a4047c4893764067036f18e9 childprocess (5.1.0) sha256=9a8d484be2fd4096a0e90a0cd3e449a05bc3aa33f8ac9e4d6dcef6ac1455b6ec climate_control (1.2.0) sha256=36b21896193fa8c8536fa1cd843a07cf8ddbd03aaba43665e26c53ec1bd70aa5 @@ -1862,20 +1864,20 @@ CHECKSUMS coercible (1.0.0) sha256=5081ad24352cc8435ce5472bc2faa30260c7ea7f2102cc6a9f167c4d9bffaadc color_conversion (0.1.2) sha256=99bea5fa412e1527a11389975aa6ad445ff8528ebae202c11d08c45ea2b94c96 colored2 (4.0.3) sha256=63e1038183976287efc43034f5cca17fb180b4deef207da8ba78d051cbce2b37 - commonmarker (2.7.0-aarch64-linux) sha256=a15a47bb901f4cfcb3b4fe54d0daf91ec46e70b6e9704c67abec0ba30064b2dc - commonmarker (2.7.0-aarch64-linux-musl) sha256=fa08eb19ffc3cf2f7132d86d74622338a0c35b480b23814a7ae63deaeafb56d3 - commonmarker (2.7.0-arm-linux) sha256=85274d47feca40bd574ac49c2959a015464936f7f830c1e2918ee147d5be1a13 - commonmarker (2.7.0-arm64-darwin) sha256=ef00f7efef4822e8e7f88be88920a319f71a251e894e02fbc82f6db5d4291db3 - commonmarker (2.7.0-x86_64-darwin) sha256=a8f5928f36138347b5e672d080b8f76dc430c6207566128b117cdd02f3e48c34 - commonmarker (2.7.0-x86_64-linux) sha256=53243eeb30e4ddb6a474672597ff7e59334e55a7653126bb87e65e6ab2629a4c - commonmarker (2.7.0-x86_64-linux-musl) sha256=e4f5c06975f37a405bd157d276609e1c02502d2fbaf576f6e3616fba2e7b5662 + commonmarker (2.8.1-aarch64-linux) sha256=f855599cc6855f4137d72dcacae9571451075afe6e6c8522eba353df9b81d0bf + commonmarker (2.8.1-aarch64-linux-musl) sha256=bbc2b3d361403431a5aac737dc86997d3b2843276ef395e7499cf17ad48e1b2c + commonmarker (2.8.1-arm-linux) sha256=ede48564a9c2e29e003361fd7b0b158b3b85bcbd5a15b963fa2b3c78eef3993f + commonmarker (2.8.1-arm64-darwin) sha256=cf75760122d6e1c3f8626b5a6911636d2e0aaa26fa8dd377fea440a49c854b3e + commonmarker (2.8.1-x86_64-darwin) sha256=75afa559c1f1cc201afbb81291a2f2fc77b2941347deaa8d564d008ba04ecb18 + commonmarker (2.8.1-x86_64-linux) sha256=c8f2e903c6ee4e2c7e280aa8b49ef37c53202d388e3a2ee06dfdccc8c6fb634f + commonmarker (2.8.1-x86_64-linux-musl) sha256=9b6305ce47e0d444b77dabf1762bf76085c2f7963b63c25c02ec36edcf06c785 compare-xml (0.66) sha256=e21aa5c0f69ef1177eced997c688fd4df989084e74a1b612257af32e1dd05319 concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a cookiejar (0.3.4) sha256=11b16acfc4baf7a0f463c21a6212005e04e25f5554d4d9f24d97f3492dfda0df cose (1.3.1) sha256=d5d4dbcd6b035d513edc4e1ab9bc10e9ce13b4011c96e3d1b8fe5e6413fd6de5 costs (1.0.0) - counter_culture (3.12.2) sha256=26358dbb15e2f1f7e60f2c11976e37517a74ffd34eb5ef8d7269862b9ff8aadd + counter_culture (3.13.0) sha256=a2cde20642ddd27aec9ff0c09b73fa5b4fd729da368079e67c177bface3148bf crack (1.0.1) sha256=ff4a10390cd31d66440b7524eb1841874db86201d5b70032028553130b6d4c7e crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d css_parser (2.0.0) sha256=af5c759a127b125b635006a6c6c2e05b96a1ebdeec21b3c415fd5f09ec714a0a @@ -1911,7 +1913,7 @@ CHECKSUMS em-synchrony (1.0.6) sha256=6e7470a684d9bbc00d61d552911b65711540bd89e95c157156f5aacdd6f306ca email_validator (2.2.4) sha256=5ab238095bec7aef9389f230e9e0c64c5081cdf91f19d6c5cecee0a93af20604 equivalent-xml (0.6.0) sha256=8919761efa848ad0846369ff8be1f646b17e5061698c4867b09829000cc3f487 - erb (6.0.2) sha256=9fe6264d44f79422c87490a1558479bd0e7dad4dd0e317656e67ea3077b5242b + erb (6.0.3) sha256=e43685a8a0a0ea6a924871b2162e8953ef73147ce46b75b36d1f6774fd286e91 erb_lint (0.9.0) sha256=dfb5e40ad839e8d1f0d56ca85ec9a7ac4c9cd966ec281138282f35b323ca7c31 erblint-github (1.0.1) sha256=9f28f7dc381a0dc68a0093ef7af3424ed9d2bb2b3e39bdc8e8cba86a0d31f2d0 erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9 @@ -1919,22 +1921,21 @@ CHECKSUMS et-orbi (1.4.0) sha256=6c7e3c90779821f9e3b324c5e96fda9767f72995d6ae435b96678a4f3e2de8bc eventmachine (1.2.7) sha256=994016e42aa041477ba9cff45cbe50de2047f25dd418eba003e84f0d16560972 eventmachine_httpserver (0.2.1) sha256=5db5e8a23754204d43592e5fcc2160457c57c870babe6307c4e61fc95019b809 - excon (1.4.0) sha256=5d2bc9d2c79511a562e7fcac77cc7a40acd9cebcc55b80e537975ad8187f2924 + excon (1.4.2) sha256=32d8d8eda619717d9b8043b4675e096fb5c2139b080e2ad3b267f88c545aaa35 factory_bot (6.5.6) sha256=12beb373214dccc086a7a63763d6718c49769d5606f0501e0a4442676917e077 factory_bot_rails (6.5.1) sha256=d3cc4851eae4dea8a665ec4a4516895045e710554d2b5ac9e68b94d351bc6d68 faraday (2.14.1) sha256=a43cceedc1e39d188f4d2cdd360a8aaa6a11da0c407052e426ba8d3fb42ef61c faraday-follow_redirects (0.5.0) sha256=5cde93c894b30943a5d2b93c2fe9284216a6b756f7af406a1e55f211d97d10ad faraday-net_http (3.4.2) sha256=f147758260d3526939bf57ecf911682f94926a3666502e24c69992765875906c - fastimage (2.4.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 + ferrum (0.17.2) sha256=2c2540a850b211a46f4d81de21bfd62048f507e4c327d1807225c3823c17e6ee + 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 @@ -1947,21 +1948,21 @@ CHECKSUMS fuubar (2.5.1) sha256=b272a7804b282661c7fab583a3764f92543cb482c365ae39c685cd218fdd4880 glob (0.4.0) sha256=893dc9e2d24abe13dda907ce0cda576f680ff382f2a6cf9e543f98ecbe29238c globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11 - good_job (4.13.3) sha256=37478710dddd2630ed055f2159111ce6f7f14482d8ccb412142ee04674151e2e + good_job (4.14.2) sha256=f38f164346aee724bbfbdaed73e1a0bd382cc6354146029e0adcc619245ab6d1 google-apis-core (1.0.2) sha256=ba4579aaadc902d6cc7bc8db88f566ab00f5e31ea87ab41e9f9a032c470f2629 google-apis-gmail_v1 (0.47.0) sha256=3064434b6da55b85e2828ce4bb0f4d04e8cfd187a4ab262ceb1dcb01f98e49ef google-cloud-env (2.3.1) sha256=0faac01eb27be78c2591d64433663b1a114f8f7af55a4f819755426cac9178e7 google-logging-utils (0.2.0) sha256=675462b4ea5affa825a3442694ca2d75d0069455a1d0956127207498fca3df7b - google-protobuf (4.34.0) sha256=bffaea30fbe2807c80667a78953b15645b3bef62b25c10ca187e4418119be531 - google-protobuf (4.34.0-aarch64-linux-gnu) sha256=0ab8a8a97976a2265d647e69b3ff1980c89184abdaf06d36091856c5ab37cc55 - google-protobuf (4.34.0-aarch64-linux-musl) sha256=0632a86df6d320eac3b335bd779499d43ad8ee6d1f8c8494b773ed5d3d5c6ab4 - google-protobuf (4.34.0-arm64-darwin) sha256=f83967a8095a9da676b79ba372c58fef2ca3878428bd40febfce65b3752c90d1 - google-protobuf (4.34.0-x86_64-darwin) sha256=4a5b67281993345adca54bb32947f25a289597eafaa240e5b714d0a740f99321 - google-protobuf (4.34.0-x86_64-linux-gnu) sha256=bbb333fbe79c16f35a2e2154cf29f3ce26f60390dba286b339861206d5435ef9 - google-protobuf (4.34.0-x86_64-linux-musl) sha256=0b75858a388b17e73aa4176df2e722762dbc92551b7075fdc562d33c1c6de0b0 + google-protobuf (4.34.1) sha256=347181542b8d659c60f028fa3791c9cccce651a91ad27782dbc5c5e374796cdc + google-protobuf (4.34.1-aarch64-linux-gnu) sha256=f9c07607dc139c895f2792a7740fcd01cd94d4d7b0e0a939045b50d7999f0b1d + google-protobuf (4.34.1-aarch64-linux-musl) sha256=db58e5a4a492b43c6614486aea31b7fb86955b175d1d48f28ebf388f058d78a9 + google-protobuf (4.34.1-arm64-darwin) sha256=2745061f973119e6e7f3c81a0c77025d291a3caa6585a2cd24a25bbc7bedb267 + google-protobuf (4.34.1-x86_64-darwin) sha256=4dc498376e218871613589c4d872400d42ad9ae0c700bdb2606fe1c77a593075 + google-protobuf (4.34.1-x86_64-linux-gnu) sha256=87088c9fd8e47b5b40ca498fc1195add6149e941ff7e81c532a5b0b8876d4cc9 + google-protobuf (4.34.1-x86_64-linux-musl) sha256=8c0e91436fbe504ffc64f0bd621f2e69adbcce8ed2c58439d7a21117069cfdd7 googleapis-common-protos-types (1.22.0) sha256=f97492b77bd6da0018c860d5004f512fe7cd165554d7019a8f4df6a56fbfc4c7 googleauth (1.16.2) sha256=15009502e2e38af71948cda918f230e27d327f6882a1e47967a5a4664930a638 - grape (3.1.1) sha256=774f16782d917a90e69de0499dfaab571e5ad967569ac066a2b0b918af12de69 + grape (3.2.0) sha256=2aeeb020e5605f6314ce8ca8d30d90c9ee8f26bc959c5b34db7b8486764e4d2c grape_logging (3.0.0) sha256=7b62d984ce96df15d120508668debe307e6a59ac1c511f1d9b5f3b4bea793e13 gravatar_image_tag (1.2.0) sha256=eb5630fea846b711e713b934a0178fb9785f02f4eb9ced8d6faa4d537c40fdcf grids (1.0.0) @@ -1976,7 +1977,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.6) sha256=82d825abc9876a132adc3492c56a0c528478ac238dd6f74d3422ab0036c6b5c8 i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5 i18n-js (4.2.4) sha256=61390d372f8fa68c495c5907d577657e8cc3a7031f4945db1e91f935e1391355 i18n-tasks (1.1.2) sha256=4dcfba49e52a623f30661cb316cb80d84fbba5cb8c6d88ef5e02545fffa3637a @@ -1990,8 +1991,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 @@ -2000,7 +2001,6 @@ CHECKSUMS ladle (1.0.1) sha256=e8586964108c798d48bf57d2a65bd5602e8e5223a176b6602a0fb36c0bda90dc language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc launchy (3.1.1) sha256=72b847b5cc961589dde2c395af0108c86ff0119f42d4648d25b5440ebb10059e - lefthook (2.1.4) sha256=b3c5bba86911e85b239fea3861ba8c74740fc084ba9ac79dba3fe79267572d6a letter_opener (1.10.0) sha256=2ff33f2e3b5c3c26d1959be54b395c086ca6d44826e8bf41a14ff96fdf1bdbb2 letter_opener_web (3.0.0) sha256=3f391efe0e8b9b24becfab5537dfb17a5cf5eb532038f947daab58cb4b749860 lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87 @@ -2012,21 +2012,21 @@ CHECKSUMS lookbook (2.3.14) sha256=c11a693bde9915b553c4463440ad5e750829f90bff08abdb6b8610373864cd7c mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941 marcel (1.0.4) sha256=0d5649feb64b8f19f3d3468b96c680bae9746335d02194270287868a661516a4 - markly (0.15.2) sha256=65dae965d4dd4ecd997fba43b93acc0fe7dadfec6f07a748640c7a9299a8551e + markly (0.16.0) sha256=6f70d79e385b1efc9e171f74c81628826259039fe6c778e03c3924c71dac5511 matrix (0.4.3) sha256=a0d5ab7ddcc1973ff690ab361b67f359acbb16958d1dc072b8b956a286564c5b - mcp (0.8.0) sha256=ae8bd146bb8e168852866fd26f805f52744f6326afb3211e073f78a95e0c34fb + mcp (0.10.0) sha256=09b9231eb16dff75cc7b8a95817c8acfcf4d1cab8d34f350671e43e765242b57 md_to_pdf (0.2.6) messagebird-rest (5.0.0) sha256=da4cc1efba3d5e4aa021fad07426c2cb6b326ce5670da5104bb8f6056a39d59c meta-tags (2.23.0) sha256=ffe78b5bee398de4ff5ac3316f5a786049538a651643b8476def06c3acc762c1 method_source (1.1.0) sha256=181301c9c45b731b4769bc81e8860e72f9161ad7d66dd99103c9ab84f560f5c5 mime-types (3.7.0) sha256=dcebf61c246f08e15a4de34e386ebe8233791e868564a470c3fe77c00eed5e56 - mime-types-data (3.2026.0317) sha256=77f078a4d8631d52b842ba77099734b06eddb7ad339d792e746d2272b67e511b + mime-types-data (3.2026.0407) sha256=909395cf029731355136527aa11bf58ea0655ee782359ccbf32c66238a8cadb3 mini_magick (5.3.1) sha256=29395dfd76badcabb6403ee5aff6f681e867074f8f28ce08d78661e9e4a351c4 mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef - minitest (6.0.2) sha256=db6e57956f6ecc6134683b4c87467d6dd792323c7f0eea7b93f66bd284adbc3d + minitest (6.0.4) sha256=df1304664589d40f46089247fdc451f866b0ce0d7cae1457a15fc1eb7d48dca1 msgpack (1.8.0) sha256=e64ce0212000d016809f5048b48eb3a65ffb169db22238fb4b72472fecb2d732 - multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7 - mustermann (3.0.4) sha256=85fadcb6b3c6493a8b511b42426f904b7f27b282835502233dd154daab13aa22 + multi_json (1.20.1) sha256=2f3934e805cc45ef91b551a1f89d0e9191abd06a5e04a2ef09a6a036c452ca6d + mustermann (3.1.0) sha256=e73b006ffb7f743eae9303a7d6622e0dd9e1e5522718a2139c006085878768b9 mustermann-grape (1.1.0) sha256=8d258a986004c8f01ce4c023c0b037c168a9ed889cf5778068ad54398fa458c5 my_page (1.0.0) net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 @@ -2044,7 +2044,7 @@ CHECKSUMS nokogiri (1.19.2-x86_64-darwin) sha256=7d9af11fda72dfaa2961d8c4d5380ca0b51bc389dc5f8d4b859b9644f195e7a4 nokogiri (1.19.2-x86_64-linux-gnu) sha256=fa8feca882b73e871a9845f3817a72e9734c8e974bdc4fbad6e4bc6e8076b94f nokogiri (1.19.2-x86_64-linux-musl) sha256=93128448e61a9383a30baef041bf1f5817e22f297a1d400521e90294445069a8 - oj (3.16.16) sha256=3635b36128991796434f55da8decc0de236a323535adcb36fc04e6d0253c013d + oj (3.16.17) sha256=a6688f666143632a1ef11a8d80c8d631b1112733c7da698ffafa4a22a8488244 okcomputer (1.19.1) sha256=7df770e768434816d228407f0786563827cbf34cb379933578829720cb4f1e77 omniauth (1.9.2) omniauth-openid-connect (0.5.0) @@ -2082,9 +2082,9 @@ CHECKSUMS openproject-xls_export (1.0.0) openssl (4.0.1) sha256=e27974136b7b02894a1bce46c5397ee889afafe704a839446b54dc81cb9c5f7d openssl-signature_algorithm (1.3.0) sha256=a3b40b5e8276162d4a6e50c7c97cdaf1446f9b2c3946a6fa2c14628e0c957e80 - opentelemetry-api (1.8.0) sha256=3af51183daf0f56a164bc1579782245be70a40678566b9a393cbe5af28ea87c6 - opentelemetry-common (0.23.0) sha256=da721190479d57bae0ad2207468f47f3e2c3b9a91024b5bc32c9d280183eb32c - opentelemetry-exporter-otlp (0.32.0) sha256=fd4c77a07bb96919e8ff8bbd19ed96d07cac1e368d8e920af2bf2ab02bfb1ec3 + opentelemetry-api (1.9.0) sha256=d24065dd26583babd8d498d38ea35f74dfa193fb7102512e6e161649440079fb + opentelemetry-common (0.24.0) sha256=f1647b233b8ac667feeb74d66a65b702008d9ab55aae825c220b4fe2c14fa773 + opentelemetry-exporter-otlp (0.33.0) sha256=6e9ce38e393c7eb9aea3fb57b128174a0066767bf495f4fd9e63d7607e0b2ad3 opentelemetry-helpers-mysql (0.5.0) sha256=8c2a5d5428aec271a7d2e25c158d06d4d8a914143b5004305964d1fcbc176eca opentelemetry-helpers-sql (0.3.0) sha256=4bb08017d6a16dd41c4d1c53c7fd30f9c5bb691195d8b458933724627b3f37f9 opentelemetry-helpers-sql-processor (0.4.0) sha256=ec238d7a2887219bd247dc31d0eb8a1a03d414a899963b68e14bb9f4d18b23f4 @@ -2117,7 +2117,7 @@ CHECKSUMS opentelemetry-instrumentation-httpx (0.7.0) sha256=3928185b62066cf6d8fe3b011dc5587ba53b09a5c7b573e36481b8d713d6aa03 opentelemetry-instrumentation-koala (0.23.0) sha256=8f324b50a2a64fd4994bb2b105a4cb0c80b64ec05cf5487d2daa906c650bc6f9 opentelemetry-instrumentation-lmdb (0.25.0) sha256=1e4d66d583ea242d4f72051062971f5af1ea353484d224abbd0aabdd1ce5f5cb - opentelemetry-instrumentation-mongo (0.25.0) sha256=d04585669f928ea82e7c469f996061d39d8ff184278d57cf4fc77a6d607f9c7a + opentelemetry-instrumentation-mongo (0.25.1) sha256=b66a8544bb0c60ab032ecd224333d50138f2b280d2d394c508d2ff8ca3fb94b9 opentelemetry-instrumentation-mysql2 (0.33.0) sha256=b49b7957d5eef59e046e73be3ca370518965d61495745b4cb7ece3ef5470bcf9 opentelemetry-instrumentation-net_http (0.28.0) sha256=63b00c1c8fcfba15cd293ece8383d19bbc35e9b5cc04056b3e95799be11026f5 opentelemetry-instrumentation-pg (0.35.0) sha256=65a6e78bd45282b56021f1ee1b88b9fd318abf6812c32bd740465e6b9997aad4 @@ -2134,19 +2134,19 @@ CHECKSUMS opentelemetry-instrumentation-sidekiq (0.28.1) sha256=abc85d62996a5362e7a9fd7af9f6c709d01ce04795514d12fee5126335ae97ae opentelemetry-instrumentation-sinatra (0.29.0) sha256=08595fec08d198df581d96aceb4b27998b84431e44a679950af7d00ab6559bdb opentelemetry-instrumentation-trilogy (0.67.0) sha256=40394d3071d92aa418ef5aedab8e74f7683c0566c285a5418f75ca0586fd025f - opentelemetry-registry (0.4.0) sha256=903fa6bfaa29eac1c1d73a4fdd29b850977b5353b84b8cdff11222c00ad2968f - opentelemetry-sdk (1.10.0) sha256=43719949be8df24dcaeb86ebbf75636cda87d51a01af2729499b92a48b80521a - opentelemetry-semantic_conventions (1.36.0) sha256=c1b1607dbc7853aac7f9e23f6e8b76969c45b07f2b812a4aa4383c19a3b0f617 + opentelemetry-registry (0.5.0) sha256=726ca58ada93a23efaa5f7bb81b8ab7a8a1e14602935c9c65dfa2e597a19fb4f + opentelemetry-sdk (1.11.0) sha256=427c6708f4732105ffa46c11afecb91807085c59e92538eaa6cf46b97b1850c6 + opentelemetry-semantic_conventions (1.37.0) sha256=1e2dc5ad649e19ba2fb0fa7c6f9303e5cdd8d3952511415cb07efe28a0f8f4c3 optimist (3.2.1) sha256=8cf8a0fd69f3aa24ab48885d3a666717c27bc3d9edd6e976e18b9d771e72e34e os (1.1.4) sha256=57816d6a334e7bd6aed048f4b0308226c5fb027433b67d90a9ab435f35108d3f ostruct (0.6.3) sha256=95a2ed4a4bd1d190784e666b47b2d3f078e4a9efda2fccf18f84ddc6538ed912 overviews (1.0.0) ox (2.14.23) sha256=4a9aedb4d6c78c5ebac1d7287dc7cc6808e14a8831d7adb727438f6a1b461b66 - pagy (43.4.1) sha256=a211fc64b2c396f84db08f8de7bf919e73732de9e9e170a33eec7960bbdd3822 + pagy (43.5.1) sha256=ca5aaa6d65d21eee67a48fe8801d022d07ee72afbc5bea6a9e21b13a27b7c0b9 paper_trail (17.0.0) sha256=1c2842061d3874ca7015908e821e2aa14f9b982af2acb2a7974713bf79021c85 - parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130 + parallel (2.0.1) sha256=337782d3e39f4121e67563bf91dd8ece67f48923d90698614773a0ec9a5b2c7d 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 @@ -2170,7 +2170,7 @@ CHECKSUMS prometheus-client-mmap (1.5.0-x86_64-darwin) sha256=96d360bbffc5603f8322a0b8c3353cf531c06410cf80dbdeda46efbd27a6a5a2 prometheus-client-mmap (1.5.0-x86_64-linux-gnu) sha256=2f5e7be72ea0b8bddd0e6a123e8f2468d7e2bdfa664a4d355fec4ed4f659d457 prometheus-client-mmap (1.5.0-x86_64-linux-musl) sha256=4673196ca44feb6db5eb63d5248feea32ffaae851f7861eee25218ccf7057ef4 - pry (0.16.0) sha256=d76c69065698ed1f85e717bd33d7942c38a50868f6b0673c636192b3d1b6054e + pry (0.16.0) pry-byebug (3.12.0) sha256=594e094ae8a8390a7ad4c7b36ae36e13304ed02664c67417d108dc5f7213d1b7 pry-rails (0.3.11) sha256=a69e28e24a34d75d1f60bcf241192a54253f8f7ef8a62cba1e75750a9653593d pry-rescue (1.6.0) sha256=985bfd506d9866b587fd86790cf8445266a41b7f92c627fc5b21ec7d92aba6db @@ -2181,7 +2181,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,26 +2192,26 @@ 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 (13.4.2) sha256=cb825b2bd5f1f8e91ca37bddb4b9aaf345551b4731da62949be002fa89283701 rake-compiler-dock (1.11.0) sha256=eab51f2cd533eb35cea6b624a75281f047123e70a64c58b607471bb49428f8c2 rb-fsevent (0.11.2) sha256=43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe rb-inotify (0.11.1) sha256=a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e - rb_sys (0.9.124) sha256=513476557b12eaf73764b3da9f8746024558fe8699bda785fb548c9aa3877ae7 + rb_sys (0.9.126) sha256=ba958e0b8b4b89eeae0b3d24b64c809eb2c37e0ab0773a49e9b1c2e22c95aef8 rbtrace (0.5.3) sha256=c432292f305d9ab12fd47d9722e0d5210d983758a951fe6107c36cc955cb923f rbtree3 (0.7.1) sha256=ab60ead728a5491b70df4f4065e180b18dbab5319f817ce1dbf5dd906f26d8ba rdoc (7.2.0) sha256=8650f76cd4009c3b54955eb5d7e3a075c60a57276766ebf36f9085e8c9f23192 - recaptcha (5.21.1) sha256=e003e9ceba9b993a9f0c6a828c192f2d46693cd2aa0b0beae94f936649507adb + recaptcha (5.21.2) sha256=8f7bb6cc9f04c523fff3f38126b7c5f1314c35fcb3691fc8e70d2b464533c12a 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,11 +2230,11 @@ 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.1) sha256=44415f3f01d01a21e01132248d2fd0867572475b566ca188a0a42133a08d4531 rubocop-ast (1.49.1) sha256=4412f3ee70f6fe4546cc489548e0f6fcf76cafcfa80fa03af67098ffed755035 rubocop-capybara (2.22.1) sha256=ced88caef23efea53f46e098ff352f8fc1068c649606ca75cb74650970f51c0c rubocop-factory_bot (2.28.0) sha256=4b17fc02124444173317e131759d195b0d762844a71a29fe8139c1105d92f0cb - rubocop-openproject (0.3.0) sha256=9554496e7ef0a2cf65dc2b32bee1bfa223b4f9ae058a5c603489d34e9001a828 + rubocop-openproject (0.4.0) sha256=ce56d9e591f9be5a4d98125b10a73564b0557a5e408f97918f9630fb15ae66ae rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834 rubocop-rails (2.34.3) sha256=10d37989024865ecda8199f311f3faca990143fbac967de943f88aca11eb9ad2 rubocop-rspec (3.9.0) sha256=8fa70a3619408237d789aeecfb9beef40576acc855173e60939d63332fdb55e2 @@ -2247,15 +2247,14 @@ CHECKSUMS ruby-rc4 (0.1.5) sha256=00cc40a39d20b53f5459e7ea006a92cf584e9bc275e2a6f7aa1515510e896c03 ruby-saml (1.18.1) sha256=1b0e7a44aef150b4197955f5e015d593672e242cfdc5d06aa7554ec2350b9107 ruby-vips (2.3.0) sha256=e685ec02c13969912debbd98019e50492e12989282da5f37d05f5471442f5374 - ruby2_keywords (0.0.5) sha256=ffd13740c573b7301cf7a2e61fc857b2a8e3d3aff32545d6f8300d8bae10e3ef rubytree (2.2.0) sha256=e312dc1ed814153583b57d9662e6caac1fd60830886a571b48bb06daef906eb6 rubyzip (2.4.1) sha256=8577c88edc1fde8935eb91064c5cb1aef9ad5494b940cf19c775ee833e075615 safety_net_attestation (0.5.0) sha256=c8cd01dd550dbe8553862918af6355a04672db11d218ec96104ce3955293f2aa sanitize (7.0.0) sha256=269d1b9d7326e69307723af5643ec032ff86ad616e72a3b36d301ac75a273984 scimitar (2.15.0) sha256=700901afab9303b705a8f37644cd733ee3c3819b168d24a14a48dec060b16d63 securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1 - selenium-devtools (0.145.0) sha256=629c4964ebeb140f0e08cd6e88e241b07a995416a0e45d8416ba25cf4a7f0513 - selenium-webdriver (4.41.0) sha256=cdc1173cd55cf186022cea83156cc2d0bec06d337e039b02ad25d94e41bedd22 + selenium-devtools (0.147.0) sha256=8e9262c7c3bfafc7e16b773047912fb952e7bb59c2e6b592ea74a781700d6873 + selenium-webdriver (4.43.0) sha256=a634377b964b701c6ac0a009ce3a08fa34ec1e1e7fe9a6d57e3088d14529a65c semantic (1.6.1) sha256=3cdbb48f59198ebb782a3fdfb87b559e0822a311610db153bae22777a7d0c163 shoulda-context (2.0.0) sha256=7adf45342cd800f507d2a053658cb1cce2884b616b26004d39684b912ea32c34 shoulda-matchers (7.0.1) sha256=b4bfd8744c10e0a36c8ac1a687f921ee7e25ed529e50488d61b79a8688749c77 @@ -2268,7 +2267,7 @@ CHECKSUMS spring-commands-rubocop (0.4.0) sha256=3e677a2c8a27ae8a986f04bfb69e66d5d55b017541e8be93bf0dc48a7f5690c1 sprockets (3.7.5) sha256=72c20f256548f8a37fe7db41d96be86c3262fddaf4ebe9d69ec8317394fed383 sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e - ssrf_filter (1.3.0) sha256=66882d7de7d09c019098d6d7372412950ae184ebbc7c51478002058307aba6f2 + ssrf_filter (1.5.0) sha256=e03dcdb9d1730d7f6710532a606b3543df2a448a0293ce04a2d995523c5a97f6 stackprof (0.2.28) sha256=4ec2ace02f386012b40ca20ef80c030ad711831f59511da12e83b34efb0f9a04 statesman (13.1.0) sha256=3ecb78466dfd2682e433f335a7722aa0e5b8c6853d72d83e460151b8af17a84e store_attribute (2.1.1) sha256=de53611c01d3fb4c91e0d7af4208861bd18d017c4cee682ed0473d0e4496f5b8 @@ -2280,11 +2279,11 @@ 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.1) sha256=0332d9c39a7c118ed942b2db582f055d9f24964d150004ec6b964d2fa262499e text-hyphen (1.5.0) sha256=c44a4533b8a554e7ff7c955e131bcccc78a0b4c56ce1d73f2c8c11f43b075a06 thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73 thread_safe (0.3.6) sha256=9ed7072821b51c57e8d6b7011a8e282e25aeea3a4065eab326e43f66f063b05a - timecop (0.9.10) sha256=12ba45ce57cdcf6b1043cb6cdffa6381fd89ce10d369c28a7f6f04dc1b0cd8eb + timecop (0.9.11) sha256=41284dc6e5041f2184f781ace766f942108c842f8d8c1386a26e6343decc7542 timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb tpm-key_attestation (0.14.1) sha256=7fd4e4653a7afd0a386632ddfb05d10ecfdd47678299c5e69165bc9ae111193f trailblazer-option (0.1.2) sha256=20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3 @@ -2304,7 +2303,7 @@ CHECKSUMS validate_url (1.0.15) sha256=72fe164c0713d63a9970bd6700bea948babbfbdcec392f2342b6704042f57451 vcr (6.4.0) sha256=077ac92cc16efc5904eb90492a18153b5e6ca5398046d8a249a7c96a9ea24ae6 vernier (1.10.0) sha256=5b1dc57012e08ed23e14f4d2943540140d454aa8434c7c35e7eb97befd4969bf - view_component (4.5.0) sha256=0d951360d830752da4d1daa5f6cb643f17c30f295fb779fd4de90a051537d9c1 + view_component (4.6.0) sha256=aabbcc68ab4af8a0135bd3f488e1a4132180cb611aa2565f86cb6e9135f4ed7e virtus (2.0.0) sha256=8841dae4eb7fcc097320ba5ea516bf1839e5d056c61ee27138aa4bddd6e3d1c2 warden (1.2.9) sha256=46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0 warden-basic_auth (0.2.1) sha256=bfc752e0109c0182c3e69e930284c5e1e81e7b4a354aeb2b5914ead1391f3c6e @@ -2324,11 +2323,11 @@ CHECKSUMS yabeda-puma-plugin (0.9.0) sha256=b78673ecc7ee30bc50691ddc41b7022c1c1801843900d5101418f4a14b550bc8 yabeda-rails (0.11.0) sha256=afa2581bd44c8f419cb3f2bbf9f6fb40f817c30476f7caf5d1c55c48d69a5b29 yaml (0.4.0) sha256=240e69d1e6ce3584d6085978719a0faa6218ae426e034d8f9b02fb54d3471942 - yard (0.9.38) sha256=721fb82afb10532aa49860655f6cc2eaa7130889df291b052e1e6b268283010f + yard (0.9.42) sha256=4e2be01f8623556093497731d44c801e600d7c9759ec7a35a2dd5dd83bbbba68 zeitwerk (2.7.5) sha256=d8da92128c09ea6ec62c949011b00ed4a20242b255293dd66bf41545398f73dd RUBY VERSION - ruby 4.0.1 + ruby 4.0.2 BUNDLED WITH - 4.0.3 + 4.0.9 diff --git a/app/components/_index.sass b/app/components/_index.sass index 3401f8f417e..ca04d7a7782 100644 --- a/app/components/_index.sass +++ b/app/components/_index.sass @@ -3,6 +3,7 @@ @import "op_primer/border_box_table_component" @import "op_primer/full_page_prompt_component" @import "op_primer/form_helpers" +@import "op_primer/inline_macro_component" @import "open_project/common/attribute_component" @import "open_project/common/attribute_help_text_component" @import "open_project/common/attribute_help_text_caption_component" diff --git a/app/components/admin/departments/add_department_component.html.erb b/app/components/admin/departments/add_department_component.html.erb new file mode 100644 index 00000000000..e8f954c223d --- /dev/null +++ b/app/components/admin/departments/add_department_component.html.erb @@ -0,0 +1,75 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + component_wrapper do + primer_form_with( + url: add_department_admin_departments_path, + method: :post, + scope: :group + ) do |f| + cancel_path = if group + helpers.admin_department_path(group) + else + helpers.admin_departments_path + end + + parent_id = group&.id + + render_inline_form(f) do |form| + form.hidden(name: :parent_id, value: parent_id) if parent_id + + form.group(layout: :horizontal) do |row| + row.text_field( + name: :lastname, + label: I18n.t("departments.add_department_form.name_label"), + visually_hide_label: true, + placeholder: I18n.t("departments.add_department_form.name_placeholder"), + required: true, + autofocus: true, + value: "", + autocomplete: "off" + ) + row.button( + name: :cancel, + tag: :a, + label: I18n.t(:button_cancel), + scheme: :default, + href: cancel_path + ) + row.submit( + name: :submit, + label: I18n.t(:button_add), + scheme: :primary + ) + end + end + end + end +%> diff --git a/app/components/admin/departments/add_department_component.rb b/app/components/admin/departments/add_department_component.rb new file mode 100644 index 00000000000..29401cea457 --- /dev/null +++ b/app/components/admin/departments/add_department_component.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. +#++ + +module Admin + module Departments + class AddDepartmentComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + attr_reader :group + + def initialize(group:) + super() + @group = group + end + end + end +end diff --git a/app/components/admin/departments/add_user_component.html.erb b/app/components/admin/departments/add_user_component.html.erb new file mode 100644 index 00000000000..bbaffadb8d9 --- /dev/null +++ b/app/components/admin/departments/add_user_component.html.erb @@ -0,0 +1,77 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + component_wrapper do + primer_form_with( + url: add_user_admin_department_path(group), + method: :post + ) do |f| + cancel_path = if group + helpers.admin_department_path(group) + else + helpers.admin_departments_path + end + + autocompleter_filters = filters + + render_inline_form(f) do |form| + form.group(layout: :horizontal) do |row| + row.autocompleter( + name: :user_id, + label: User.model_name.human, + visually_hide_label: true, + autocomplete_options: { + resource: "principals", + component: "opce-user-autocompleter", + url: ::API::V3::Utilities::PathHelper::ApiV3Path.principals, + searchKey: "any_name_attribute", + focusDirectly: true, + multiple: false, + filters: autocompleter_filters, + inputName: "user_id" + } + ) + row.button( + name: :cancel, + tag: :a, + label: I18n.t(:button_cancel), + scheme: :default, + href: cancel_path + ) + row.submit( + name: :submit, + label: I18n.t(:button_add), + scheme: :primary + ) + end + end + end + end +%> diff --git a/app/components/admin/departments/add_user_component.rb b/app/components/admin/departments/add_user_component.rb new file mode 100644 index 00000000000..61dddf5ae57 --- /dev/null +++ b/app/components/admin/departments/add_user_component.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class AddUserComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + attr_reader :group + + def initialize(group:) + super() + @group = group + end + + def filters + filters = [ + { name: "type", operator: "=", values: %w[User] }, + { name: "status", operator: "=", values: [Principal.statuses[:active].to_s, Principal.statuses[:invited].to_s] } + ] + + existing_user_ids = group.user_ids.map(&:to_s) + if existing_user_ids.any? + filters << { name: "id", operator: "!", values: existing_user_ids } + end + + filters + end + end + end +end diff --git a/app/components/admin/departments/blankslate_component.rb b/app/components/admin/departments/blankslate_component.rb new file mode 100644 index 00000000000..525a091af0c --- /dev/null +++ b/app/components/admin/departments/blankslate_component.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class BlankslateComponent < ApplicationComponent + include ApplicationHelper + include OpPrimer::ComponentHelpers + + def call + render(Primer::Beta::Blankslate.new(border: false)) do |component| + component.with_visual_icon(icon: :people, size: :medium) + component.with_heading(tag: :h2) { t("departments.blankslate.heading") } + component.with_description { t("departments.blankslate.description") } + component.with_primary_action( + href: new_department_admin_departments_path, + scheme: :primary, + data: { turbo_frame: Admin::Departments::DetailComponent.wrapper_key } + ) do |button| + button.with_leading_visual_icon(icon: :plus) + t("departments.blankslate.add_button") + end + end + end + end + end +end diff --git a/modules/backlogs/app/views/rb_master_backlogs/index.html.erb b/app/components/admin/departments/change_parent_dialog_component.html.erb similarity index 54% rename from modules/backlogs/app/views/rb_master_backlogs/index.html.erb rename to app/components/admin/departments/change_parent_dialog_component.html.erb index bf4222ec02e..a9c14c46dfb 100644 --- a/modules/backlogs/app/views/rb_master_backlogs/index.html.erb +++ b/app/components/admin/departments/change_parent_dialog_component.html.erb @@ -27,40 +27,40 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<% html_title t(:label_backlogs) %> - -<% content_controller "backlogs" %> - -<% content_for :content_header do %> - <%= - render Primer::OpenProject::PageHeader.new do |header| - header.with_title { t(:label_backlogs) } - header.with_breadcrumbs( - [{ href: project_overview_path(@project), text: @project.name }, - t(:label_backlogs)] - ) - end - %> - - <%= - render(Primer::OpenProject::SubHeader.new) do |subheader| - subheader.with_action_button( - scheme: :primary, - leading_icon: :plus, - label: I18n.t(:label_version_new), - tag: :a, - href: new_project_version_path(@project) - ) do - Version.human_model_name +<%= + render( + Primer::Alpha::Dialog.new( + id: DIALOG_ID, + title: I18n.t(:label_change_parent), + size: :medium_portrait + ) + ) do |dialog| + dialog.with_body do + primer_form_with(**form_arguments) do |form| + render( + Primer::OpenProject::FilterableTreeView.new( + form_arguments: { builder: form, name: :new_parent_id }, + include_sub_items_check_box_arguments: { hidden: true }, + filter_mode_control_arguments: { hidden: true } + ) + ) do |tree_view| + render_tree(tree_view) + end end end - %> -<% end %> -<% content_for :content_body do %> - <%= turbo_frame_tag :backlogs_container, refresh: :morph, src: backlogs_project_backlogs_path(@project), class: "op-backlogs-page" %> -<% end %> - -<% content_for :content_body_right do %> - <%= render(split_view_instance) if render_work_package_split_view? %> -<% end %> + dialog.with_footer(show_divider: true) do + concat(render(Primer::Beta::Button.new(data: { close_dialog_id: DIALOG_ID })) { I18n.t(:button_cancel) }) + concat( + render( + Primer::Beta::Button.new( + scheme: :primary, + form: FORM_ID, + type: :submit, + data: { turbo: true } + ) + ) { I18n.t(:button_select) } + ) + end + end +%> diff --git a/app/components/admin/departments/change_parent_dialog_component.rb b/app/components/admin/departments/change_parent_dialog_component.rb new file mode 100644 index 00000000000..c9adbc875f5 --- /dev/null +++ b/app/components/admin/departments/change_parent_dialog_component.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class ChangeParentDialogComponent < ApplicationComponent + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + DIALOG_ID = "departments--change-parent-dialog" + FORM_ID = "departments--change-parent-form" + + def initialize(department:, departments:) + super() + @department = department + @departments = departments + end + + def form_arguments + { + id: FORM_ID, + url: change_parent_admin_department_path(@department), + method: :post + } + end + + def render_tree(tree_view) + add_sub_tree(tree_view, nil) + end + + private + + def departments_by_parent_id + @departments_by_parent_id ||= @departments.group_by(&:parent_id) + end + + def children_for(parent_id) + departments_by_parent_id[parent_id] || [] + end + + def add_sub_tree(tree_view, parent_id) + children_for(parent_id).each do |dept| + attrs = item_attributes(dept) + children = children_for(dept.id) + + if children.any? + tree_view.with_sub_tree(**attrs) do |sub_tree| + add_sub_tree(sub_tree, dept.id) + end + else + tree_view.with_leaf(**attrs) + end + end + end + + def item_attributes(dept) + { + label: dept.name, + value: dept.id, + select_variant: :single, + current: dept.id == @department.id, + disabled: disabled_ids.include?(dept.id), + expanded: dept.id == @department.parent_id + } + end + + def disabled_ids + @disabled_ids ||= Set.new( + [@department.id, @department.parent_id].compact + descendant_ids(@department.id) + ) + end + + def descendant_ids(dept_id) + children_for(dept_id).flat_map { |child| [child.id] + descendant_ids(child.id) } + end + end + end +end diff --git a/app/components/admin/departments/department_row_component.rb b/app/components/admin/departments/department_row_component.rb new file mode 100644 index 00000000000..40559961be5 --- /dev/null +++ b/app/components/admin/departments/department_row_component.rb @@ -0,0 +1,125 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class DepartmentRowComponent < ApplicationComponent + include ApplicationHelper + include OpPrimer::ComponentHelpers + + def initialize(department:) + super() + @department = department + end + + def call + flex_layout(align_items: :center, justify_content: :space_between) do |row| + row.with_column do + render(Primer::Beta::Link.new(href: admin_department_path(@department))) { @department.name } + end + + row.with_column do + render(Primer::Alpha::ActionMenu.new) do |menu| + menu.with_show_button( + icon: "kebab-horizontal", + scheme: :invisible, + "aria-label": I18n.t(:label_actions) + ) + menu_items(menu) + end + end + end + end + + private + + def menu_items(menu) + with_item_group(menu) { edit_item(menu) } + with_item_group(menu) do + add_sub_department_item(menu) + add_user_item(menu) + end + with_item_group(menu) { change_parent_item(menu) } + with_item_group(menu) { delete_item(menu) } + end + + def edit_item(menu) + menu.with_item( + label: I18n.t(:button_edit), + tag: :a, + href: edit_admin_department_path(@department) + ) { it.with_leading_visual_icon(icon: :pencil) } + end + + def add_sub_department_item(menu) + menu.with_item( + label: I18n.t("departments.context_menu.add_sub_department"), + tag: :a, + href: new_department_admin_departments_path(parent_id: @department.id), + content_arguments: { data: { turbo_frame: Admin::Departments::HierarchyLayoutComponent.wrapper_key } } + ) { it.with_leading_visual_icon(icon: "op-arrow-in") } + end + + def add_user_item(menu) + menu.with_item( + label: I18n.t("departments.context_menu.add_user"), + tag: :a, + href: new_user_admin_department_path(@department), + content_arguments: { data: { turbo_frame: Admin::Departments::HierarchyLayoutComponent.wrapper_key } } + ) { it.with_leading_visual_icon(icon: "person-add") } + end + + def change_parent_item(menu) + menu.with_item( + label: I18n.t(:label_change_parent), + tag: :a, + href: change_parent_admin_department_path(@department), + content_arguments: { data: { controller: "async-dialog" } } + ) { it.with_leading_visual_icon(icon: "arrow-switch") } + end + + def delete_item(menu) + menu.with_item( + label: I18n.t(:button_delete), + scheme: :danger, + tag: :a, + href: admin_department_path(@department), + content_arguments: { + data: { + turbo_confirm: I18n.t(:text_are_you_sure), + turbo_method: :delete, + turbo_frame: "_top" + } + } + ) { it.with_leading_visual_icon(icon: :trash) } + end + end + end +end diff --git a/modules/backlogs/app/controllers/rb_wikis_controller.rb b/app/components/admin/departments/detail_blankslate_component.rb similarity index 71% rename from modules/backlogs/app/controllers/rb_wikis_controller.rb rename to app/components/admin/departments/detail_blankslate_component.rb index 7684363cffa..d0512272a44 100644 --- a/modules/backlogs/app/controllers/rb_wikis_controller.rb +++ b/app/components/admin/departments/detail_blankslate_component.rb @@ -28,16 +28,18 @@ # See COPYRIGHT and LICENSE files for more details. #++ -class RbWikisController < RbApplicationController - # NOTE: The methods #show and #edit are public (see init.rb). We will let - # OpenProject's WikiController#index take care of authorization - # - # NOTE: The methods #show and #edit create a template page when called. - def show - redirect_to controller: "/wiki", action: "index", project_id: @project, id: @sprint.wiki_page - end +module Admin + module Departments + class DetailBlankslateComponent < ApplicationComponent + include OpPrimer::ComponentHelpers - def edit - redirect_to controller: "/wiki", action: "edit", project_id: @project, id: @sprint.wiki_page + def call + render(Primer::Beta::Blankslate.new(border: false)) do |component| + component.with_visual_icon(icon: :people, size: :medium) + component.with_heading(tag: :h2) { t("departments.detail_blankslate.heading") } + component.with_description { t("departments.detail_blankslate.description") } + end + end + end end end diff --git a/app/components/admin/departments/detail_component.html.erb b/app/components/admin/departments/detail_component.html.erb new file mode 100644 index 00000000000..ed24540b0a1 --- /dev/null +++ b/app/components/admin/departments/detail_component.html.erb @@ -0,0 +1,94 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + component_wrapper(tag: "turbo-frame", target: "_top") do + render(Primer::Beta::BorderBox.new) do |box| + box.with_header do + flex_layout(align_items: :center, justify_content: :space_between) do |header| + header.with_column do + render(Primer::Beta::Breadcrumbs.new) do |loaf| + breadcrumb_items.each do |item| + loaf.with_item(href: item[:href] || "#", target: nil) { item[:label] } + end + end + end + + if group + header.with_column do + render( + Primer::Beta::IconButton.new( + tag: :a, + href: edit_admin_department_path(group), + icon: :pencil, + scheme: :invisible, + "aria-label": I18n.t(:button_edit) + ) + ) + end + end + end + end + + if show_global_empty_state? && !add_subgroup? + box.with_row do + render(Admin::Departments::BlankslateComponent.new) + end + elsif show_department_empty_state? && !add_user? && !add_subgroup? + box.with_row do + render(Admin::Departments::DetailBlankslateComponent.new) + end + else + child_groups.each do |child| + box.with_row do + render(Admin::Departments::DepartmentRowComponent.new(department: child)) + end + end + + if add_subgroup? + box.with_row do + render(Admin::Departments::AddDepartmentComponent.new(group:)) + end + end + + users.each do |user| + box.with_row do + render(Admin::Departments::UserRowComponent.new(user:, group:)) + end + end + + if add_user? + box.with_row do + render(Admin::Departments::AddUserComponent.new(group:)) + end + end + end + end + end +%> diff --git a/app/components/admin/departments/detail_component.rb b/app/components/admin/departments/detail_component.rb new file mode 100644 index 00000000000..9af578d7898 --- /dev/null +++ b/app/components/admin/departments/detail_component.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class DetailComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + attr_reader :group, :ancestors, :child_groups + + def initialize(group:, ancestors: [], child_groups: [], add_user: false, add_subgroup: false) + super(nil) + @group = group + @ancestors = ancestors + @child_groups = child_groups + @add_user = add_user + @add_subgroup = add_subgroup + end + + def add_user? + @add_user + end + + def add_subgroup? + @add_subgroup + end + + def users + @users ||= group&.users || [] + end + + def breadcrumb_items + items = [] + + if group + items << { label: organization_name, href: admin_departments_path } + ancestors.each do |ancestor| + items << { label: ancestor.name, href: admin_department_path(ancestor) } + end + items << { label: group.name } + else + items << { label: organization_name } + end + + items + end + + def show_global_empty_state? + group.blank? && child_groups.empty? + end + + def show_department_empty_state? + group.present? && child_groups.empty? && users.empty? + end + + private + + def organization_name + Setting.organization_name.presence || I18n.t("setting_organization_name") + end + end + end +end diff --git a/app/components/admin/departments/edit_page_header_component.html.erb b/app/components/admin/departments/edit_page_header_component.html.erb new file mode 100644 index 00000000000..b4a19bff6cf --- /dev/null +++ b/app/components/admin/departments/edit_page_header_component.html.erb @@ -0,0 +1,69 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> +<%= + render(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { @group.name } + header.with_breadcrumbs(breadcrumb_items) + + header.with_action_button( + tag: :a, + mobile_icon: :person, + mobile_label: t(:label_profile), + size: :medium, + href: show_group_path(@group), + aria: { label: I18n.t(:label_profile) }, + title: I18n.t(:label_profile) + ) do |button| + button.with_leading_visual_icon(icon: :person) + t(:label_profile) + end + + if @current_user.admin? + header.with_action_button( + tag: :a, + scheme: :danger, + mobile_icon: :trash, + mobile_label: t(:button_delete), + size: :medium, + href: group_path(@group), + aria: { label: I18n.t(:button_delete) }, + data: { + turbo_confirm: t(:text_are_you_sure), + turbo_method: :delete + }, + title: I18n.t(:button_delete) + ) do |button| + button.with_leading_visual_icon(icon: :trash) + t(:button_delete) + end + end + + helpers.render_tab_header_nav(header, @tabs) + end +%> diff --git a/modules/backlogs/app/helpers/burndown_charts_helper.rb b/app/components/admin/departments/edit_page_header_component.rb similarity index 66% rename from modules/backlogs/app/helpers/burndown_charts_helper.rb rename to app/components/admin/departments/edit_page_header_component.rb index 1c087f11125..b50d9333bd1 100644 --- a/modules/backlogs/app/helpers/burndown_charts_helper.rb +++ b/app/components/admin/departments/edit_page_header_component.rb @@ -28,24 +28,26 @@ # See COPYRIGHT and LICENSE files for more details. #++ -module BurndownChartsHelper - def xaxis_labels(burndown) - # 14 entries (plus the axis label) have come along as the best value for a good optical result. - # Thus it is enough space between the entries. - entries_displayed = (burndown.days.length / 14.0).ceil - burndown.days.enum_for(:each_with_index).map do |d, i| - if (i % entries_displayed) == 0 - ["#{escape_javascript(::I18n.t('date.abbr_day_names')[d.wday % 7])} #{d.strftime('%d/%m')}"] +module Admin + module Departments + class EditPageHeaderComponent < ApplicationComponent + include OpPrimer::ComponentHelpers + include ApplicationHelper + include TabsHelper + + def initialize(group:, current_user:, tabs: nil) + super + @group = group + @tabs = tabs + @current_user = current_user + end + + def breadcrumb_items + [{ href: admin_index_path, text: t("label_administration") }, + { href: admin_settings_users_path, text: t(:label_user_and_permission) }, + { href: admin_departments_path, text: t(:label_departments) }, + @group.name] end end end - - def dataseries(burndown) - burndown.series.map do |s| - { - label: I18n.t("burndown.#{s.first}"), - data: s.last.enum_for(:each) - } - end - end end diff --git a/app/components/admin/departments/hierarchy_layout_component.html.erb b/app/components/admin/departments/hierarchy_layout_component.html.erb new file mode 100644 index 00000000000..f58c8694484 --- /dev/null +++ b/app/components/admin/departments/hierarchy_layout_component.html.erb @@ -0,0 +1,87 @@ +<%#-- 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. + +++#%> + +<%# helpers.content_controller "admin--departments-page" %> + +<%= + component_wrapper(tag: "turbo-frame", class: "admin-groups-tree-page--wrapper", refresh: :morph, data: { turbo_action: :advance }) do + render(Primer::Alpha::Layout.new(stacking_breakpoint: :md, overflow: :hidden, h: :full, classes: "admin-groups-tree-page")) do |content| + content.with_main(overflow: :auto) do + flex_layout do |main| + main.with_row do + render(Primer::OpenProject::SubHeader.new) do |subheader| + subheader.with_action_menu(leading_icon: :plus, trailing_icon: :"triangle-down", label: I18n.t(:button_add), button_arguments: { scheme: :primary, "aria-label": I18n.t(:button_add) }) do |menu| + if active_group + menu.with_item( + label: I18n.t("departments.add_user"), + tag: :a, + href: new_user_admin_department_path(active_group), + content_arguments: { data: { turbo_frame: Admin::Departments::DetailComponent.wrapper_key } } + ) + end + menu.with_item( + label: I18n.t("departments.add_department"), + tag: :a, + href: new_department_admin_departments_path(parent_id: active_group&.id), + content_arguments: { data: { turbo_frame: Admin::Departments::DetailComponent.wrapper_key } } + ) + end + end + end + + main.with_row do + render( + Admin::Departments::DetailComponent.new( + group: active_group, + ancestors: ancestors_for(active_group), + child_groups: children_for(active_group&.id), + add_user: @add_user, + add_subgroup: @add_subgroup + ) + ) + end + end + end + + content.with_sidebar(row_placement: :start, col_placement: :start, border: true, border_bottom: 0, p: 3, overflow: :auto, classes: "admin-groups-tree-page--sidebar") do + flex_layout do |sidebar| + sidebar.with_row(mb: 3) do + render(Admin::Departments::OrganizationNameComponent.new) + end + + sidebar.with_row do + render(Primer::Alpha::TreeView.new(node_variant: :anchor)) do |tree_view| + render_group_tree(tree_view) + end + end + end + end + end + end +%> diff --git a/app/components/admin/departments/hierarchy_layout_component.rb b/app/components/admin/departments/hierarchy_layout_component.rb new file mode 100644 index 00000000000..fa9480f9978 --- /dev/null +++ b/app/components/admin/departments/hierarchy_layout_component.rb @@ -0,0 +1,115 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class HierarchyLayoutComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + attr_reader :groups, :active_group + + def initialize(groups:, active_group: nil, add_user: false, add_subgroup: false) + super() + @groups = groups + @active_group = active_group + @add_user = add_user + @add_subgroup = add_subgroup + end + + def render_group_tree(tree, parent_id: nil) + children_for(parent_id).each do |group| + node_attrs = { + label: group.name, + href: admin_department_path(group), + current: group == active_group + } + + if children?(group) + tree.with_sub_tree(**node_attrs, expanded: expanded?(group)) do |sub_tree| + render_group_tree(sub_tree, parent_id: group.id) + end + else + tree.with_leaf(**node_attrs) + end + end + end + + private + + def children_by_parent_id + @children_by_parent_id ||= groups.group_by(&:parent_id) + end + + def children_for(parent_id) + children_by_parent_id[parent_id] || [] + end + + def children?(group) + children_by_parent_id.key?(group.id) + end + + def expanded?(group) + return false unless active_group + + active_group == group || active_group_ancestor_ids.include?(group.id) + end + + def active_group_ancestor_ids + @active_group_ancestor_ids ||= compute_ancestor_ids(active_group) + end + + def groups_by_id + @groups_by_id ||= groups.index_by(&:id) + end + + def ancestors_for(group) + return [] unless group + + ancestor_ids = active_group_ancestor_ids + ancestor_ids.reverse.filter_map { |id| groups_by_id[id] } + end + + def compute_ancestor_ids(group) + return [] unless group + + ids = [] + current = group + while current.parent_id + ids << current.parent_id + current = groups_by_id[current.parent_id] + break unless current + end + ids + end + end + end +end diff --git a/app/components/admin/departments/move_user_dialog_component.html.erb b/app/components/admin/departments/move_user_dialog_component.html.erb new file mode 100644 index 00000000000..88cbef2812a --- /dev/null +++ b/app/components/admin/departments/move_user_dialog_component.html.erb @@ -0,0 +1,59 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + render( + Primer::OpenProject::DangerDialog.new( + id: DIALOG_ID, + title: t("departments.move_user_dialog.title"), + confirm_button_text: t("departments.move_user_dialog.confirm"), + size: :medium_portrait, + form_arguments: { + action: add_user_admin_department_path(to_department), + method: :post + } + ) + ) do |dialog| + dialog.with_confirmation_message do |message| + message.with_heading(tag: :h2) { t("departments.move_user_dialog.heading") } + message.with_description do + t( + "departments.move_user_dialog.description", + user: moved_user.name, + from_department: from_department.name + ) + end + end + + dialog.with_additional_details do + concat(hidden_field_tag(:user_id, moved_user.id)) + concat(hidden_field_tag(:remove_from_previous_department, "true")) + end + end +%> diff --git a/app/components/admin/departments/move_user_dialog_component.rb b/app/components/admin/departments/move_user_dialog_component.rb new file mode 100644 index 00000000000..0aa4bcbb041 --- /dev/null +++ b/app/components/admin/departments/move_user_dialog_component.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class MoveUserDialogComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + + DIALOG_ID = "move-user-department-dialog" + + attr_reader :moved_user, :from_department, :to_department + + def initialize(user:, from_department:, to_department:) + super() + @moved_user = user + @from_department = from_department + @to_department = to_department + end + end + end +end diff --git a/app/components/admin/departments/organization_name_component.html.erb b/app/components/admin/departments/organization_name_component.html.erb new file mode 100644 index 00000000000..c677b49a6b1 --- /dev/null +++ b/app/components/admin/departments/organization_name_component.html.erb @@ -0,0 +1,52 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + component_wrapper do + flex_layout(align_items: :center, justify_content: :space_between) do |container| + container.with_column do + render(Primer::Beta::Heading.new(tag: :h4)) { organization_name } + end + + container.with_column do + render( + Primer::Beta::IconButton.new( + tag: :a, + href: edit_organization_name_admin_departments_path, + icon: :pencil, + scheme: :invisible, + "aria-label": I18n.t(:button_edit), + data: { turbo_stream: true }, + test_selector: "edit-organization-name-button" + ) + ) + end + end + end +%> diff --git a/modules/backlogs/app/controllers/rb_burndown_charts_controller.rb b/app/components/admin/departments/organization_name_component.rb similarity index 77% rename from modules/backlogs/app/controllers/rb_burndown_charts_controller.rb rename to app/components/admin/departments/organization_name_component.rb index d5001d452a7..0ab12024364 100644 --- a/modules/backlogs/app/controllers/rb_burndown_charts_controller.rb +++ b/app/components/admin/departments/organization_name_component.rb @@ -28,18 +28,20 @@ # See COPYRIGHT and LICENSE files for more details. #++ -class RbBurndownChartsController < RbApplicationController - helper :burndown_charts +module Admin + module Departments + class OrganizationNameComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers - def show - @burndown = if OpenProject::FeatureDecisions.scrum_projects_active? - Burndown.new(@sprint, @project) - else - @sprint.burndown(@project) - end + def initialize + super(nil) + end - respond_to do |format| - format.html { render layout: true } + def organization_name + Setting.organization_name.presence || I18n.t("setting_organization_name") + end end end end diff --git a/app/components/admin/departments/organization_name_form_component.html.erb b/app/components/admin/departments/organization_name_form_component.html.erb new file mode 100644 index 00000000000..91d0fcf08fb --- /dev/null +++ b/app/components/admin/departments/organization_name_form_component.html.erb @@ -0,0 +1,68 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<%= + component_wrapper do + primer_form_with( + url: update_organization_name_admin_departments_path, + method: :patch, + data: { turbo_stream: true } + ) do |f| + render_inline_form(f) do |form| + form.group(layout: :horizontal) do |input_group| + input_group.text_field( + name: :organization_name, + label: I18n.t("setting_organization_name"), + value: Setting.organization_name, + visually_hide_label: true, + required: true, + autofocus: true, + input_width: :medium + ) + end + + form.group(layout: :horizontal) do |button_group| + button_group.button( + name: :cancel, + tag: :a, + label: I18n.t(:button_cancel), + scheme: :default, + href: helpers.cancel_edit_organization_name_admin_departments_path, + data: { turbo_stream: true, turbo_method: :patch } + ) + button_group.submit( + name: :submit, + label: I18n.t(:button_save), + scheme: :primary + ) + end + end + end + end +%> diff --git a/app/components/admin/departments/organization_name_form_component.rb b/app/components/admin/departments/organization_name_form_component.rb new file mode 100644 index 00000000000..84183b1a386 --- /dev/null +++ b/app/components/admin/departments/organization_name_form_component.rb @@ -0,0 +1,45 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + module Departments + class OrganizationNameFormComponent < ApplicationComponent + include ApplicationHelper + include OpTurbo::Streamable + include OpPrimer::ComponentHelpers + + def initialize + super(nil) + end + + delegate :wrapper_key, to: OrganizationNameComponent + end + end +end diff --git a/modules/backlogs/app/views/shared/not_configured.html.erb b/app/components/admin/departments/page_header_component.html.erb similarity index 58% rename from modules/backlogs/app/views/shared/not_configured.html.erb rename to app/components/admin/departments/page_header_component.html.erb index 03689ecfd3b..0b7596dc7c7 100644 --- a/modules/backlogs/app/views/shared/not_configured.html.erb +++ b/app/components/admin/departments/page_header_component.html.erb @@ -1,4 +1,4 @@ -<%# -- copyright +<%#-- copyright OpenProject is an open source project management software. Copyright (C) the OpenProject GmbH @@ -25,31 +25,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. See COPYRIGHT and LICENSE files for more details. -++# %> +++#%> -<% html_title t(:label_backlogs) %> +<% helpers.html_title t(:label_administration), t(:label_departments) %> -<% content_for :content_header do %> - <%= - render Primer::OpenProject::PageHeader.new do |header| - header.with_title { t(:label_backlogs) } - header.with_breadcrumbs( - [{ href: project_overview_path(@project), text: @project.name }, - t(:label_backlogs)] - ) - end - %> -<% end %> - -<% content_for :content_body do %> - <%= - render(Primer::Beta::Blankslate.new(border: true, spacious: true)) do |blankslate| - blankslate.with_visual_icon(icon: :"op-backlogs") - blankslate.with_heading(tag: :h2).with_content(t(:backlogs_not_configured_title)) - blankslate.with_description_content(t(:backlogs_not_configured_description)) - blankslate.with_secondary_action(href: admin_backlogs_settings_path, scheme: :default) do - t(:backlogs_not_configured_action_text) +<%= render(Primer::OpenProject::PageHeader.new) do |header| + header.with_title { t(:label_departments) } + header.with_description do + link_translate(:label_departments_description_html, links: { ldap_docs_article: "#" }) end - end - %> -<% end %> + header.with_breadcrumbs( + [ + { href: admin_index_path, text: t("label_administration") }, + { href: admin_settings_users_path, text: t(:label_user_and_permission) }, + t("label_departments") + ] + ) + end %> diff --git a/modules/backlogs/spec/views/shared/not_configured_spec.rb b/app/components/admin/departments/page_header_component.rb similarity index 88% rename from modules/backlogs/spec/views/shared/not_configured_spec.rb rename to app/components/admin/departments/page_header_component.rb index 57d240e138f..be4e00156f6 100644 --- a/modules/backlogs/spec/views/shared/not_configured_spec.rb +++ b/app/components/admin/departments/page_header_component.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + #-- copyright # OpenProject is an open source project management software. # Copyright (C) the OpenProject GmbH @@ -26,12 +28,10 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require "spec_helper" - -RSpec.describe "shared/not_configured" do - before { assign(:project, create(:project)) } - - it "renders without errors" do - render +module Admin + module Departments + class PageHeaderComponent < ApplicationComponent + include ApplicationHelper + end end end diff --git a/app/components/admin/departments/user_row_component.rb b/app/components/admin/departments/user_row_component.rb new file mode 100644 index 00000000000..c0b9a3c6f2b --- /dev/null +++ b/app/components/admin/departments/user_row_component.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. +#++ + +module Admin + module Departments + class UserRowComponent < ApplicationComponent + include ApplicationHelper + include OpPrimer::ComponentHelpers + + def initialize(user:, group:) + super() + @user = user + @group = group + end + + def call + flex_layout(align_items: :center, justify_content: :space_between) do |row| + row.with_column do + render(Users::AvatarComponent.new(user: @user, size: "mini")) + end + + row.with_column do + render(Primer::Alpha::ActionMenu.new) do |menu| + menu.with_show_button( + icon: "kebab-horizontal", + scheme: :invisible, + "aria-label": I18n.t(:label_actions) + ) + menu.with_item( + label: I18n.t(:button_remove), + scheme: :danger, + tag: :a, + href: remove_user_admin_department_path(@group, @user.id), + content_arguments: { + data: { + turbo_confirm: I18n.t(:text_are_you_sure), + turbo_method: :delete, + turbo_frame: "_top" + } + } + ) + end + end + end + end + end + end +end 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/enterprise_edition/plan_for_feature.rb b/app/components/enterprise_edition/plan_for_feature.rb index 747708b6953..6d458270d3b 100644 --- a/app/components/enterprise_edition/plan_for_feature.rb +++ b/app/components/enterprise_edition/plan_for_feature.rb @@ -81,6 +81,16 @@ module EnterpriseEdition end def plan_text + if trial_feature? + safe_join [helpers.t("ee.upsell.trial_text"), upsell_plan_text], " " + else + upsell_plan_text + end + end + + private + + def upsell_plan_text plan_name = render(Primer::Beta::Text.new(font_weight: :bold, classes: "upsell-colored-text")) do I18n.t("ee.upsell.plan_name", plan: plan.capitalize) end diff --git a/app/components/groups/show_page_header_component.html.erb b/app/components/groups/show_page_header_component.html.erb index da21c0482af..39f5d9b136c 100644 --- a/app/components/groups/show_page_header_component.html.erb +++ b/app/components/groups/show_page_header_component.html.erb @@ -35,15 +35,15 @@ See COPYRIGHT and LICENSE files for more details. header.with_action_button( tag: :a, mobile_icon: :pencil, - mobile_label: t(:button_edit), + mobile_label: edit_label, size: :medium, - href: edit_group_path(@group), - aria: { label: I18n.t(:button_edit) }, + href: edit_path, + aria: { label: edit_label }, data: { "test-selector": "groups--edit-group-button" }, - title: I18n.t(:button_edit) + title: edit_label ) do |button| button.with_leading_visual_icon(icon: :pencil) - t(:button_edit) + edit_label end header.with_action_button( diff --git a/app/components/groups/show_page_header_component.rb b/app/components/groups/show_page_header_component.rb index 14a34d03869..df4090f3986 100644 --- a/app/components/groups/show_page_header_component.rb +++ b/app/components/groups/show_page_header_component.rb @@ -40,8 +40,50 @@ module Groups end def breadcrumb_items - [{ href: groups_path, text: t(:label_group_plural) }, - @group.name] + if @current_user.admin? + admin_breadcrumb_items + else + non_admin_breadcrumb_items + end + end + + private + + def admin_breadcrumb_items + items = [{ href: admin_index_path, text: t("label_administration") }, + { href: admin_settings_users_path, text: t(:label_user_and_permission) }] + + items << if @group.organizational_unit? + { href: admin_departments_path, text: t(:label_departments) } + else + { href: groups_path, text: t(:label_group_plural) } + end + + items << @group.name + end + + def non_admin_breadcrumb_items + if @group.organizational_unit? + [t(:label_departments), @group.name] + else + [t(:label_group_plural), @group.name] + end + end + + def edit_path + if @group.organizational_unit? + edit_admin_department_path(@group) + else + edit_group_path(@group) + end + end + + def edit_label + if @group.organizational_unit? + t("departments.edit") + else + t(:button_edit) + end end end end diff --git a/app/components/homescreen/announcement_component.html.erb b/app/components/homescreen/announcement_component.html.erb index 43b1086e845..72388f4d46d 100644 --- a/app/components/homescreen/announcement_component.html.erb +++ b/app/components/homescreen/announcement_component.html.erb @@ -27,4 +27,4 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -<%= render(Primer::Alpha::Banner.new(scheme: :default)) { format_text announcement.text } %> +<%= render(Primer::Alpha::Banner.new(scheme: :default, mb: 3)) { format_text announcement.text } %> diff --git a/app/components/homescreen/blocks/users.rb b/app/components/homescreen/blocks/meetings.rb similarity index 81% rename from app/components/homescreen/blocks/users.rb rename to app/components/homescreen/blocks/meetings.rb index 55ebe7d798b..1abfa96d312 100644 --- a/app/components/homescreen/blocks/users.rb +++ b/app/components/homescreen/blocks/meetings.rb @@ -30,19 +30,9 @@ module Homescreen module Blocks - class Users < Grids::WidgetComponent - include IconsHelper - include OpenProject::ObjectLinking - include Redmine::I18n - - def initialize(*) - super - - @newest_users = User.active.newest.take(3) - end - - def title - I18n.t(:label_user_plural) + class Meetings < Grids::WidgetComponent + def call + render(::Meetings::Widgets::Meetings.new(limit: 3)) end end end diff --git a/app/components/homescreen/blocks/users.html.erb b/app/components/homescreen/blocks/users.html.erb deleted file mode 100644 index f88f3887caf..00000000000 --- a/app/components/homescreen/blocks/users.html.erb +++ /dev/null @@ -1,23 +0,0 @@ -<%= widget_wrapper do %> -

<%= t("homescreen.additional.users") %>

- - <% unless @newest_users.empty? %> - - <% end %> - -
- <% if current_user.admin? %> - <%= link_to new_user_path, class: "button -primary" do %> - <%= op_icon("button--icon icon-add") %> - <%= t(:label_invite_user) %> - <% end %> - <% end %> -
-<% end %> 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/modules/backlogs/app/views/shared/_model_errors.html.erb b/app/components/op_primer/inline_macro_component.html.erb similarity index 89% rename from modules/backlogs/app/views/shared/_model_errors.html.erb rename to app/components/op_primer/inline_macro_component.html.erb index 328edde8973..71fb370380f 100644 --- a/modules/backlogs/app/views/shared/_model_errors.html.erb +++ b/app/components/op_primer/inline_macro_component.html.erb @@ -27,8 +27,7 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -
- <% model_errors.full_messages.each do |err| %> -
<%= err %>
- <% end %> -
+<%= render(Primer::BaseComponent.new(tag: :span, **@system_arguments)) do %> + <%= leading_visual_icon %> + <%= content %> +<% end %> diff --git a/modules/backlogs/spec/forms/admin/settings/backlogs_settings_model_spec.rb b/app/components/op_primer/inline_macro_component.rb similarity index 74% rename from modules/backlogs/spec/forms/admin/settings/backlogs_settings_model_spec.rb rename to app/components/op_primer/inline_macro_component.rb index f8a17c7cb56..008e0f47df5 100644 --- a/modules/backlogs/spec/forms/admin/settings/backlogs_settings_model_spec.rb +++ b/app/components/op_primer/inline_macro_component.rb @@ -28,16 +28,20 @@ # See COPYRIGHT and LICENSE files for more details. #++ -require "rails_helper" +module OpPrimer + class InlineMacroComponent < Primer::Component + renders_one :leading_visual_icon, ->(icon:, color: :muted) do + Primer::Beta::Octicon.new(icon:, color:, mr: 2, vertical_align: :middle) + end -RSpec.describe Admin::Settings::BacklogsSettingsModel, type: :model do - describe "validations" do - subject(:model) { described_class.new(story_types: [1, 2, 3]) } + def initialize(**system_arguments) + super() - it "validates that a story type cannot be used as a task type" do - expect(subject).to validate_exclusion_of(:task_type) - .in_array([1, 2, 3]) - .with_message(I18n.t("errors.attributes.task_type.cannot_be_story_type")) + @system_arguments = system_arguments + @system_arguments[:classes] = class_names( + @system_arguments[:classes], + "op-inline-macro" + ) end end end diff --git a/app/components/op_primer/inline_macro_component.sass b/app/components/op_primer/inline_macro_component.sass new file mode 100644 index 00000000000..0356f0647d7 --- /dev/null +++ b/app/components/op_primer/inline_macro_component.sass @@ -0,0 +1,7 @@ +@media screen + .op-inline-macro + display: inline + background: var(--bgColor-muted) + border: 1px solid transparent + border-radius: var(--borderRadius-medium) + padding: 4px 8px 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/versions/row_component.rb b/app/components/versions/row_component.rb index 1a6187596f3..73dd9cb06db 100644 --- a/app/components/versions/row_component.rb +++ b/app/components/versions/row_component.rb @@ -85,7 +85,7 @@ module Versions end def button_links - [edit_link, delete_link, backlogs_edit_link].compact + [edit_link, delete_link].compact end private 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_autofix_section_component.rb b/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb index 75ac28dd0db..7ca1d1acc62 100644 --- a/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb +++ b/app/components/work_packages/admin/settings/identifier_autofix_section_component.rb @@ -34,7 +34,7 @@ module WorkPackages class IdentifierAutofixSectionComponent < ApplicationComponent include OpPrimer::ComponentHelpers - DISPLAY_COUNT = WorkPackages::IdentifierAutofix::PreviewQuery::DISPLAY_COUNT + DISPLAY_COUNT = ProjectIdentifiers::IdentifierAutofix::PreviewQuery::DISPLAY_COUNT def initialize(projects_data:, total_count: projects_data.size) super() 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..5ddad8f2df5 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 @@ -46,7 +46,7 @@ module WorkPackages super() @state = state if state == :edit - result = WorkPackages::IdentifierAutofix::PreviewQuery.new.call + result = ProjectIdentifiers::IdentifierAutofix::PreviewQuery.new.call @projects_data = result.projects_data @total_count = result.total_count else @@ -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/bulk_delete_dialog_component.html.erb b/app/components/work_packages/bulk_delete_dialog_component.html.erb new file mode 100644 index 00000000000..148e8dc3f64 --- /dev/null +++ b/app/components/work_packages/bulk_delete_dialog_component.html.erb @@ -0,0 +1,81 @@ +<%# + -- copyright + OpenProject is an open source project management software. + Copyright (C) the OpenProject GmbH + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 3. + + OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: + Copyright (C) 2006-2013 Jean-Philippe Lang + Copyright (C) 2010-2013 the ChiliProject Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + See COPYRIGHT and LICENSE files for more details. + + ++# +%> + +<%= + render( + Primer::OpenProject::DangerDialog.new( + id:, + title:, + form_arguments: { + action: form_action, + method: :delete, + data: { turbo: false } + }, + size: :medium_portrait + ) + ) do |dialog| +%> + <% dialog.with_confirmation_message do |message| + message.with_heading(tag: :h2) { heading } + message.with_description_content(description) + end %> + + <% dialog.with_additional_details(display: :block) do %> + <% if multiple_projects? %> + <%= render(Primer::Alpha::Banner.new(mb: 2, scheme: :warning, icon: :alert)) do %> + <%= I18n.t("work_packages.bulk_delete_dialog.cross_project_warning", projects: project_names) %> + <% end %> + <% end %> + <%= render(OpPrimer::InsetBoxComponent.new) do %> + <% work_packages.each do |wp| %> + <%= render WorkPackages::InfoLineComponent.new(work_package: wp, + show_subject: true, + show_status: false, + show_project: multiple_projects?) %> + <% if descendants_for(wp).any? %> + <%= render(Primer::Box.new(pl: 2, my: 1)) do %> + <%= render(Primer::Beta::Text.new(mt: 1, font_size: :small, font_weight: :bold, display: :block, mb: 1)) do %> + <%= I18n.t("work_packages.bulk_delete_dialog.children_label") %> + <% end %> + <% descendants_for(wp).each do |descendant| %> + <%= render WorkPackages::InfoLineComponent.new(work_package: descendant, + show_subject: true, + show_status: false, + show_project: descendant.project != wp.project) %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + <% end %> + + <% dialog.with_confirmation_check_box_content(confirmation_checkbox_text) %> +<% end %> diff --git a/app/components/work_packages/bulk_delete_dialog_component.rb b/app/components/work_packages/bulk_delete_dialog_component.rb new file mode 100644 index 00000000000..a3e75df4cbf --- /dev/null +++ b/app/components/work_packages/bulk_delete_dialog_component.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 + class BulkDeleteDialogComponent < ApplicationComponent + include OpTurbo::Streamable + + attr_reader :work_packages + + def initialize(work_packages:, back_url: nil) + super + @work_packages = work_packages + @back_url = back_url + end + + private + + def id = "wp-delete-dialog" + + def title + I18n.t("work_packages.bulk_delete_dialog.title", count: total_count) + end + + def heading + I18n.t("work_packages.bulk_delete_dialog.heading", count: total_count) + end + + def description + if has_descendants? + I18n.t("work_packages.bulk_delete_dialog.description_with_children") + else + I18n.t("work_packages.bulk_delete_dialog.description") + end + end + + def confirmation_checkbox_text + if has_descendants? + I18n.t("work_packages.bulk_delete_dialog.confirm_children_deletion") + else + I18n.t("text_permanent_delete_confirmation_checkbox_label") + end + end + + def total_count + @total_count ||= work_packages.count + descendants_by_work_package.values.sum(&:size) + end + + def multiple_projects? + projects.size > 1 + end + + def project_names + projects.map(&:name).join(", ") + end + + def descendants_for(work_package) + (descendants_by_work_package[work_package.id] || []) + .reject { |child| work_packages.include?(child) } + end + + def has_descendants? + work_packages.any? { |wp| descendants_for(wp).any? } + end + + def form_action + helpers.work_packages_bulk_path(ids: work_packages.map(&:id), back_url: @back_url) + end + + def projects + @projects ||= work_packages.filter_map(&:project).uniq + end + + def descendants_by_work_package + @descendants_by_work_package ||= begin + hierarchies = WorkPackageHierarchy + .where(ancestor_id: work_packages.map(&:id)) + .where("generations > 0") + .order(:generations, :descendant_id) + + descendant_records = WorkPackage + .where(id: hierarchies.pluck(:descendant_id)) + .includes(:project, :type, :status) + .index_by(&:id) + + hierarchies + .group_by(&:ancestor_id) + .transform_values { |rows| rows.filter_map { |r| descendant_records[r.descendant_id] } } + end + end + end +end diff --git a/app/components/work_packages/delete_dialog_component.html.erb b/app/components/work_packages/delete_dialog_component.html.erb new file mode 100644 index 00000000000..ac66a997eab --- /dev/null +++ b/app/components/work_packages/delete_dialog_component.html.erb @@ -0,0 +1,75 @@ +<%# + -- copyright + OpenProject is an open source project management software. + Copyright (C) the OpenProject GmbH + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License version 3. + + OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: + Copyright (C) 2006-2013 Jean-Philippe Lang + Copyright (C) 2010-2013 the ChiliProject Team + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + + See COPYRIGHT and LICENSE files for more details. + + ++# +%> + +<%= + render( + Primer::OpenProject::DangerDialog.new( + id:, + title:, + form_arguments: { + action: form_action, + method: :delete, + data: { turbo: false } + }, + size: :medium_portrait + ) + ) do |dialog| +%> + <% dialog.with_confirmation_message do |message| + message.with_heading(tag: :h2) { heading } + message.with_description_content(description) + end %> + + <% if has_descendants? %> + <% dialog.with_additional_details(display: :block) do %> + <% if cross_project_descendants? %> + <%= render(Primer::Alpha::Banner.new(mb: 2, scheme: :warning, icon: :alert)) do %> + <%= I18n.t("work_packages.delete_dialog.cross_project_warning", projects: all_project_names) %> + <% end %> + <% end %> + <%= render(OpPrimer::InsetBoxComponent.new) do %> + <%= render(Primer::Beta::Text.new(font_size: :small, font_weight: :bold, display: :block, mb: 2)) do %> + <%= I18n.t("work_packages.bulk_delete_dialog.children_label") %> + <% end %> + <% descendants.each do |descendant| %> + <%= render WorkPackages::InfoLineComponent.new(pl:2, + my: 1, + work_package: descendant, + show_subject: true, + show_status: false, + show_project: descendant.project != work_package.project) %> + <% end %> + <% end %> + <% end %> + <% end %> + + <% dialog.with_confirmation_check_box_content(confirmation_checkbox_text) %> +<% end %> diff --git a/app/components/work_packages/delete_dialog_component.rb b/app/components/work_packages/delete_dialog_component.rb new file mode 100644 index 00000000000..d2daf8ec27a --- /dev/null +++ b/app/components/work_packages/delete_dialog_component.rb @@ -0,0 +1,100 @@ +# 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 + class DeleteDialogComponent < ApplicationComponent + include OpTurbo::Streamable + + attr_reader :work_package + + def initialize(work_package:, back_url: nil) + super + @work_package = work_package + @back_url = back_url + end + + private + + def id = "wp-delete-dialog" + + def title + I18n.t("work_packages.delete_dialog.title") + end + + def heading + I18n.t("work_packages.delete_dialog.heading") + end + + def description + I18n.t("work_packages.delete_dialog.description", name: work_package.to_s) + end + + def confirmation_checkbox_text + if has_descendants? + I18n.t("work_packages.delete_dialog.confirm_descendants_deletion") + else + I18n.t("text_permanent_delete_confirmation_checkbox_label") + end + end + + def descendants + @descendants ||= WorkPackage + .joins("INNER JOIN work_package_hierarchies ON work_package_hierarchies.descendant_id = work_packages.id") + .where(work_package_hierarchies: { ancestor_id: work_package.id }) + .where("work_package_hierarchies.generations > 0") + .includes(:project, :type, :status) + .order("work_package_hierarchies.generations ASC, work_packages.id ASC") + end + + def has_descendants? + descendants.any? + end + + def cross_project_descendants? + descendants.any? { |d| d.project != work_package.project } + end + + def all_project_names + names = descendants + .filter_map(&:project) + .uniq + .reject { |p| p == work_package.project } + .map(&:name) + + names + .unshift(work_package.project.name) + .join(", ") + end + + def form_action + helpers.work_packages_bulk_path(ids: [work_package.id], back_url: @back_url) + end + end +end 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/components/work_packages/info_line_component.html.erb b/app/components/work_packages/info_line_component.html.erb index e124a5c3582..1f590ec6550 100644 --- a/app/components/work_packages/info_line_component.html.erb +++ b/app/components/work_packages/info_line_component.html.erb @@ -1,9 +1,14 @@ <%= - flex_layout(flex_wrap: :wrap) do |flex| + flex_layout(flex_wrap: :wrap, **@system_arguments) do |flex| + if @show_project && !@show_subject + flex.with_column(mr: 2) do + render(Primer::Beta::Text.new(font_size: @font_size)) { "#{@work_package.project.name}: " } + end + end flex.with_column(mr: 2) do render(WorkPackages::HighlightedTypeComponent.new(work_package: @work_package, font_size: :small)) end - flex.with_column(mr: 2) do + flex.with_column do render( Primer::Beta::Link.new( href: url_for(controller: "/work_packages", action: "show", id: @work_package), @@ -13,8 +18,23 @@ ) ) { "##{@work_package.id}" } end - flex.with_column do - render WorkPackages::StatusBadgeComponent.new(status: @work_package.status) + + if @show_status + flex.with_column(ml: 2) do + render WorkPackages::StatusBadgeComponent.new(status: @work_package.status) + end + end + + if @show_subject + flex.with_column(classes: "ellipsis", ml: 1) do + render(Primer::Beta::Text.new(font_size: @font_size)) do + if @show_project + "#{@work_package.project.name}: #{@work_package.subject}" + else + @work_package.subject + end + end + end end end %> diff --git a/app/components/work_packages/info_line_component.rb b/app/components/work_packages/info_line_component.rb index 293d79eedc5..9be082a5acc 100644 --- a/app/components/work_packages/info_line_component.rb +++ b/app/components/work_packages/info_line_component.rb @@ -31,10 +31,20 @@ class WorkPackages::InfoLineComponent < ApplicationComponent include OpPrimer::ComponentHelpers - def initialize(work_package:, font_size: :small) + def initialize(work_package:, + show_project: false, + show_subject: false, + show_status: true, + font_size: :small, + **system_arguments) super @work_package = work_package @font_size = font_size + @show_project = show_project + @show_subject = show_subject + @show_status = show_status + + @system_arguments = system_arguments end end diff --git a/app/contracts/groups/base_contract.rb b/app/contracts/groups/base_contract.rb index 9b222968645..c54b69527a1 100644 --- a/app/contracts/groups/base_contract.rb +++ b/app/contracts/groups/base_contract.rb @@ -38,9 +38,9 @@ module Groups attribute :name attribute :lastname attribute :parent_id - attribute :organizational_unit validate :validate_unique_users + validate :validate_users_not_in_other_department private @@ -53,5 +53,25 @@ module Groups errors.add(:group_users, :taken) end end + + def validate_users_not_in_other_department + return unless model.organizational_unit? + + new_user_ids = model.group_users.select(&:new_record?).map(&:user_id) + return if new_user_ids.empty? + + users_already_in_departments(new_user_ids).each do |user_id, department_id| + errors.add(:group_users, :user_already_in_department, user_id:, department_id:) + end + end + + def users_already_in_departments(user_ids) + GroupUser + .joins(:group) + .merge(Group.organizational_units) + .where(user_id: user_ids) + .where.not(group_id: model.id) + .pluck(:user_id, :group_id) + end end end diff --git a/app/contracts/groups/create_contract.rb b/app/contracts/groups/create_contract.rb index fbb3c6fda7b..c3842bc1340 100644 --- a/app/contracts/groups/create_contract.rb +++ b/app/contracts/groups/create_contract.rb @@ -31,6 +31,7 @@ module Groups class CreateContract < BaseContract attribute :type + attribute :organizational_unit validate :type_is_group 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/contracts/users/update_contract.rb b/app/contracts/users/update_contract.rb index 98ae032c688..993e1b3b0bd 100644 --- a/app/contracts/users/update_contract.rb +++ b/app/contracts/users/update_contract.rb @@ -32,6 +32,7 @@ module Users class UpdateContract < BaseContract validate :user_allowed_to_update validate :at_least_one_admin_is_active + validate :user_limit_not_exceeded ## # Users can only be updated when @@ -60,6 +61,12 @@ module Users end end + def user_limit_not_exceeded + if activating_user? && OpenProject::Enterprise.user_limit_reached? + errors.add :base, :user_limit_reached + end + end + def editing_themself? user == model end @@ -69,5 +76,9 @@ module Users def can_manage_user? user.allowed_globally?(:manage_user) && (user.admin? || !model.admin?) end + + def activating_user? + model.status_changed? && model.active? + 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/departments_controller.rb b/app/controllers/admin/departments_controller.rb new file mode 100644 index 00000000000..0f69ef8c9dd --- /dev/null +++ b/app/controllers/admin/departments_controller.rb @@ -0,0 +1,270 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Admin + class DepartmentsController < ::ApplicationController + include OpTurbo::ComponentStream + include GroupsHelper + + layout :admin_or_frame_layout + + menu_item :departments + + # TODO: We will check for users permission here + before_action :require_admin + before_action :find_group, + only: %i[show edit new_user add_user remove_user update destroy change_parent_dialog change_parent + create_memberships edit_membership destroy_membership] + + def index + @groups = Group.with_detail.organizational_units.visible.order(:lastname) + end + + def new_user + @groups = Group.with_detail.organizational_units.visible.order(:lastname) + @add_user = true + render action: :index + end + + def add_user # rubocop:disable Metrics/AbcSize + result = ::Departments::AddUserService + .new(@group, user: current_user) + .call( + user_id: params[:user_id], + remove_from_previous_department: params[:remove_from_previous_department] == "true" + ) + + if result.success? + flash[:notice] = I18n.t("departments.flash.user_added") + redirect_to admin_department_path(@group), status: :see_other + elsif result.result.is_a?(Group) + respond_with_dialog( + Admin::Departments::MoveUserDialogComponent.new( + user: User.find(params[:user_id]), + from_department: result.result, + to_department: @group + ) + ) + else + flash[:error] = result.errors.full_messages.join("\n") + redirect_to admin_department_path(@group), status: :see_other + end + end + + def new_department + @group = Group.visible.with_detail.organizational_units.find(params[:parent_id]) if params[:parent_id].present? + @groups = Group.with_detail.organizational_units.visible.order(:lastname) + @add_subgroup = true + render action: :index + end + + def add_department + service_call = ::Groups::CreateService + .new(user: current_user) + .call(permitted_params.group.merge(organizational_unit: true)) + + respond_department_created(service_call) + end + + def remove_user + service_call = ::Groups::UpdateService + .new(user: current_user, model: @group) + .call(remove_user_ids: [params[:user_id]]) + + if service_call.success? + flash[:notice] = I18n.t("departments.flash.user_removed") + else + flash[:error] = service_call.errors.full_messages.join("\n") + end + redirect_to admin_department_path(@group), status: :see_other + end + + def change_parent_dialog + departments = Group.with_detail.organizational_units.visible.order(:lastname) + respond_with_dialog( + Admin::Departments::ChangeParentDialogComponent.new(department: @group, departments:) + ) + end + + def change_parent + new_parent_id = parse_new_parent_id(params[:new_parent_id]) + service_call = ::Groups::UpdateService + .new(user: current_user, model: @group) + .call(parent_id: new_parent_id) + + respond_parent_changed(service_call) + end + + def edit_organization_name + replace_via_turbo_stream(component: Admin::Departments::OrganizationNameFormComponent.new) + respond_with_turbo_streams + end + + def cancel_edit_organization_name + replace_via_turbo_stream(component: Admin::Departments::OrganizationNameComponent.new) + respond_with_turbo_streams + end + + def update_organization_name + ::Settings::UpdateService + .new(user: current_user) + .call(organization_name: params[:organization_name]) + + replace_via_turbo_stream(component: Admin::Departments::OrganizationNameComponent.new) + respond_with_turbo_streams + end + + # old groups interface that we adapted for departments. + + def show + @groups = Group.with_detail.organizational_units.visible.order(:lastname) + render action: :index + end + + def edit; end + + def update + service_call = ::Groups::UpdateService + .new(user: current_user, model: @group) + .call(permitted_params.group) + + if service_call.success? + flash[:notice] = I18n.t(:notice_successful_update) + redirect_to edit_admin_department_path(@group), status: :see_other + else + render action: :edit, status: :unprocessable_entity + end + end + + def destroy + redirect_target = @group.parent + + ::Groups::DeleteService + .new(user: current_user, model: @group) + .call + + flash[:info] = I18n.t(:notice_deletion_scheduled) + redirect_to redirect_target ? admin_department_path(redirect_target) : admin_departments_path, status: :see_other + end + + def create_memberships + membership_params = permitted_params.group_membership[:membership] + + service_call = ::Members::CreateService + .new(user: current_user) + .call(membership_params.merge(principal: @group)) + + respond_membership_altered(service_call) + end + + def edit_membership + membership_params = permitted_params.group_membership + + @membership = Member.find(membership_params[:membership_id]) + + service_call = ::Members::UpdateService + .new(model: @membership, user: current_user) + .call(membership_params[:membership]) + + respond_membership_altered(service_call) + end + + def destroy_membership + member = Member.find(params[:membership_id]) + ::Members::DeleteService + .new(model: member, user: current_user) + .call + + flash[:notice] = I18n.t(:notice_successful_delete) + redirect_to edit_admin_department_path(@group, tab: redirected_to_tab(member)), status: :see_other + end + + private + + def admin_or_frame_layout + return "turbo_rails/frame" if turbo_frame_request? + + "admin" + end + + def redirect_target_for(department) + department.parent || department + end + + def find_group + @group = Group.visible.organizational_units.includes(:members, :users, :group_detail).find(params[:id]) + end + + def parse_new_parent_id(input) + return nil if input.blank? + + value = MultiJson.load(Array(input).first, symbolize_keys: true)[:value] + value.presence + end + + def respond_parent_changed(service_call) + if service_call.success? + flash[:notice] = I18n.t(:notice_successful_update) + redirect_to admin_department_path(service_call.result.parent || service_call.result), status: :see_other + else + flash[:error] = service_call.errors.full_messages.join("\n") + redirect_to admin_department_path(@group), status: :see_other + end + end + + def respond_department_created(service_call) + if service_call.success? + flash[:notice] = I18n.t("departments.flash.department_created") + redirect_to admin_department_path(redirect_target_for(service_call.result)), status: :see_other + else + flash[:error] = service_call.errors.full_messages.join("\n") + redirect_back_or_to(admin_departments_path) + end + end + + def respond_membership_altered(service_call) + if service_call.success? + flash[:notice] = I18n.t(:notice_successful_update) + else + flash[:error] = service_call.errors.full_messages.join("\n") + end + + redirect_to edit_admin_department_path(@group, tab: redirected_to_tab(service_call.result)) + end + + def redirected_to_tab(membership) + if membership.project + "memberships" + else + "global_roles" + end + end + end +end 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/admin/settings/work_packages_identifier_controller.rb b/app/controllers/admin/settings/work_packages_identifier_controller.rb index 1e4ecbb008f..a7ec6be5294 100644 --- a/app/controllers/admin/settings/work_packages_identifier_controller.rb +++ b/app/controllers/admin/settings/work_packages_identifier_controller.rb @@ -39,21 +39,14 @@ module Admin::Settings end def show - @form_state = WorkPackages::IdentifierAutofix.job_in_progress? ? :change_in_progress : :edit + @form_state = ProjectIdentifiers::IdentifierAutofix.job_in_progress? ? :change_in_progress : :edit end def update - return render_400 unless params[:settings] - - if autofix_requested? - call = update_service.new(user: current_user).call(settings_params) - call.on_success do - WorkPackages::IdentifierAutofix::ApplyHandlesJob.perform_later - redirect_to action: "show" - end - call.on_failure { failure_callback(call) } - else - super + case params.dig(:settings, :work_packages_identifier) + when Setting::WorkPackageIdentifier::SEMANTIC then switch_to_semantic + when Setting::WorkPackageIdentifier::CLASSIC then switch_to_classic + else render_400 end end @@ -62,7 +55,7 @@ module Admin::Settings end def status - if WorkPackages::IdentifierAutofix.job_in_progress? + if ProjectIdentifiers::IdentifierAutofix.job_in_progress? head :no_content else replace_via_turbo_stream( @@ -74,12 +67,27 @@ module Admin::Settings private + def switch_to_semantic + unless ProjectIdentifiers::IdentifierAutofix.job_in_progress? + ProjectIdentifiers::ConvertInstanceToSemanticIdsJob.perform_later + end + redirect_to action: "show" + end + + def switch_to_classic + call = update_service.new(user: current_user) + .call(work_packages_identifier: Setting::WorkPackageIdentifier::CLASSIC) + call.on_success do + unless ProjectIdentifiers::IdentifierAutofix.job_in_progress? + ProjectIdentifiers::RevertInstanceToClassicIdsJob.perform_later + end + redirect_to action: "show" + end + call.on_failure { failure_callback(call) } + end + def check_feature_flag render_404 unless OpenProject::FeatureDecisions.semantic_work_package_ids_active? end - - def autofix_requested? - ActiveRecord::Type::Boolean.new.cast(params[:confirm_dangerous_action]) - end end end 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/groups_controller.rb b/app/controllers/groups_controller.rb index 676366dcb8d..76fb9fa2302 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -40,7 +40,7 @@ class GroupsController < ApplicationController edit_membership add_users] def index - @groups = Group.in_tree_order + @groups = Group.with_detail.not_organizational_units.in_tree_order end def show diff --git a/app/controllers/my_controller.rb b/app/controllers/my_controller.rb index 290fba911ac..c226fe18129 100644 --- a/app/controllers/my_controller.rb +++ b/app/controllers/my_controller.rb @@ -34,6 +34,7 @@ class MyController < ApplicationController include ActionView::Helpers::TagHelper include OpTurbo::ComponentStream include FlashMessagesOutputSafetyHelper + include Notifications::NotificationSettingsActions layout "my" @@ -46,20 +47,28 @@ class MyController < ApplicationController :locale, :interface, :update_settings, + :update_workdays, + :update_email_alerts, + :update_participating, + :update_non_participating, + :update_date_alerts, :password, :change_password, :password_confirmation_dialog, :notifications, - :reminders, :non_working_times, - :working_hours + :working_hours, + :new_project_settings, + :create_project_settings, + :edit_project_settings, + :update_project_settings, + :destroy_project_settings menu_item :account, only: [:account] menu_item :locale, only: [:locale] menu_item :interface, only: [:interface] menu_item :password, only: [:password] menu_item :notifications, only: [:notifications] - menu_item :reminders, only: [:reminders] menu_item :working_hours, only: %i[working_hours non_working_times] def account; end @@ -74,6 +83,22 @@ class MyController < ApplicationController write_settings end + def update_email_alerts + update_global_notification_setting(permitted_params.notification_setting_email_alerts) + end + + def update_participating + update_global_notification_setting(permitted_params.notification_setting_participating) + end + + def update_non_participating + update_global_notification_setting(permitted_params.notification_setting_non_participating) + end + + def update_date_alerts + update_global_notification_setting(build_date_alerts_params) + end + def interface; end # Manage user's password @@ -93,11 +118,10 @@ class MyController < ApplicationController respond_with_dialog My::PasswordConfirmationDialog.new end - # Configure user's in app notifications - def notifications; end - - # Configure user's mail reminders - def reminders; end + # Configure user's notifications and email reminders + def notifications + set_global_notification_setting + end def working_hours render_403 unless OpenProject::FeatureDecisions.user_working_times_active? @@ -164,6 +188,24 @@ class MyController < ApplicationController permitted_params.user.to_h.merge(params.permit(pref: {})) end + def update_global_notification_setting(update_params) + set_global_notification_setting + persist_notification_setting(@global_notification_setting, update_params) + redirect_back_or_to(my_notifications_path) + end + + def set_global_notification_setting + @global_notification_setting = @user.notification_settings.find_or_initialize_by(project: nil) + end + + def persist_notification_setting(setting, update_params) + if setting.update(update_params) + flash[:notice] = notice_account_updated + else + flash[:error] = error_account_update_failed(nil) + end + end + def notice_account_updated OpenProject::LocaleHelper.with_locale_for(current_user) do t(:notice_account_updated) @@ -175,6 +217,22 @@ class MyController < ApplicationController [t(:notice_account_update_failed), errors] end + def notifications_settings_path + my_notifications_path + end + + def workdays_redirect_path + my_notifications_path + end + + def project_notifications_create_url + my_project_notifications_path + end + + def project_setting_form_url(project_id) + my_project_setting_path(project_id:) + end + def set_current_user @user = current_user end diff --git a/app/controllers/scim_v2/base_controller_actions.rb b/app/controllers/scim_v2/base_controller_actions.rb index 01b3448478c..0617022889d 100644 --- a/app/controllers/scim_v2/base_controller_actions.rb +++ b/app/controllers/scim_v2/base_controller_actions.rb @@ -38,15 +38,9 @@ module ScimV2 rescue_from "ActiveRecord::RecordNotFound", with: :handle_resource_not_found def index - query = if params[:filter].blank? - storage_scope - else - attribute_map = storage_class.new.scim_queryable_attributes - parser = ::Scimitar::Lists::QueryParser.new(attribute_map) - - parser.parse(params[:filter]) - parser.to_activerecord_query(storage_scope) - end + # Applies .distinct to avoid duplicate records caused by + # left_joins in storage_scope (e.g. groups, auth provider links). + query = scim_index_storage_query.distinct pagination_info = scim_pagination_info(query.count) page_of_results = query @@ -75,19 +69,37 @@ module ScimV2 private - def include_attributes - first_level_attrs = storage_class.scim_attributes_map.keys.map(&:to_s) - second_level_attrs = - storage_class - .scim_attributes_map - .find_all { |_, v| v.is_a? Hash } - .flat_map { |parent, childs| childs.map { |child, _| "#{parent}.#{child}" } } - all_possible_attributes = (first_level_attrs + second_level_attrs) + # Builds the base query for the SCIM index action, + # applying any SCIM filter params if present. + def scim_index_storage_query + return storage_scope if params[:filter].blank? + # Returns the list of SCIM attributes to include in the response, + # excluding any attributes specified in the excludedAttributes param. + attribute_map = storage_class.new.scim_queryable_attributes + parser = ::Scimitar::Lists::QueryParser.new(attribute_map) + + parser.parse(params[:filter]) + parser.to_activerecord_query(storage_scope) + end + + def include_attributes + # Collects all possible SCIM attribute names (top-level and nested) + # from the storage class's scim_attributes_map. excluded_attributes = params.fetch(:excludedAttributes, "").split(",") excluded_parents = excluded_attributes.filter_map { |attr| attr.split(".")[-2] } - all_possible_attributes - excluded_attributes - excluded_parents + scim_all_attribute_names - excluded_attributes - excluded_parents + end + + def scim_all_attribute_names + map = storage_class.scim_attributes_map + nested = + map + .find_all { |_, v| v.is_a? Hash } + .flat_map { |parent, childs| childs.map { |child, _| "#{parent}.#{child}" } } + + map.keys.map(&:to_s) + nested end def raise_result_errors_for_scim(result) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 01f21d2bb2e..b54d615a889 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/bulk_controller.rb b/app/controllers/work_packages/bulk_controller.rb index e281c2b4c10..2393cff6737 100644 --- a/app/controllers/work_packages/bulk_controller.rb +++ b/app/controllers/work_packages/bulk_controller.rb @@ -38,6 +38,18 @@ class WorkPackages::BulkController < ApplicationController include QueriesHelper include WorkPackages::BulkErrorMessage + include OpTurbo::ComponentStream + + def delete_dialog + component = + if @work_packages.one? + WorkPackages::DeleteDialogComponent.new(work_package: @work_packages.first, back_url: params[:back_url]) + else + WorkPackages::BulkDeleteDialogComponent.new(work_packages: @work_packages, back_url: params[:back_url]) + end + + respond_with_dialog component + end def edit setup_edit @@ -70,20 +82,21 @@ class WorkPackages::BulkController < ApplicationController end end - def destroy + def destroy # rubocop:disable Metrics/AbcSize if WorkPackage.cleanup_associated_before_destructing_if_required(@work_packages, current_user, params[:to_do]) destroy_work_packages(@work_packages) respond_to do |format| format.html do - redirect_to (project_work_packages_path(@work_packages.first.project)) + redirect_back_or_default(project_work_packages_path(@work_packages.first.project), + status: :see_other) end format.json do head :ok end end else - redirect_to(action: :reassign, ids: @work_packages.map(&:id)) + redirect_to(action: :reassign, ids: @work_packages.map(&:id), back_url: params[:back_url]) end end diff --git a/app/controllers/work_packages/moves_controller.rb b/app/controllers/work_packages/moves_controller.rb index 861e3c0dd59..15b0a3fad29 100644 --- a/app/controllers/work_packages/moves_controller.rb +++ b/app/controllers/work_packages/moves_controller.rb @@ -33,7 +33,8 @@ class WorkPackages::MovesController < ApplicationController default_search_scope :work_packages before_action :find_work_packages, :check_project_uniqueness - before_action :authorize + before_action :authorize_move_or_copy + authorization_checked! :new, :create def new prepare_for_work_package_move @@ -47,6 +48,11 @@ class WorkPackages::MovesController < ApplicationController private + def authorize_move_or_copy + permission = params.has_key?(:copy) ? :copy_work_packages : :move_work_packages + do_authorize(permission) + end + def perform_operation if within_frontend_treshold? perform_in_frontend diff --git a/app/forms/admin/settings/external_links_settings_form.rb b/app/forms/admin/settings/external_links_settings_form.rb index 5aa54f233f0..1b26481695b 100644 --- a/app/forms/admin/settings/external_links_settings_form.rb +++ b/app/forms/admin/settings/external_links_settings_form.rb @@ -32,6 +32,8 @@ module Admin module Settings class ExternalLinksSettingsForm < ApplicationForm settings_form do |sf| + helpers.call_hook(:component_admin_settings_external_redirect, form: sf) + sf.check_box( name: :capture_external_links, caption: I18n.t(:setting_capture_external_links_text) diff --git a/app/forms/admin/settings/general_settings_form.rb b/app/forms/admin/settings/general_settings_form.rb index a2ae499c254..a399108bba8 100644 --- a/app/forms/admin/settings/general_settings_form.rb +++ b/app/forms/admin/settings/general_settings_form.rb @@ -41,6 +41,11 @@ module Admin input_width: :medium ) + sf.text_field( + name: :organization_name, + input_width: :medium + ) + sf.text_field( name: :per_page_options, input_width: :medium 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/groups/form.rb b/app/forms/groups/form.rb index b18a50f36b3..652baaabeb2 100644 --- a/app/forms/groups/form.rb +++ b/app/forms/groups/form.rb @@ -47,8 +47,7 @@ module Groups caption: I18n.t(:label_parent_group_caption), input_width: :medium ) do |list| - excluded_ids = model.self_and_descendants.pluck(:id).to_set - Group.in_tree_order.reject { |g| excluded_ids.include?(g.id) }.each do |group| + parent_candidates.each do |group| prefix = "\u00A0\u00A0" * (group.hierarchy_depth || 0) list.option(label: "#{prefix}#{group.name}", value: group.id, selected: model.parent_id == group.id) end @@ -75,5 +74,18 @@ module Groups def custom_fields model.available_custom_fields end + + def parent_candidates + @parent_candidates ||= begin + scope = if model.organizational_unit + Group.organizational_units + else + Group.not_organizational_units + end + + excluded_ids = model.self_and_descendants.pluck(:id).to_set + scope.in_tree_order.reject { |group| excluded_ids.include?(group.id) } + end + end 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/forms/versions/form.rb b/app/forms/versions/form.rb index 35f8b6b0533..778650eda8a 100644 --- a/app/forms/versions/form.rb +++ b/app/forms/versions/form.rb @@ -110,35 +110,6 @@ module Versions end end - if backlogs_enabled? - setting = version_setting_for_project - - f.select_list( - name: "version[version_settings_attributes][][display]", - scope_name_to_model: false, - label: I18n.t(:label_column_in_backlog), - input_width: :small - ) do |list| - position_display_options.each do |label, value| - list.option(label:, value:, selected: setting.display == value) - end - end - - if setting.persisted? - f.hidden( - name: "version[version_settings_attributes][][id]", - value: setting.id, - scope_name_to_model: false - ) - end - end - - f.hidden( - name: "project_id", - value: project.id, - scope_name_to_model: false - ) - render_custom_fields(form: f) f.submit( @@ -177,35 +148,5 @@ module Versions def wiki_pages_disabled? contract.assignable_wiki_pages.none? end - - def backlogs_enabled? - resolved_project.backlogs_enabled? && !OpenProject::FeatureDecisions.scrum_projects_active? - end - - def resolved_project - @project || version.project - end - - def version_setting_for_project - setting = version.version_settings.detect { |vs| vs.project_id == resolved_project.id || vs.project_id.nil? } - setting || version.version_settings.new(display: VersionSetting::DISPLAY_LEFT, project: resolved_project) - end - - def position_display_options - [VersionSetting::DISPLAY_NONE, - VersionSetting::DISPLAY_LEFT, - VersionSetting::DISPLAY_RIGHT].map { |s| [humanize_display_option(s), s] } - end - - def humanize_display_option(option) - case option - when VersionSetting::DISPLAY_NONE - I18n.t("version_settings_display_option_none") - when VersionSetting::DISPLAY_LEFT - I18n.t("version_settings_display_option_left") - when VersionSetting::DISPLAY_RIGHT - I18n.t("version_settings_display_option_right") - end - end end end diff --git a/app/helpers/groups_helper.rb b/app/helpers/groups_helper.rb index 2a36884936a..5fbc9751a9d 100644 --- a/app/helpers/groups_helper.rb +++ b/app/helpers/groups_helper.rb @@ -64,6 +64,29 @@ module GroupsHelper ] end + def department_settings_tabs(group) + [ + { + name: "general", + partial: "admin/departments/general", + path: edit_admin_department_path(group), + label: :label_general + }, + { + name: "memberships", + partial: "admin/departments/memberships", + path: edit_admin_department_path(group, tab: :memberships), + label: :label_project_plural + }, + { + name: "global_roles", + partial: "principals/global_roles", + path: edit_admin_department_path(group, tab: :global_roles), + label: :label_global_roles + } + ] + end + def autocompleter_filters(group) [ { name: "status", operator: "=", values: ["active", "invited"] }, diff --git a/app/helpers/members_helper.rb b/app/helpers/members_helper.rb index d6416a80b0f..16e166e49e5 100644 --- a/app/helpers/members_helper.rb +++ b/app/helpers/members_helper.rb @@ -66,7 +66,9 @@ module MembersHelper end def principal_membership_path(principal, global_member, options = {}) - if principal.is_a?(Group) + if principal.is_a?(Group) && principal.organizational_unit? + membership_of_admin_department_path(principal, global_member, options) + elsif principal.is_a?(Group) membership_of_group_path(principal, global_member, options) else user_membership_path(principal, global_member, options) @@ -74,7 +76,9 @@ module MembersHelper end def principal_memberships_path(principal, options = {}) - if principal.is_a?(Group) + if principal.is_a?(Group) && principal.organizational_unit? + memberships_of_admin_department_path(principal, options) + elsif principal.is_a?(Group) memberships_of_group_path(principal, options) else user_memberships_path(principal, options) 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/concerns/has_details_table.rb b/app/models/concerns/has_details_table.rb index e8b58db734d..8a8c7b7494e 100644 --- a/app/models/concerns/has_details_table.rb +++ b/app/models/concerns/has_details_table.rb @@ -59,6 +59,7 @@ module HasDetailsTable setup_detail_association(association_name, detail_class, foreign_key) setup_detail_aliases(association_name) setup_detail_delegation(detail_class, foreign_key) + setup_detail_changed_tracking(detail_class, foreign_key) setup_detail_dup end @@ -118,6 +119,112 @@ module HasDetailsTable alias_method :build_detail, :"build_#{association_name}" end + # Include detail column changes in the owner's `changed` and `changes` so + # that ModelContract can detect unauthorized writes to delegated attributes. + def setup_detail_changed_tracking(detail_class, foreign_key) + setup_changed_method(detail_class, foreign_key) + setup_changes_method(detail_class, foreign_key) + setup_changed_question_method(detail_class, foreign_key) + setup_changed_attributes_method(detail_class, foreign_key) + setup_previous_changes_method(detail_class, foreign_key) + setup_restore_attributes_method(detail_class, foreign_key) + setup_reload_method + + # Rails 5.1+ alias - only define if the original method exists + alias_method :saved_changes, :previous_changes if method_defined?(:saved_changes) + end + + def setup_changed_method(detail_class, foreign_key) + define_method(:changed) do + result = super() + return result unless detail&.persisted? + + internal_columns = %w[id created_at updated_at] + [foreign_key] + detail_columns = detail_class.column_names - internal_columns + result | (detail.changed & detail_columns) + end + end + + def setup_changes_method(detail_class, foreign_key) + define_method(:changes) do + result = super() + return result unless detail&.persisted? + + internal_columns = %w[id created_at updated_at] + [foreign_key] + detail_columns = detail_class.column_names - internal_columns + detail_changes = detail.changes.slice(*detail_columns) + result.merge(detail_changes) + end + end + + def setup_changed_question_method(detail_class, foreign_key) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity + define_method(:changed?) do |attr = nil| # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity + internal_columns = %w[id created_at updated_at] + [foreign_key] + detail_columns = detail_class.column_names - internal_columns + + if attr.nil? + return true if super() + return false unless detail&.persisted? + + detail.changed.intersect?(detail_columns) + else + attr = attr.to_s + if detail_columns.include?(attr) + detail.persisted? && detail.changed.include?(attr) + else + return false unless super() + + changed.include?(attr) + end + end + end + end + + def setup_changed_attributes_method(detail_class, foreign_key) + define_method(:changed_attributes) do + result = super() + return result unless detail&.persisted? + + internal_columns = %w[id created_at updated_at] + [foreign_key] + detail_columns = detail_class.column_names - internal_columns + detail_changed = detail.changed_attributes.slice(*detail_columns) + result.merge(detail_changed) + end + end + + def setup_previous_changes_method(detail_class, foreign_key) + define_method(:previous_changes) do + result = super() + return result unless detail&.persisted? + + internal_columns = %w[id created_at updated_at] + [foreign_key] + detail_columns = detail_class.column_names - internal_columns + detail_previous = detail.previous_changes.slice(*detail_columns) + result.merge(detail_previous) + end + end + + def setup_restore_attributes_method(detail_class, foreign_key) + define_method(:restore_attributes) do |attributes = changed| + attributes = Array(attributes).map(&:to_s) + internal_columns = %w[id created_at updated_at] + [foreign_key] + detail_columns = detail_class.column_names - internal_columns + owner_attrs = attributes - detail_columns + detail_attrs = attributes & detail_columns + + super(owner_attrs) if owner_attrs.any? + detail.restore_attributes(detail_attrs) if detail_attrs.any? && detail&.persisted? + end + end + + def setup_reload_method + define_method(:reload) do |*args| + result = super(*args) + detail&.reload + result + end + end + def setup_detail_delegation(detail_class, foreign_key) # Try to set up delegation eagerly so that writer methods exist # during assign_attributes in new/create. Requires DB + table. @@ -169,6 +276,10 @@ module HasDetailsTable (detail_class.column_names - internal_columns).each do |col| delegate col.to_sym, to: :detail define_detail_writer(:"#{col}=") + + if detail_class.columns_hash[col]&.type == :boolean + delegate :"#{col}?", to: :detail + end end end diff --git a/app/models/custom_actions/actions/base.rb b/app/models/custom_actions/actions/base.rb index a600586f581..3b131d6f80d 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.rb b/app/models/group.rb index 40b613afa4d..91ac56e2f6c 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -41,6 +41,7 @@ class Group < Principal end validate :no_circular_parent, if: -> { parent_id.present? } + validate :no_organizational_unit_mismatch, if: -> { parent_id.present? } # Register a partial to be rendered on the synchronized groups tab of the groups admin page # @@ -170,4 +171,13 @@ class Group < Principal errors.add(:parent_id, :circular_dependency) end end + + def no_organizational_unit_mismatch + parent = self.class.find_by(id: parent_id) + return unless parent + + if organizational_unit? != parent.organizational_unit? + errors.add(:parent_id, :organizational_unit_mismatch) + end + end end 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/groups/hierarchy.rb b/app/models/groups/hierarchy.rb index 3af58b2a189..bebe514c554 100644 --- a/app/models/groups/hierarchy.rb +++ b/app/models/groups/hierarchy.rb @@ -47,8 +47,22 @@ module Groups::Hierarchy end # All groups above this one in the tree up to the root. - def ancestors - Group.where(id: ancestor_ids) + # Pass `order: :asc` to get root-first, `:desc` for closest-ancestor-first. + def ancestors(order: nil) + ids = ancestor_ids + scope = Group.where(id: ids) + + if order + # ancestor_ids are returned child-first by the CTE. + # Use array_position to preserve that order, then apply asc/desc. + ordered_ids = order == :asc ? ids.reverse : ids + order_sql = self.class.sanitize_sql_array( + ["array_position(ARRAY[?]::bigint[], #{Group.table_name}.id)", ordered_ids] + ) + scope.order(Arel.sql(order_sql)) + else + scope + end end # Self and all ancestor groups, ordered from root down. diff --git a/app/models/groups/scopes/organizational_units.rb b/app/models/groups/scopes/organizational_units.rb index e1741d7a369..e4db55b48cb 100644 --- a/app/models/groups/scopes/organizational_units.rb +++ b/app/models/groups/scopes/organizational_units.rb @@ -36,6 +36,10 @@ module Groups::Scopes def organizational_units where_detail(organizational_unit: true) end + + def not_organizational_units + where_detail(organizational_unit: false) + end end end end 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..d0a44db3c3a 100644 --- a/app/models/permitted_params.rb +++ b/app/models/permitted_params.rb @@ -274,7 +274,31 @@ class PermittedParams :comments_sorting, :disable_keyboard_shortcuts, :warn_on_leaving_unsaved, - :auto_hide_popups) + :auto_hide_popups, + immediate_reminders: %i[mentioned personal_reminder], + daily_reminders: [:enabled, { times: [] }], + workdays: [], + pause_reminders: %i[enabled date_range]) + end + + def notification_setting_email_alerts + params.fetch(:notification_setting, {}).permit(*NotificationSetting.email_settings) + end + + def notification_setting_participating + params.fetch(:notification_setting, {}).permit(:assignee, :responsible, :shared) + end + + def notification_setting_non_participating + params.fetch(:notification_setting, {}).permit(*NotificationSetting.non_participating_settings) + end + + def notification_setting_project + params.fetch(:notification_setting, {}).permit( + :project_id, + :assignee, :responsible, :shared, + *NotificationSetting.non_participating_settings + ) end def project @@ -335,9 +359,6 @@ class PermittedParams end def version - # `version_settings_attributes` is from a plugin. Unfortunately as it stands - # now it is less work to do it this way than have the plugin override this - # method. We hopefully will change this in the future. permitted_params = params.fetch(:version, {}).permit(:name, :description, :effective_date, @@ -345,8 +366,7 @@ class PermittedParams :start_date, :wiki_page_title, :status, - :sharing, - version_settings_attributes: %i(id display project_id)) + :sharing) permitted_params.merge(custom_field_values(:version, required: false)) end 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..e505f66e0b7 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,8 +96,10 @@ module Projects::Identifier class_methods do def suggest_identifier(name) - if Setting::WorkPackageIdentifier.alphanumeric? - WorkPackages::IdentifierAutofix::ProjectIdentifierSuggestionGenerator.suggest_identifier(name) + if Setting::WorkPackageIdentifier.semantic? + exclude = ProjectIdentifiers::IdentifierAutofix::ProblematicIdentifiers.reserved_identifiers + ProjectIdentifiers::IdentifierAutofix::ProjectIdentifierSuggestionGenerator + .suggest_identifier(name, exclude:) else # This should closely enough emulate Project models' usage of acts_as_url name.to_url.first(IDENTIFIER_MAX_LENGTH).presence || "project" end @@ -130,14 +126,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? @@ -145,6 +165,8 @@ module Projects::Identifier end def generate_semantic_identifier - self.identifier = self.class.suggest_identifier(name) if name.present? + return if name.blank? + + self.identifier = self.class.suggest_identifier(name) end end diff --git a/app/models/projects/scopes/visible.rb b/app/models/projects/scopes/visible.rb index 6ecae100fc3..ee627718e34 100644 --- a/app/models/projects/scopes/visible.rb +++ b/app/models/projects/scopes/visible.rb @@ -40,7 +40,7 @@ module Projects::Scopes def visible(user = User.current) # Use a shortcut for admins and anonymous where # we don't need to calculate for work package roles which is more expensive - if user.admin? || user.anonymous? + if user.active_admin? || user.anonymous? allowed_to(user, :view_project) else active.public_projects.or(active.where(id: user.members.select(:project_id))) diff --git a/app/models/projects/semantic_identifier.rb b/app/models/projects/semantic_identifier.rb new file mode 100644 index 00000000000..100f04de7f3 --- /dev/null +++ b/app/models/projects/semantic_identifier.rb @@ -0,0 +1,97 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module Projects::SemanticIdentifier + extend ActiveSupport::Concern + + # Atomically allocates the next sequence number for a work package in this project + # and returns it paired with the resulting semantic identifier (e.g. [42, "PROJ-42"]). + # Uses an advisory lock scoped to this project to serialize concurrent allocations + # without blocking unrelated project row writes. + def allocate_wp_semantic_identifier! + seq = OpenProject::Mutex.with_advisory_lock( + self.class, + "wp_sequence_#{id}" + ) do + self.class.connection.select_value(<<~SQL.squish) + UPDATE projects + SET wp_sequence_counter = wp_sequence_counter + 1 + WHERE id = #{self.class.connection.quote(id)} + RETURNING wp_sequence_counter + SQL + end + + [seq, "#{identifier}-#{seq}"] + end + + # Called after this project's identifier is renamed. Atomically: + # 1. Appends new-prefix aliases for every WP that ever carried an old-prefix alias. + # 2. Updates identifier on resident WPs to the new prefix. + def handle_semantic_rename(old_identifier, batch_size: 1000) + like_pattern = "#{self.class.sanitize_sql_like(old_identifier)}-%" + prefix = "#{old_identifier}-" + new_prefix = "#{identifier}-" + + WorkPackageSemanticAlias.transaction do + append_aliases_with_new_prefix(like_pattern:, prefix:, new_prefix:, batch_size:) + rewrite_semantic_ids(like_pattern:, prefix:, new_prefix:, batch_size:) + end + end + + private + + # For every alias row whose identifier starts with the old prefix, inserts a + # corresponding row with the new prefix. This covers WPs still in the project + # as well as any that have moved out but still carry old-prefix alias rows. + def append_aliases_with_new_prefix(like_pattern:, prefix:, new_prefix:, batch_size:) + WorkPackageSemanticAlias + .where("identifier LIKE ?", like_pattern) + .in_batches(of: batch_size) do |relation| + now = Time.current + WorkPackageSemanticAlias.connection.execute( + WorkPackageSemanticAlias.sanitize_sql([<<~SQL.squish, { prefix:, new_prefix:, now: }]) + INSERT INTO work_package_semantic_aliases (identifier, work_package_id, created_at, updated_at) + SELECT REPLACE(identifier, :prefix, :new_prefix), work_package_id, :now, :now + FROM (#{relation.to_sql}) AS batch + ON CONFLICT (identifier) DO NOTHING + SQL + ) + end + end + + # Updates the identifier column on all resident WPs to replace the old prefix with the new one. + def rewrite_semantic_ids(like_pattern:, prefix:, new_prefix:, batch_size:) + WorkPackage + .where("identifier LIKE ?", like_pattern) + .in_batches(of: batch_size) do |relation| + relation.update_all(["identifier = REPLACE(identifier, ?, ?)", prefix, new_prefix]) + end + end +end diff --git a/app/models/queries/filters/base.rb b/app/models/queries/filters/base.rb index bf739596141..1b049ce549a 100644 --- a/app/models/queries/filters/base.rb +++ b/app/models/queries/filters/base.rb @@ -79,11 +79,11 @@ class Queries::Filters::Base end def human_name - raise NotImplementedError + raise SubclassResponsibilityError end def type - raise NotImplementedError + raise SubclassResponsibilityError end def allowed_values diff --git a/app/models/queries/filters/shared/parsed_filter.rb b/app/models/queries/filters/shared/parsed_filter.rb index 7d16415cb06..928cc314970 100644 --- a/app/models/queries/filters/shared/parsed_filter.rb +++ b/app/models/queries/filters/shared/parsed_filter.rb @@ -50,11 +50,11 @@ module Queries::Filters::Shared::ParsedFilter private def split_values - raise NotImplementedError + raise SubclassResponsibilityError end def value_conditions - raise NotImplementedError + raise SubclassResponsibilityError end def validate_values diff --git a/app/models/queries/filters/strategies/numeric.rb b/app/models/queries/filters/strategies/numeric.rb index f2b1d938418..66531229922 100644 --- a/app/models/queries/filters/strategies/numeric.rb +++ b/app/models/queries/filters/strategies/numeric.rb @@ -42,11 +42,11 @@ module Queries::Filters::Strategies private def numeric_class - raise NotImplementedError + raise SubclassResponsibilityError end def error_message - raise NotImplementedError + raise SubclassResponsibilityError end def validate_values_all_numeric diff --git a/app/models/queries/group_bys/base.rb b/app/models/queries/group_bys/base.rb index ec2b2772db4..b1adc00d224 100644 --- a/app/models/queries/group_bys/base.rb +++ b/app/models/queries/group_bys/base.rb @@ -45,7 +45,7 @@ module Queries end def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def association_class diff --git a/app/models/queries/operators/base.rb b/app/models/queries/operators/base.rb index 0aa864e84de..c70601a6de1 100644 --- a/app/models/queries/operators/base.rb +++ b/app/models/queries/operators/base.rb @@ -53,7 +53,7 @@ module Queries::Operators end def self.sql_for_field(_values, _db_table, _db_field) - raise NotImplementedError + raise SubclassResponsibilityError end def self.connection diff --git a/app/models/queries/orders/base.rb b/app/models/queries/orders/base.rb index 4d8f4630e6b..ab72786bae3 100644 --- a/app/models/queries/orders/base.rb +++ b/app/models/queries/orders/base.rb @@ -50,7 +50,7 @@ module Queries end def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def apply_to(query_scope) diff --git a/app/models/queries/projects/filters/dynamically_from_project_phase.rb b/app/models/queries/projects/filters/dynamically_from_project_phase.rb index 1b27413f406..7d92fa3df50 100644 --- a/app/models/queries/projects/filters/dynamically_from_project_phase.rb +++ b/app/models/queries/projects/filters/dynamically_from_project_phase.rb @@ -56,7 +56,7 @@ module Queries::Projects::Filters::DynamicallyFromProjectPhase end def key - raise NotImplementedError + raise SubclassResponsibilityError end private @@ -77,7 +77,7 @@ module Queries::Projects::Filters::DynamicallyFromProjectPhase end def create_from_phase(_phase, _context) - raise NotImplementedError + raise SubclassResponsibilityError end def accessor_matches?(definition, match) diff --git a/app/models/queries/projects/filters/filter_on_project_phase.rb b/app/models/queries/projects/filters/filter_on_project_phase.rb index 0b6a5bf4613..ee137dd9492 100644 --- a/app/models/queries/projects/filters/filter_on_project_phase.rb +++ b/app/models/queries/projects/filters/filter_on_project_phase.rb @@ -74,23 +74,23 @@ module Queries::Projects::Filters::FilterOnProjectPhase private def on_date - raise NotImplementedError + raise SubclassResponsibilityError end def on_today - raise NotImplementedError + raise SubclassResponsibilityError end def between_date - raise NotImplementedError + raise SubclassResponsibilityError end def this_week - raise NotImplementedError + raise SubclassResponsibilityError end def none - raise NotImplementedError + raise SubclassResponsibilityError end def project_phase_scope_limit(scope) diff --git a/app/models/queries/relations/filters/visibility_checking.rb b/app/models/queries/relations/filters/visibility_checking.rb index 4a5d102175e..6f4999cb581 100644 --- a/app/models/queries/relations/filters/visibility_checking.rb +++ b/app/models/queries/relations/filters/visibility_checking.rb @@ -54,7 +54,7 @@ module Queries private def visibility_checked_sql(_operator, _values, _visible_sql) - raise NotImplementedError + raise SubclassResponsibilityError end end end diff --git a/app/models/queries/selects/base.rb b/app/models/queries/selects/base.rb index a1d25a8e160..ec4175a41e9 100644 --- a/app/models/queries/selects/base.rb +++ b/app/models/queries/selects/base.rb @@ -32,7 +32,7 @@ class Queries::Selects::Base include ActiveModel::Validations def self.key - raise NotImplementedError + raise SubclassResponsibilityError end def self.available? diff --git a/app/models/queries/work_packages/filter/attachment_base_filter.rb b/app/models/queries/work_packages/filter/attachment_base_filter.rb index a8d97e4b7a4..c4fdb72c69a 100644 --- a/app/models/queries/work_packages/filter/attachment_base_filter.rb +++ b/app/models/queries/work_packages/filter/attachment_base_filter.rb @@ -68,7 +68,7 @@ class Queries::WorkPackages::Filter::AttachmentBaseFilter < Queries::WorkPackage end def search_column - raise NotImplementedError + raise SubclassResponsibilityError end def normalization_type diff --git a/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb b/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb index 5594e280e57..0fefe8cf7cc 100644 --- a/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb +++ b/app/models/queries/work_packages/filter/filter_on_directed_relations_mixin.rb @@ -47,7 +47,7 @@ module Queries::WorkPackages::Filter::FilterOnDirectedRelationsMixin end def relation_type - raise NotImplementedError + raise SubclassResponsibilityError end def normalized_relation_type @@ -57,10 +57,10 @@ module Queries::WorkPackages::Filter::FilterOnDirectedRelationsMixin private def relation_filter - raise NotImplementedError + raise SubclassResponsibilityError end def relation_select - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb b/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb index d8956e65ce6..131c6d37179 100644 --- a/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb +++ b/app/models/queries/work_packages/filter/filter_on_undirected_relations_mixin.rb @@ -42,7 +42,7 @@ module Queries::WorkPackages::Filter::FilterOnUndirectedRelationsMixin end def relation_type - raise NotImplementedError + raise SubclassResponsibilityError end private diff --git a/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb b/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb index 3ce0a5cd23d..8bab4565716 100644 --- a/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb +++ b/app/models/queries/work_packages/filter/or_filter_for_wp_mixin.rb @@ -50,7 +50,7 @@ module Queries::WorkPackages::Filter::OrFilterForWpMixin end def filter_configurations - raise NotImplementedError + raise SubclassResponsibilityError end def create_instances diff --git a/app/models/query.rb b/app/models/query.rb index d72939dcb9c..ed9b540a515 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -223,10 +223,12 @@ class Query < ApplicationRecord end def self.available_columns(project = nil) - Queries::Register - .selects[self] - .map { |col| col.instances(project) } - .flatten + RequestStore.fetch(:"available_columns_#{project&.id}") do + Queries::Register + .selects[self] + .map { |col| col.instances(project) } + .flatten + end end def self.displayable_columns diff --git a/app/models/setting/work_package_identifier.rb b/app/models/setting/work_package_identifier.rb index fb0004bada8..71afe4883f6 100644 --- a/app/models/setting/work_package_identifier.rb +++ b/app/models/setting/work_package_identifier.rb @@ -30,11 +30,12 @@ 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 + def self.semantic_mode_active? = semantic? && OpenProject::FeatureDecisions.semantic_work_package_ids_active? end end diff --git a/app/models/sharing_strategies/base_strategy.rb b/app/models/sharing_strategies/base_strategy.rb index cb47cf22d90..a9061e11974 100644 --- a/app/models/sharing_strategies/base_strategy.rb +++ b/app/models/sharing_strategies/base_strategy.rb @@ -40,36 +40,36 @@ module SharingStrategies def available_roles # format: [{ label: "Role name", value: 42, description: "Role description", default: true }] - raise NotImplementedError, "Override in a subclass and return an array of roles that should be displayed" + raise SubclassResponsibilityError, "Override in a subclass and return an array of roles that should be displayed" end def viewable? - raise NotImplementedError, + raise SubclassResponsibilityError, "Override in a subclass and return true if the current user can view who the entity is shared with" end def manageable? - raise NotImplementedError, "Override in a subclass and return true if the current user can manage sharing" + raise SubclassResponsibilityError, "Override in a subclass and return true if the current user can manage sharing" end def create_contract_class - raise NotImplementedError, "Override in a subclass and return the contract class for creating a share" + raise SubclassResponsibilityError, "Override in a subclass and return the contract class for creating a share" end def update_contract_class - raise NotImplementedError, "Override in a subclass and return the contract class for updating a share" + raise SubclassResponsibilityError, "Override in a subclass and return the contract class for updating a share" end def delete_contract_class - raise NotImplementedError, "Override in a subclass and return the contract class for deleting a share" + raise SubclassResponsibilityError, "Override in a subclass and return the contract class for deleting a share" end def share_description(share) - raise NotImplementedError, "Override in a subclass and return a description for the shared user" + raise SubclassResponsibilityError, "Override in a subclass and return a description for the shared user" end def title - raise NotImplementedError, "Override in a subclass and return a title for the sharing dialog" + raise SubclassResponsibilityError, "Override in a subclass and return a title for the sharing dialog" end def enterprise_feature diff --git a/app/models/type/attributes.rb b/app/models/type/attributes.rb index 343258db690..bc4e787a1f5 100644 --- a/app/models/type/attributes.rb +++ b/app/models/type/attributes.rb @@ -82,10 +82,10 @@ module Type::Attributes WorkPackageCustomField.pluck(Arel.sql("max(updated_at), count(id)")).flatten end - OpenProject::Cache.fetch("all_work_package_form_attributes", - *wp_cf_cache_parts, - EXCLUDED.length, - merge_date) do + OpenProject::Cache.fetch_request_cached("all_work_package_form_attributes", + *wp_cf_cache_parts, + EXCLUDED.length, + merge_date) do calculate_all_work_package_form_attributes(merge_date) end end diff --git a/app/models/type/form_group.rb b/app/models/type/form_group.rb index 7014859dff5..dbdb95d27af 100644 --- a/app/models/type/form_group.rb +++ b/app/models/type/form_group.rb @@ -57,10 +57,10 @@ class Type::FormGroup end def members - raise NotImplementedError + raise SubclassResponsibilityError end def active_members(_project) - raise NotImplementedError + raise SubclassResponsibilityError end end diff --git a/app/models/user.rb b/app/models/user.rb index 707eeaa0b26..03e104a129b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -514,6 +514,10 @@ class User < Principal !logged? end + def active_admin? + admin? && active? + end + def consent_expired? # Always if the user has not consented return true if consented_at.blank? diff --git a/app/models/user_custom_fields/scopes/visible.rb b/app/models/user_custom_fields/scopes/visible.rb index ae7bfab44ba..e1fe79488e0 100644 --- a/app/models/user_custom_fields/scopes/visible.rb +++ b/app/models/user_custom_fields/scopes/visible.rb @@ -34,7 +34,7 @@ module UserCustomFields::Scopes class_methods do def visible(user = User.current) - if user.admin? + if user.active_admin? all else where(admin_only: false) diff --git a/app/models/user_password.rb b/app/models/user_password.rb index 35f027946d9..4b8fd2290cf 100644 --- a/app/models/user_password.rb +++ b/app/models/user_password.rb @@ -103,10 +103,10 @@ class UserPassword < ApplicationRecord # Require the implementation to provide a secure comparison def hash_matches?(_plain) - raise NotImplementedError, "Must be overridden by subclass" + raise SubclassResponsibilityError end def derive_password!(_input) - raise NotImplementedError, "Must be overridden by subclass" + raise SubclassResponsibilityError end end diff --git a/app/models/user_preference.rb b/app/models/user_preference.rb index ecedec0b147..2bf64584442 100644 --- a/app/models/user_preference.rb +++ b/app/models/user_preference.rb @@ -173,18 +173,51 @@ class UserPreference < ApplicationRecord super.presence || { enabled: true, times: ["08:00:00+00:00"] }.with_indifferent_access end + def daily_reminders=(value) + hash = value.to_h.with_indifferent_access + self.settings = settings.merge( + "daily_reminders" => { + "enabled" => ActiveRecord::Type::Boolean.new.cast(hash[:enabled]), + "times" => Array(hash[:times]).compact_blank + } + ) + end + def workdays super || WORKDAYS_FROM_MONDAY_TO_FRIDAY end + def workdays=(value) + self.settings = settings.merge("workdays" => Array(value).map(&:to_i)) + end + def immediate_reminders super.presence || { mentioned: true, personal_reminder: true }.with_indifferent_access end + def immediate_reminders=(value) + self.settings = settings.merge( + "immediate_reminders" => value.to_h.with_indifferent_access.transform_values { |v| ActiveRecord::Type::Boolean.new.cast(v) } + ) + end + + def mentioned + immediate_reminders[:mentioned] + end + + def personal_reminder + immediate_reminders[:personal_reminder] + end + def pause_reminders super.presence || { enabled: false }.with_indifferent_access end + def pause_reminders=(value) + hash = value.to_h.with_indifferent_access + self.settings = settings.merge("pause_reminders" => pause_reminders_hash(hash)) + end + def dismissed_banner?(feature) dismissed_enterprise_banners.key?(feature.to_s) end @@ -206,4 +239,21 @@ class UserPreference < ApplicationRecord def attribute?(name) %i[user user_id].include?(name.to_sym) end + + def pause_reminders_hash(hash) + result = { "enabled" => ActiveRecord::Type::Boolean.new.cast(hash[:enabled]) } + date_fields = if hash[:date_range].present? + parsed_date_range(hash[:date_range]) + else + { "first_day" => hash[:first_day].presence, "last_day" => hash[:last_day].presence } + end + result.merge(date_fields).compact + end + + def parsed_date_range(date_range) + return {} if date_range.blank? + + first_day, last_day = date_range.split(" - ", 2) + { "first_day" => first_day.presence, "last_day" => last_day.presence } + end end diff --git a/app/models/users/function_user.rb b/app/models/users/function_user.rb index f1bbf2f8d3a..95b1c04c04f 100644 --- a/app/models/users/function_user.rb +++ b/app/models/users/function_user.rb @@ -45,7 +45,7 @@ module Users::FunctionUser def builtin? = true - def name(*_args); raise NotImplementedError end + def name(*_args) = raise SubclassResponsibilityError def mail = nil diff --git a/app/models/work_package.rb b/app/models/work_package.rb index 93aa490b535..b80a5ef9427 100644 --- a/app/models/work_package.rb +++ b/app/models/work_package.rb @@ -29,6 +29,7 @@ #++ class WorkPackage < ApplicationRecord + include WorkPackage::SemanticIdentifier include WorkPackage::Validations include WorkPackage::SchedulingRules include WorkPackage::StatusTransitions diff --git a/app/models/work_package/semantic_identifier.rb b/app/models/work_package/semantic_identifier.rb new file mode 100644 index 00000000000..0d8861ac95d --- /dev/null +++ b/app/models/work_package/semantic_identifier.rb @@ -0,0 +1,113 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +module 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 + + # Returns the user-facing identifier for this work package. + # In semantic mode: the project-based identifier (e.g. "PROJ-42") + # In classic mode: the numeric database ID + def display_id + Setting::WorkPackageIdentifier.semantic_mode_active? ? identifier : id + 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/demo_data/references.rb b/app/seeders/demo_data/references.rb index 543e10f821c..9a71c86d505 100644 --- a/app/seeders/demo_data/references.rb +++ b/app/seeders/demo_data/references.rb @@ -48,8 +48,8 @@ module DemoData # # For instance: # - Turns `##sprint:sprint_backlog` into - # `/projects/demo-project/sprints/23/taskboard` given there is a sprint - # referenced with :sprint_backlog and its ID here is 23. + # `/projects/demo-project/backlogs/sprints/23/taskboard` given there is a + # sprint referenced with :sprint_backlog and its ID here is 23. # # Alternatively `##sprint.id:sprint_backlog` is translated into just the # id. @@ -123,7 +123,7 @@ module DemoData end def sprint_link(sprint) - url_helpers.backlogs_project_sprint_taskboard_path( + url_helpers.project_backlogs_sprint_taskboard_path( sprint_id: sprint.id, project_id: sprint.project.identifier ) 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/departments/add_user_service.rb b/app/services/departments/add_user_service.rb new file mode 100644 index 00000000000..2e4cc76ad7f --- /dev/null +++ b/app/services/departments/add_user_service.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 Departments + class AddUserService < ::BaseServices::BaseContracted + def initialize(department, user:, contract_class: AdminOnlyContract) + self.model = department + super(user:, contract_class:) + end + + private + + def persist(call) + user_id = params[:user_id].to_i + existing_department = find_existing_department(user_id) + + if existing_department.nil? || existing_department.id == model.id + add_user_to_department(model, user_id, call) + else + handle_existing_membership(existing_department, user_id, call) + end + + call + end + + def handle_existing_membership(existing_department, user_id, call) + if params[:remove_from_previous_department] + move_user(from: existing_department, to: model, user_id:, call:) + else + call.success = false + call.result = existing_department + end + end + + def find_existing_department(user_id) + GroupUser + .joins(:group) + .merge(Group.organizational_units) + .where(user_id:) + .first + &.group + end + + def add_user_to_department(department, user_id, call) + result = Groups::UpdateService + .new(user:, model: department) + .call(add_user_ids: [user_id]) + + call.add_dependent!(result) + end + + def remove_user_from_department(department, user_id, call) + result = Groups::UpdateService + .new(user:, model: department) + .call(remove_user_ids: [user_id]) + + call.add_dependent!(result) + end + + def move_user(from:, to:, user_id:, call:) + Group.transaction do + remove_user_from_department(from, user_id, call) + raise ActiveRecord::Rollback unless call.success? + + add_user_to_department(to, user_id, call) + raise ActiveRecord::Rollback unless call.success? + end + end + end +end diff --git a/app/services/groups/add_users_service.rb b/app/services/groups/add_users_service.rb index 3740295df17..25cb572189c 100644 --- a/app/services/groups/add_users_service.rb +++ b/app/services/groups/add_users_service.rb @@ -42,6 +42,9 @@ module Groups private def persist(call) + validate_department_membership(call) + return call unless call.success? + sql_query = ::OpenProject::SqlSanitization .sanitize add_to_group, group_id: model.id, @@ -51,6 +54,30 @@ module Groups call end + # The same validation exists in Groups::BaseContract, but it relies on in-memory + # group_users that are new_record?. This service inserts group_users via raw SQL, + # so the contract never sees them. We duplicate the check here against the params directly. + def validate_department_membership(call) + return unless model.organizational_unit? + + conflicts = users_already_in_departments(params[:ids]) + + conflicts.each do |user_id, department_id| + call.errors.add(:group_users, :user_already_in_department, user_id:, department_id:) + end + + call.success = false if conflicts.any? + end + + def users_already_in_departments(user_ids) + GroupUser + .joins(:group) + .merge(Group.organizational_units) + .where(user_id: user_ids) + .where.not(group_id: model.id) + .pluck(:user_id, :group_id) + end + def after_perform(call) create_inherited_roles(model) 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/import/jira_client.rb b/app/services/import/jira_client.rb index ae1d6c00ea5..a37da18f54f 100644 --- a/app/services/import/jira_client.rb +++ b/app/services/import/jira_client.rb @@ -269,7 +269,7 @@ module Import parse_json(response) else raise ApiError.new( - I18n.t("admin.jira.client.api_error", status: response.status), + I18n.t("admin.jira.client.#{response.status}_error", status: response.status, default: :"admin.jira.client.api_error"), status: response.status, response_body: response.body.to_s ) 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/work_packages/identifier_autofix.rb b/app/services/project_identifiers/identifier_autofix.rb similarity index 86% rename from app/services/work_packages/identifier_autofix.rb rename to app/services/project_identifiers/identifier_autofix.rb index b1917080f79..eab2cf347a3 100644 --- a/app/services/work_packages/identifier_autofix.rb +++ b/app/services/project_identifiers/identifier_autofix.rb @@ -28,11 +28,14 @@ # See COPYRIGHT and LICENSE files for more details. #++ -module WorkPackages +module ProjectIdentifiers module IdentifierAutofix def self.job_in_progress? GoodJob::Job - .where(job_class: WorkPackages::IdentifierAutofix::ApplyHandlesJob.name) + .where(job_class: [ + ProjectIdentifiers::ConvertInstanceToSemanticIdsJob.name, + ProjectIdentifiers::RevertInstanceToClassicIdsJob.name + ]) .exists?(finished_at: nil) end end diff --git a/app/services/project_identifiers/identifier_autofix/preview_query.rb b/app/services/project_identifiers/identifier_autofix/preview_query.rb new file mode 100644 index 00000000000..11f9f22ff04 --- /dev/null +++ b/app/services/project_identifiers/identifier_autofix/preview_query.rb @@ -0,0 +1,69 @@ +# 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 ProjectIdentifiers + module IdentifierAutofix + class PreviewQuery + Result = Data.define(:projects_data, :total_count) + DISPLAY_COUNT = 5 + + def call + analysis = ProblematicIdentifiers.new + total_count = analysis.count + projects_data = build_projects_data(analysis) + + Result.new(projects_data:, total_count:) + end + + private + + def build_projects_data(analysis) + generate_suggestions(analysis).map do |entry| + entry.merge(error_reason: analysis.error_reason(entry[:current_identifier])) + end + end + + def generate_suggestions(analysis) + ProjectIdentifierSuggestionGenerator.call( + preview_projects(analysis.scope), + exclude: analysis.exclusion_set.to_set(&:upcase) + ) + end + + def preview_projects(scope) + scope + .select(:id, :name, :identifier) + .order(:id) + .limit(DISPLAY_COUNT) + .to_a + end + end + end +end diff --git a/app/services/project_identifiers/identifier_autofix/problematic_identifiers.rb b/app/services/project_identifiers/identifier_autofix/problematic_identifiers.rb new file mode 100644 index 00000000000..a4d0b765f3a --- /dev/null +++ b/app/services/project_identifiers/identifier_autofix/problematic_identifiers.rb @@ -0,0 +1,126 @@ +# 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 ProjectIdentifiers + 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 + # Returns all project identifiers (current and historical) tracked by + # FriendlyId's slug history. Useful as an exclusion set when generating + # new identifiers, since any slug that was ever in use must not be reused. + def self.reserved_identifiers + FriendlyId::Slug.where(sluggable_type: Project.name).pluck(:slug).to_set + end + + # 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 + historical_identifiers | in_use_identifiers + end + + private + + def historical_identifiers + @historical_identifiers ||= FriendlyId::Slug + .where(sluggable_type: Project.name) + .where("LOWER(slug) NOT IN (SELECT LOWER(identifier) FROM projects)") + .pluck(:slug) + .to_set + end + + 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 historical_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 + + end + end +end diff --git a/app/services/work_packages/identifier_autofix/project_identifier_suggestion_generator.rb b/app/services/project_identifiers/identifier_autofix/project_identifier_suggestion_generator.rb similarity index 99% rename from app/services/work_packages/identifier_autofix/project_identifier_suggestion_generator.rb rename to app/services/project_identifiers/identifier_autofix/project_identifier_suggestion_generator.rb index 10af6ce4777..5945eb60db4 100644 --- a/app/services/work_packages/identifier_autofix/project_identifier_suggestion_generator.rb +++ b/app/services/project_identifiers/identifier_autofix/project_identifier_suggestion_generator.rb @@ -28,7 +28,7 @@ # See COPYRIGHT and LICENSE files for more details. #++ -module WorkPackages +module ProjectIdentifiers module IdentifierAutofix # Generates a short uppercase semantic identifier for each project. # 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 deleted file mode 100644 index 0c3c54ea341..00000000000 --- a/app/services/work_packages/identifier_autofix/preview_query.rb +++ /dev/null @@ -1,89 +0,0 @@ -# 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 - class PreviewQuery - Result = Data.define(:projects_data, :total_count) - DISPLAY_COUNT = 5 - - def call - total = problematic_scope.count - preview = problematic_scope - .select(:id, :name, :identifier) - .limit(DISPLAY_COUNT) - .to_a - - 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) - 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 - end - end - - def in_use_identifiers - @in_use_identifiers ||= Project.where.not(id: problematic_scope.select(:id)).pluck(:identifier).to_set - end - - def reserved_identifiers - # TODO: OldProjectIdentifier.pluck(:identifier).to_set - # once the OldProjectIdentifier model and migration are added. - Set.new - 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/modules/backlogs/app/views/rb_tasks/index.html.erb b/app/views/admin/departments/_general.html.erb similarity index 73% rename from modules/backlogs/app/views/rb_tasks/index.html.erb rename to app/views/admin/departments/_general.html.erb index 8631ae80618..edbd5dd3521 100644 --- a/modules/backlogs/app/views/rb_tasks/index.html.erb +++ b/app/views/admin/departments/_general.html.erb @@ -27,10 +27,8 @@ See COPYRIGHT and LICENSE files for more details. ++#%> -
-
<%= date_string_with_milliseconds((@last_updated.blank? ? Time.zone.parse(params[:after]) : @last_updated.updated_at), 0.001) %>
- <%= render partial: "task", collection: @tasks, locals: { include_meta: @include_meta } %> - <%- if @impediments %> - <%= render partial: "impediment", collection: @impediments, locals: { include_meta: @include_meta } %> - <%- end %> -
+<%= + settings_primer_form_with(model: @group, url: admin_department_path(@group), method: :put) do |f| + render Groups::Form.new(f) + end +%> diff --git a/app/views/admin/departments/_memberships.html.erb b/app/views/admin/departments/_memberships.html.erb new file mode 100644 index 00000000000..1f862da9523 --- /dev/null +++ b/app/views/admin/departments/_memberships.html.erb @@ -0,0 +1,165 @@ +<%#-- 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. + +++#%> +<% roles = ProjectRole.givable %> +<% projects = Project.active.order(Arel.sql("lft")) %> +<% memberships = @group.memberships %> + +
+
+ <% if @group.memberships.any? %> +
+
+ + + + + + + + + + + + + + + <% memberships.where.not(project: nil).find_each do |membership| %> + <% next if membership.new_record? %> + + + + + + <% end %> + +
+
+
+ + <%= Project.model_name.human %> + +
+
+
+
+
+ + <%= t(:label_role_plural) %> + +
+
+
+
+
+ <%= link_to membership.project.name, project_members_path(membership.project) %> + + + <%= h membership.roles.sort.join(", ") %> + + <%= labelled_tabular_form_for( + :membership, + url: membership_of_admin_department_path(@group, membership), + method: :patch, + id: "member-#{membership.id}-roles-form", + data: { reveal_target: "item" }, + html: { class: "d-none" } + ) do |f| %> + <% roles.each do |role| %> + + <% end %> +

+ <%= submit_tag t(:button_change), class: "memberships--edit-submit-button button -primary -small" %> + <%= button_tag t(:button_cancel), type: "button", + data: { action: "reveal#toggle" }, + class: "button -small" %> +

+ <% end %> +
+ <%= link_to( + icon_wrapper("icon icon-edit", t(:button_edit)), + "#", + data: { action: "reveal#toggle", reveal_target: "item" }, + aria: { label: t(:button_edit) } + ) %> + <%= link_to( + icon_wrapper("icon icon-remove", t(:button_remove)), + membership_of_admin_department_path(@group, membership), + data: { turbo_method: :delete }, + aria: { label: t(:button_remove) } + ) %> +
+ +
+
+ <% else %> + <%= no_results_box %> + <% end %> +
+
+ <% if projects.any? %> + <%= styled_form_tag(memberships_of_admin_department_path(@group), method: :post) do %> +
+ <%= t(:label_project_new) %> + <%= label_tag "membership_project_id", t(:description_choose_project), class: "sr-only" %> + + <% group_project_ids = @group.projects.ids %> + <% filters = [{ name: "active", operator: "=", values: ["t"] }] %> + <% filters << { name: "id", operator: "!", values: group_project_ids.map(&:to_s) } if group_project_ids.any? %> + <%= angular_component_tag "opce-project-autocompleter", + inputs: { + filters: filters, + inputName: "membership[project_id]" + }, + data: { + "test-selector": "membership_project_id" + }, + class: "form--field" %> +
+ <%= styled_label_tag nil, "#{t(:label_role_plural)}:" %> +
+ <%= labeled_check_box_tags "membership[role_ids][]", roles %> +
+
+
<%= styled_button_tag t(:button_add), class: "-primary -with-icon icon-checkmark" %>
+
+ <% end %> + <% end %> +
+
diff --git a/app/views/admin/departments/edit.html.erb b/app/views/admin/departments/edit.html.erb new file mode 100644 index 00000000000..a6fef515639 --- /dev/null +++ b/app/views/admin/departments/edit.html.erb @@ -0,0 +1,36 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) the OpenProject GmbH + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License version 3. + +OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +Copyright (C) 2006-2013 Jean-Philippe Lang +Copyright (C) 2010-2013 the ChiliProject Team + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +See COPYRIGHT and LICENSE files for more details. + +++#%> + +<% html_title t(:label_administration), "#{t(:label_edit)} #{Group.model_name.human} #{h @group.name}" %> + +<% tabs = department_settings_tabs(@group) %> + +<%= render Admin::Departments::EditPageHeaderComponent.new(group: @group, current_user:, tabs: tabs) %> + +<%= render_tabs tabs %> diff --git a/app/views/admin/departments/index.html.erb b/app/views/admin/departments/index.html.erb new file mode 100644 index 00000000000..7b8260adb2c --- /dev/null +++ b/app/views/admin/departments/index.html.erb @@ -0,0 +1,3 @@ +<%= render(Admin::Departments::PageHeaderComponent.new) %> + +<%= render(Admin::Departments::HierarchyLayoutComponent.new(groups: @groups, active_group: @group, add_user: @add_user, add_subgroup: @add_subgroup)) %> diff --git a/app/views/admin/departments/new_department.html.erb b/app/views/admin/departments/new_department.html.erb new file mode 100644 index 00000000000..197f97661e2 --- /dev/null +++ b/app/views/admin/departments/new_department.html.erb @@ -0,0 +1,8 @@ +<%= render( + Admin::Departments::DetailComponent.new( + group: @group, + ancestors: @ancestors || [], + child_groups: @child_groups, + add_subgroup: true + ) + ) %> diff --git a/app/views/admin/departments/new_user.html.erb b/app/views/admin/departments/new_user.html.erb new file mode 100644 index 00000000000..486e6b9731c --- /dev/null +++ b/app/views/admin/departments/new_user.html.erb @@ -0,0 +1,8 @@ +<%= render( + Admin::Departments::DetailComponent.new( + group: @group, + ancestors: @ancestors, + child_groups: @child_groups, + add_user: true + ) + ) %> 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/project_identifiers/convert_instance_to_semantic_ids_job.rb b/app/workers/project_identifiers/convert_instance_to_semantic_ids_job.rb new file mode 100644 index 00000000000..29d6613aadf --- /dev/null +++ b/app/workers/project_identifiers/convert_instance_to_semantic_ids_job.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class ProjectIdentifiers::ConvertInstanceToSemanticIdsJob < ApplicationJob + include GoodJob::ActiveJobExtensions::Concurrency + + good_job_control_concurrency_with(total_limit: 1) + + def perform(*); end +end diff --git a/app/workers/project_identifiers/revert_instance_to_classic_ids_job.rb b/app/workers/project_identifiers/revert_instance_to_classic_ids_job.rb new file mode 100644 index 00000000000..2321aabc5bb --- /dev/null +++ b/app/workers/project_identifiers/revert_instance_to_classic_ids_job.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +#++ + +class ProjectIdentifiers::RevertInstanceToClassicIdsJob < ApplicationJob + include GoodJob::ActiveJobExtensions::Concurrency + + good_job_control_concurrency_with(total_limit: 1) + + def perform(*); 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/bin/setup_dev b/bin/setup_dev index 06962d647bd..9eb4dfbd4c2 100755 --- a/bin/setup_dev +++ b/bin/setup_dev @@ -41,5 +41,10 @@ echo 'prefer using Overmind over Foreman if available.' echo "" echo 'Setting up git hooks' -echo 'If you want to have commit hooks for rubocop and eslint errors, you can install lefthook like so:' -echo '`bundle exec lefthook install`' +echo 'If you want to have commit hooks for rubocop and eslint errors, you can use lefthook.' +echo 'We provide a `lefthook.yml`. To install lefthook, use an installation method of your choice, e.g.:' +echo '`gem install lefthook`' +echo '(Do not forget to make the executable available on the $PATH.)' +echo "" +echo 'To configure the git hooks:' +echo '`lefthook install`' diff --git a/config/constants/settings/definition.rb b/config/constants/settings/definition.rb index 2d6d5375dcc..73b710c064d 100644 --- a/config/constants/settings/definition.rb +++ b/config/constants/settings/definition.rb @@ -78,6 +78,9 @@ module Settings app_title: { default: "OpenProject" }, + organization_name: { + default: "My Organization" + }, attachment_max_size: { default: 5120 }, @@ -1323,12 +1326,12 @@ module Settings }, work_packages_identifier: { description: "Defines how work packages are identified in the UI (e.g. in links and titles). " \ - "The 'numeric' option uses the work package numerical ID, " \ - "while 'alphanumeric' uses the project identifier and the work package ID separated by a dash " \ + "The 'classic' option uses the work package numerical ID, " \ + "while 'semantic' uses the project identifier and the work package ID separated by a dash " \ "(e.g. 'PROJA-123').", format: :string, allowed: -> { Setting::WorkPackageIdentifier::ALLOWED_VALUES }, - default: "numeric" + default: "classic" }, work_package_list_default_highlighted_attributes: { default: ["status", "priority", "due_date"], @@ -1428,6 +1431,10 @@ module Settings end def value + unless (override = resolve_value_override).nil? + return cast(override) + end + cast(@value) end @@ -1444,6 +1451,8 @@ module Settings end def writable? + return false if value_override? + if writable.respond_to?(:call) writable.call else @@ -1567,6 +1576,38 @@ module Settings @all ||= {} end + # Registers a value override block for a setting. The block is called + # whenever the setting's value or writability is evaluated. + # + # If the block returns a non-nil value, that value is used as the setting's + # value and the setting becomes non-writable. If the block returns nil, + # no override is applied. + # + # To override a setting with nil, return a callable: +-> { nil }+ + # + # @param name [Symbol] The setting name to override. + # @yield A block that returns the override value, or nil to skip. + # + # @example Force a setting to true when a condition is met + # Settings::Definition.add_value_override(:capture_external_links) do + # true if MyPlugin.active? + # end + def add_value_override(name, &block) + (value_overrides[name.to_sym] ||= []) << block + end + + def value_overrides + @value_overrides ||= {} + end + + def clear_value_overrides(name = nil) + if name + value_overrides.delete(name.to_sym) + else + @value_overrides = {} + end + end + private def file_config @@ -1760,6 +1801,18 @@ module Settings attr_accessor :serialized, :writable + def value_override? + !resolve_value_override.nil? + end + + def resolve_value_override + self.class.value_overrides[name.to_sym]&.each do |block| + result = block.call + return result unless result.nil? + end + nil + end + def cast(value) return nil if value.nil? diff --git a/config/initializers/feature_decisions.rb b/config/initializers/feature_decisions.rb index e8101395c4a..da186951a1f 100644 --- a/config/initializers/feature_decisions.rb +++ b/config/initializers/feature_decisions.rb @@ -61,17 +61,15 @@ OpenProject::FeatureDecisions.add :jira_import, description: "Enables Jira Migration Tool.", force_active: false -OpenProject::FeatureDecisions.add :scrum_projects, - description: "Enables an overhauled version of the backlogs module to " \ - "support Scrum projects with a new sprint planning experience. ", - force_active: true - OpenProject::FeatureDecisions.add :user_working_times, description: "Enables tracking of user working hours and non-working days." OpenProject::FeatureDecisions.add :wiki_enhancements, description: "Enables Wiki enhancements, such as the Wikis tab and XWiki integration." +OpenProject::FeatureDecisions.add :departments, + description: "Enables the management of departments within the organization." + OpenProject::FeatureDecisions.add :semantic_work_package_ids, description: "Enables the use of semantic work package IDs, " \ "in the schema -. " \ diff --git a/config/initializers/homescreen.rb b/config/initializers/homescreen.rb index 70b11bffdb9..932eca9cf78 100644 --- a/config/initializers/homescreen.rb +++ b/config/initializers/homescreen.rb @@ -45,8 +45,7 @@ OpenProject::Static::Homescreen.manage :blocks do |blocks| if: Proc.new { OpenProject::Configuration.show_community_links? } }, { - name: "users", - if: Proc.new { User.current.admin? } + name: "meetings" }, { name: "my_account", diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb index 733be24b1cd..42425cb03e2 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| @@ -365,6 +362,12 @@ Redmine::MenuManager.map :admin_menu do |menu| caption: :label_group_plural, parent: :users_and_permissions + menu.push :departments, + { controller: "/admin/departments" }, + if: ->(_) { User.current.admin? && OpenProject::FeatureDecisions.departments_active? }, + caption: :label_departments, + parent: :users_and_permissions + menu.push :roles, { controller: "/roles" }, if: ->(_) { User.current.admin? }, @@ -629,6 +632,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? }, @@ -668,12 +677,6 @@ Redmine::MenuManager.map :admin_menu do |menu| icon: "op-enterprise-addons", if: proc { User.current.admin? && OpenProject::Configuration.ee_manager_visible? } - menu.push :admin_backlogs, - { controller: "/backlogs_settings", action: :show }, - if: ->(_) { User.current.admin? }, - caption: :label_backlogs, - icon: "op-backlogs" - menu.push :import, { controller: "/admin/import/jira/instances", action: :index }, if: ->(_) { User.current.admin? && OpenProject::FeatureDecisions.jira_import_active? }, diff --git a/config/initializers/meta_tags.rb b/config/initializers/meta_tags.rb new file mode 100644 index 00000000000..6483cdbba9c --- /dev/null +++ b/config/initializers/meta_tags.rb @@ -0,0 +1,33 @@ +# 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. +#++ + +MetaTags.configure do |config| + config.title_limit = nil +end diff --git a/config/initializers/permissions.rb b/config/initializers/permissions.rb index 9d7eca42f1e..67b18ebc2fa 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] }, @@ -431,7 +435,7 @@ Rails.application.reloader.to_prepare do wpt.permission :delete_work_packages, { work_packages: :destroy, - "work_packages/bulk": %i[destroy reassign] + "work_packages/bulk": %i[delete_dialog destroy reassign] }, permissible_on: :project, require: :member, diff --git a/app/workers/work_packages/identifier_autofix/apply_handles_job.rb b/config/initializers/text_formatting.rb similarity index 73% rename from app/workers/work_packages/identifier_autofix/apply_handles_job.rb rename to config/initializers/text_formatting.rb index ff2107b4a3e..e6a51576830 100644 --- a/app/workers/work_packages/identifier_autofix/apply_handles_job.rb +++ b/config/initializers/text_formatting.rb @@ -23,18 +23,17 @@ # # 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. +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See COPYRIGHT and LICENSE files for more details. #++ -class WorkPackages::IdentifierAutofix::ApplyHandlesJob < ApplicationJob - # FIXME: The admin UI's job_in_progress? query and :change_in_progress state - # assume at most one active instance of this job at any given time. - # Enforce this with good_job_control_concurrency_with(perform_limit: 1) - # when the real migration body is implemented. - def perform - # FIXME: replace with actual project handle migration - sleep 5 +Rails.application.configure do |application| + application.config.to_prepare do + namespace = OpenProject::TextFormatting + + namespace::Filters::PatternMatcherFilter.append_matcher namespace::Matchers::ResourceLinksMatcher + namespace::Filters::PatternMatcherFilter.append_matcher namespace::Matchers::WikiLinksMatcher + namespace::Filters::PatternMatcherFilter.append_matcher namespace::Matchers::AttributeMacros end end diff --git a/config/locales/crowdin/af.yml b/config/locales/crowdin/af.yml index eb42e744c61..ffa87615856 100644 --- a/config/locales/crowdin/af.yml +++ b/config/locales/crowdin/af.yml @@ -114,7 +114,7 @@ af: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ af: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ af: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ af: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ af: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ af: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ af: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ af: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ af: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ af: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ af: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ af: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ af: avatar: Avatar base: 'Algemene fout:' body: Body - blocks_ids: ID's van geblokkeerde werkspakkette category: Kategorie comment: Opmerking comments: Opmerking @@ -2922,6 +2977,7 @@ af: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ af: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ af: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Gedelegeerde @@ -3484,6 +3611,7 @@ af: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Rekening + label_actions: Actions label_active: Aktiewe label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ af: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Toegepaste status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ af: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Teken af label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Kant kieslys @@ -4884,6 +5017,7 @@ af: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ af: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ar.yml b/config/locales/crowdin/ar.yml index 10ecf89720c..7ab6760119b 100644 --- a/config/locales/crowdin/ar.yml +++ b/config/locales/crowdin/ar.yml @@ -114,7 +114,7 @@ ar: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ar: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ar: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ar: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -411,9 +412,9 @@ ar: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -422,10 +423,14 @@ ar: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: zero: "... %{count} more projects" one: "... 1 more project" @@ -444,7 +449,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 @@ -714,6 +719,37 @@ ar: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1409,6 +1445,20 @@ ar: index: no_results_title_text: لا يوجد حالياً أي سير عمل. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1608,6 +1658,9 @@ ar: dependencies: الاعتماديات activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1616,7 +1669,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: @@ -1872,6 +1925,7 @@ ar: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1989,6 +2043,7 @@ ar: confirmation: لا يتطابق مع %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: غير موجود. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2055,6 +2110,7 @@ ar: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2615,7 +2671,6 @@ ar: avatar: الصورة الرمزية base: 'خطأ عام:' body: Body - blocks_ids: مُعرِّفات مجموعات العمل المحظورة category: الفئة comment: التعليق comments: تعليق @@ -3152,6 +3207,7 @@ ar: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3625,6 +3681,11 @@ ar: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3687,6 +3748,72 @@ ar: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: المُسند إليه @@ -3718,6 +3845,7 @@ ar: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: الحساب + label_actions: Actions label_active: مفعّل label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3758,6 +3886,7 @@ ar: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: الحالة المطبقة label_archive_project: Archive project @@ -4008,7 +4137,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: آخر نشاط @@ -4046,6 +4175,10 @@ ar: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: تسجيل الخروج label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: القائمة الثانوية @@ -5130,6 +5263,7 @@ ar: ' setting_app_subtitle: العنوان الفرعي للتطبيق setting_app_title: عنوان التطبيق + setting_organization_name: Organization name setting_attachment_max_size: الحد الأقصى لحجم المرفقات setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5276,12 +5410,12 @@ ar: setting_welcome_text: نص كتلة الترجيب setting_welcome_title: عنوان كتلة الترحيب setting_welcome_on_homescreen: كتلة الترحيب عرض على الشاشة الرئيسية - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/az.yml b/config/locales/crowdin/az.yml index c416002675c..7ef033c2393 100644 --- a/config/locales/crowdin/az.yml +++ b/config/locales/crowdin/az.yml @@ -114,7 +114,7 @@ az: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ az: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ az: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ az: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ az: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ az: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ az: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ az: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ az: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ az: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ az: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ az: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ az: avatar: Avatar base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ az: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ az: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ az: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ az: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ az: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ az: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ az: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ az: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/be.yml b/config/locales/crowdin/be.yml index 48774b98d06..549d83b38f6 100644 --- a/config/locales/crowdin/be.yml +++ b/config/locales/crowdin/be.yml @@ -114,7 +114,7 @@ be: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ be: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ be: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ be: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +400,9 @@ be: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +411,14 @@ be: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -430,7 +435,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 @@ -698,6 +703,37 @@ be: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1375,6 +1411,20 @@ be: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1572,6 +1622,9 @@ be: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1580,7 +1633,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: @@ -1836,6 +1889,7 @@ be: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1953,6 +2007,7 @@ be: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2019,6 +2074,7 @@ be: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2541,7 +2597,6 @@ be: avatar: Аватар base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -3038,6 +3093,7 @@ be: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3509,6 +3565,11 @@ be: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3571,6 +3632,72 @@ be: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Прызначаная асоба @@ -3602,6 +3729,7 @@ be: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3642,6 +3770,7 @@ be: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3892,7 +4021,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 @@ -3930,6 +4059,10 @@ be: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -5012,6 +5145,7 @@ be: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5158,12 +5292,12 @@ be: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/bg.yml b/config/locales/crowdin/bg.yml index 9efe399f0d6..8b6a29190f0 100644 --- a/config/locales/crowdin/bg.yml +++ b/config/locales/crowdin/bg.yml @@ -114,7 +114,7 @@ bg: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ bg: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ bg: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ bg: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ bg: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ bg: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ bg: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ bg: index: no_results_title_text: В момента няма работни потоци. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ bg: dependencies: Зависимости activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ bg: identity_url: URL адрес на идентичност parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ bg: confirmation: не съвпада с %{attribute}. could_not_be_copied: "%{dependency} не може да бъде (напълно) копирана." does_not_exist: не съществува. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: не може да бъде осъществен достъп. error_readonly: направен е неуспешен опит за запис @@ -1983,6 +2038,7 @@ bg: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2463,7 +2519,6 @@ bg: avatar: Аватар base: 'Обща грешка:' body: Body - blocks_ids: ИД на блокираните работни пакети category: Категория comment: Коментар comments: Коментар @@ -2920,6 +2975,7 @@ bg: title: Добавка за големи предприятия plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3389,6 +3445,11 @@ bg: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3451,6 +3512,72 @@ bg: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Изпълнител @@ -3482,6 +3609,7 @@ bg: invalid_filter: Invalid notification filter label_accessibility: Достъпност label_account: Акаунт + label_actions: Действия label_active: Активен label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3522,6 +3650,7 @@ bg: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Отмени + label_integrations: Integrations label_add_column: Add column label_applied_status: Приложен статус label_archive_project: Archive project @@ -3772,7 +3901,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: Последна активност @@ -3810,6 +3939,10 @@ bg: label_custom_pdf_export_settings: Потребителски настройки за експортиране на PDF label_custom_favicon: Персонализиран favicon label_custom_touch_icon: Потребителска икона за докосване + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Изход label_mapping_for: 'Картографиране за: %{attribute}' label_main_menu: Главното меню @@ -4878,6 +5011,7 @@ bg: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5024,12 +5158,12 @@ bg: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ca.yml b/config/locales/crowdin/ca.yml index eda8b90f7d2..76d2317774a 100644 --- a/config/locales/crowdin/ca.yml +++ b/config/locales/crowdin/ca.yml @@ -114,7 +114,7 @@ ca: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ca: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ca: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ca: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ ca: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ ca: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -681,6 +686,37 @@ ca: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1340,6 +1376,20 @@ ca: index: no_results_title_text: Actualment no hi ha cap flux de treball. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1535,6 +1585,9 @@ ca: dependencies: Dependències activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1543,7 +1596,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: @@ -1799,6 +1852,7 @@ ca: identity_url: URL d'identitat parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1916,6 +1970,7 @@ ca: confirmation: no coincideix amb el %{attribute}. could_not_be_copied: "%{dependency} no s'ha pogut copiar (completament)." does_not_exist: no existeix. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: no és possible accedir. error_readonly: es va intentar d'escriure-hi però no és modificable. @@ -1982,6 +2037,7 @@ ca: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2462,7 +2518,6 @@ ca: avatar: Avatar base: 'Error General:' body: Body - blocks_ids: Identificadors dels paquets de treball bloquejats category: Categoria comment: Comentari comments: Comentari @@ -2919,6 +2974,7 @@ ca: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Il·limitat already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3388,6 +3444,11 @@ ca: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3450,6 +3511,72 @@ ca: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignat a @@ -3481,6 +3608,7 @@ ca: invalid_filter: Invalid notification filter label_accessibility: Accessibilitat label_account: Compte + label_actions: Accions label_active: Actiu label_activate_user: Activa usuari label_active_in_new_projects: Activa a nous projectes @@ -3521,6 +3649,7 @@ ca: label_ical_access_key_generation_hint: Generat automàticament en subscriure't a un calendari. label_ical_access_key_latest: úlitma label_ical_access_key_revoke: Revoca + label_integrations: Integrations label_add_column: Add column label_applied_status: Estat aplicat label_archive_project: Arxiva el projecte @@ -3771,7 +3900,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 @@ -3809,6 +3938,10 @@ ca: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Favicon personalitzat label_custom_touch_icon: Icona "touch" personalitzada + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Tancar sessió label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Menú lateral @@ -4871,6 +5004,7 @@ ca: ' setting_app_subtitle: Subtítol de l'aplicació setting_app_title: Títol de l'aplicació + setting_organization_name: Organization name setting_attachment_max_size: Mida màxima dels fitxers adjunts setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5017,12 +5151,12 @@ ca: setting_welcome_text: Bloc de text de benvinguda setting_welcome_title: Títol del bloc de benvinguda setting_welcome_on_homescreen: Mostrar el bloc de benvinguda a la pàgina d'inici - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Mètode de destacament per defecte diff --git a/config/locales/crowdin/ckb-IR.yml b/config/locales/crowdin/ckb-IR.yml index 953730cf1b2..c1fde31a195 100644 --- a/config/locales/crowdin/ckb-IR.yml +++ b/config/locales/crowdin/ckb-IR.yml @@ -114,7 +114,7 @@ ckb-IR: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ckb-IR: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ckb-IR: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ckb-IR: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ ckb-IR: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ ckb-IR: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ ckb-IR: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ ckb-IR: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ ckb-IR: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ ckb-IR: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ ckb-IR: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ ckb-IR: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ ckb-IR: avatar: Avatar base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ ckb-IR: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ ckb-IR: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ ckb-IR: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ ckb-IR: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ ckb-IR: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ ckb-IR: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ ckb-IR: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ ckb-IR: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/cs.yml b/config/locales/crowdin/cs.yml index e58bd2eba08..397125be903 100644 --- a/config/locales/crowdin/cs.yml +++ b/config/locales/crowdin/cs.yml @@ -114,7 +114,7 @@ cs: import: title: Import jira: - title: Jira import + title: Jira Migrátor description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ cs: title: Jira configuration new: Nová konfigurace banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ cs: connection_timeout: 'Připojení k serveru Jira vypršelo: %{message}' parse_error: 'Nepodařilo se zpracovat odpověď z Jira API: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projekty last_change: Poslední změna @@ -162,7 +163,7 @@ cs: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +400,9 @@ cs: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +411,14 @@ cs: label_autofixed_suggestion: Budoucí identifikátor label_example_work_package_id: Příklad ID pracovního balíčku autofix_preview: - error_too_long: Musí mít méně než 5 znaků + error_too_long: Musí obsahovat maximálně 10 znaků + error_numerical: Nemůže být čistě číselný + error_starts_with_number: Nemůže začínat číslem error_special_characters: Speciální znaky nejsou povoleny + error_not_fully_uppercased: Musí být napsán velkými písmeny error_in_use: Již se používá jako identifikátor jiného projektu error_reserved: Již se dříve používal jako identifikátor jiného projektu + error_unknown: Potřebuje ruční revizi remaining_projects: one: "... 1 další projekt" few: "... %{count} more projects" @@ -430,7 +435,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 @@ -698,6 +703,37 @@ cs: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Stránkování prev: Předchozí @@ -1375,6 +1411,20 @@ cs: index: no_results_title_text: V současné době neexistují žádné pracovní postupy. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1572,6 +1622,9 @@ cs: dependencies: Závislosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projekty import/jira: @@ -1580,7 +1633,7 @@ cs: personal_access_token: Personal access token import/jira_open_project_reference: jira: Jira - jira_import: Jira import + jira_import: Jira Migrator announcements: show_until: Zobrazit do attachment: @@ -1697,7 +1750,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 @@ -1838,6 +1891,7 @@ cs: identity_url: Adresa URL identity parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1955,6 +2009,7 @@ cs: confirmation: neshoduje se s %{attribute}. could_not_be_copied: "%{dependency} nemůže být (zcela) zkopírován." does_not_exist: neexistuje. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: není přístupný. error_readonly: se pokusil být napsán, ale není zapisovatelný. @@ -2021,6 +2076,7 @@ cs: attributes: parent_id: circular_dependency: by vytvořil kruhovou hierarchii skupin. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2102,7 +2158,7 @@ cs: message: cannot_move_message_to_forum_of_different_project: A message cannot be moved to a forum of a different project. notifications: - at_least_one_channel: Pro odesílání notifikací musí být specifikován alespoň jeden kanál + at_least_one_channel: Alespoň jeden kanál pro odesílání oznámení musí být specifikován. attributes: read_ian: read_on_creation: 'nelze nastavit na pravdivé při vytváření oznámení ' @@ -2409,11 +2465,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 @@ -2543,7 +2599,6 @@ cs: avatar: Avatar base: 'Obecná chyba:' body: Body - blocks_ids: ID blokovaných pracovních balíčků category: Kategorie comment: Komentář comments: Komentář @@ -3040,6 +3095,7 @@ cs: title: Doplňky podnikových plánů plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: K dispozici od plánu %{plan_name}. unlimited: Bez omezení already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3505,12 +3561,17 @@ cs: ai: Umělá Inteligence (AI) aggregation: Agregace api_and_webhooks: API & Webhooky - mail_notification: E-mailové notifikace + mail_notification: E-mailová upozornění mails_and_notifications: E-maily a oznámení mcp_configurations: Protokol MCP (Model Context Protocol) quick_add: label: Přidat… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Poskytovatelské tokeny vydává OpenProject a umožňuje tím přístup ostatním aplikacím. Klientské tokeny jsou vydávány jinými aplikacemi a umožňují OpenProjectu přistup k nim. no_results: @@ -3573,6 +3634,72 @@ cs: disabled_text: RSS tokeny nejsou administrátorem povoleny. Pro použití této funkce kontaktujte svého správce. storages: unknown_storage: Neznámé úložiště + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Řešitel @@ -3591,7 +3718,7 @@ cs: by_project: Nepřečteno dle projektu by_reason: Důvod inbox: Doručená pošta - send_notifications: Pro tuto akci odeslat notifikaci + send_notifications: Odeslat oznámení pro tuto akci work_packages: subject: created: Pracovní balíček byl vytvořen. @@ -3604,6 +3731,7 @@ cs: invalid_filter: Neplatný filtr oznámení label_accessibility: Přístupnost label_account: Účet + label_actions: Akce label_active: Aktivní label_activate_user: Aktivovat uživatele label_active_in_new_projects: Aktivní v nových projektech @@ -3644,6 +3772,7 @@ cs: label_ical_access_key_generation_hint: Automaticky vygenerováno při odebírání kalendáře. label_ical_access_key_latest: poslední label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Přidat sloupec label_applied_status: Přiřazený stav label_archive_project: Archivovat projekt @@ -3894,7 +4023,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 @@ -3932,6 +4061,10 @@ cs: label_custom_pdf_export_settings: Vlastní nastavení exportu PDF label_custom_favicon: Vlastní favicon label_custom_touch_icon: Vlastní ikona dotyku + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Odhlásit se label_mapping_for: 'Mapování pro: %{attribute}' label_main_menu: Boční Menu @@ -4043,9 +4176,9 @@ cs: label_permissions: Práva label_permissions_report: Přehled oprávnění label_personalize_page: Přizpůsobit tuto stránku - label_placeholder_user: Placeholder uživatel + label_placeholder_user: placeholder uživatel label_placeholder_user_new: '' - label_placeholder_user_plural: Placeholder uživatelé + label_placeholder_user_plural: placeholder uživatelé label_planning: Plánování label_please_login: Přihlaste se prosím label_plugins: Pluginy @@ -4070,7 +4203,7 @@ cs: label_project_attribute_plural: Atributy projektu label_project_attribute_manage_link: Správa atributů produktu label_project_count: Celkový počet projektů - label_project_copy_notifications: Během kopírování projektu odeslat notifikace e-mailem + label_project_copy_notifications: Během kopie projektu odeslat oznámení e-mailem label_project_initiation_export_pdf: Export PDF for %{project_creation_name} label_project_latest: Nejnovější projekty label_project_default_type: Povolit prázdný typ @@ -4231,7 +4364,7 @@ cs: label_version_new: Nová verze label_version_edit: Upravit verzi label_version_plural: Verze - label_version_sharing_descendants: S podprojekty + label_version_sharing_descendants: S Podprojekty label_version_sharing_hierarchy: S hierarchií projektu label_version_sharing_none: Není sdíleno label_version_sharing_system: Se všemi projekty @@ -4369,28 +4502,28 @@ cs: digests: including_mention_singular: včetně zmínky including_mention_plural: včetně %{number_mentioned} zmínění - unread_notification_singular: 1 nepřečtená notifikace - unread_notification_plural: "%{number_unread} nepřečtených notifikací" + unread_notification_singular: 1 nepřečtené oznámení + unread_notification_plural: "%{number_unread} nepřečtených oznámení" you_have: Máte logo_alt_text: Logo mention: subject: "%{user_name} vás zmínil v #%{id} - %{subject}" notification: - center: Centrum notifikací + center: Centrum oznámení see_in_center: Zobrazit komentář v oznamovacím centru settings: Změnit nastavení e-mailu salutation: Dobrý den, %{user}, salutation_full_name: Jméno a příjmení work_packages: created_at: 'Vytvořeno v %{timestamp} uživatelem %{user} ' - login_to_see_all: Přihlaste se pro zobrazení všech notifikací. + login_to_see_all: Přihlaste se pro zobrazení všech oznámení. mentioned: Byli jste zmíněni v komentáři mentioned_by: "%{user} vás zmínil v komentáři OpenProject" more_to_see: - one: Existuje ještě 1 pracovní balíček s notifikací. - few: Existuje ještě %{count} pracovních balíčků s notifikacema. - many: Existuje ještě %{count} pracovních balíčků s notifikacema. - other: Existuje ještě %{count} pracovních balíčků s notifikacema. + one: Máte ještě 1 pracovní balíček s notifikací. + few: Existuje ještě %{count} pracovních balíčků s oznámeními. + many: Máte ještě %{count} pracovních balíčků s notifikacemi. + other: Existuje ještě %{count} pracovních balíčků s oznámeními. open_in_browser: Otevřít v prohlížeči reason: watched: Sledováno @@ -4399,7 +4532,7 @@ cs: mentioned: Zmíněné shared: Sdílené subscribed: vše - prefix: 'Obdrženo z důvodu nastavení notifikací: %{reason}' + prefix: 'Obdrženo z důvodu nastavení oznámení: %{reason}' date_alert_start_date: Upozornění na datum date_alert_due_date: Upozornění na datum reminder: Připomínka @@ -4712,7 +4845,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 @@ -5014,6 +5147,7 @@ cs: ' setting_app_subtitle: Podtitulek aplikace setting_app_title: Název aplikace + setting_organization_name: Organization name setting_attachment_max_size: Maximální velikost příloh setting_show_work_package_attachments: Ve výchozím nastavení zobrazit přílohy na kartě souborů setting_antivirus_scan_mode: Režim skenování @@ -5160,12 +5294,12 @@ cs: setting_welcome_text: Text uvítacího bloku setting_welcome_title: Název uvítacího bloku setting_welcome_on_homescreen: Zobrazit uvítací blok na domovské obrazovce - setting_work_packages_identifier_numeric: Číselná posloupnost pro celou instanci (výchozí) - setting_work_packages_identifier_numeric_caption: 'Každý pracovní balíček dostane pořadové číslo začínající číslem 1 a zvyšující se s každým novým. Čísla jsou v rámci této instance jedinečná, takže zůstávají stejná, i když se pracovní balíčky přesouvají mezi projekty. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Projektové alfanumerické identifikátory - setting_work_packages_identifier_alphanumeric_caption: 'Každý projekt má jedinečný identifikátor, který je předřazen ID pracovního balíčku. Pokud se pracovní balíček přesune do jiného projektu, vygeneruje se nový identifikátor, ale starý zůstává funkční. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Výchozí režim zvýraznění @@ -5240,7 +5374,7 @@ cs: enable_subscriptions_text_html: Umožňuje uživatelům s nezbytnými oprávněními přihlásit se do OpenProject kalendářů a získat přístup k informacím o pracovním balíčku prostřednictvím externího klienta kalendáře. Poznámka: Před povolením si prosím přečtěte podrobnosti o odběru. language_name_being_default: "%{language_name} (výchozí)" notifications: - events_explanation: Určuje, pro kterou událost je odeslán e-mail. Pracovní balíčky jsou z tohoto seznamu vyloučeny, protože notifikace pro ně mohou být nastavena speciálně pro každého uživatele. + events_explanation: Určuje, pro kterou událost je odeslán e-mail. Pracovní balíčky jsou z tohoto seznamu vyloučeny, protože oznámení pro ně mohou být nastavena speciálně pro každého uživatele. delay_minutes_explanation: Odesílání e-mailu může být pozdrženo, aby bylo uživatelům s nakonfigurovaným v oznámení aplikace před odesláním pošty potvrzeno oznámení. Uživatelé, kteří si přečtou oznámení v aplikaci, nedostanou e-mail pro již přečtené oznámení. other: Ostatní passwords: Hesla @@ -5445,7 +5579,7 @@ cs: text_destroy_what_to_do: Co chcete udělat? text_diff_truncated: "... Toto rozlišení bylo zkráceno, protože přesahuje maximální velikost, kterou lze zobrazit." text_email_delivery_not_configured: |- - Doručení e-mailu není nakonfigurováno a notifikace jsou zakázány. + Doručení e-mailu není nakonfigurováno a oznámení jsou zakázána. Nakonfigurujte váš SMTP server pro jejich povolení. text_enumeration_category_reassign_to: 'Přiřadit je k této hodnotě:' text_enumeration_destroy_question: "%{count} objektů je přiřazeno k této hodnotě." diff --git a/config/locales/crowdin/da.yml b/config/locales/crowdin/da.yml index 6b11707ed26..3319f90ad8a 100644 --- a/config/locales/crowdin/da.yml +++ b/config/locales/crowdin/da.yml @@ -114,7 +114,7 @@ da: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ da: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ da: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ da: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ da: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ da: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -681,6 +686,37 @@ da: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1340,6 +1376,20 @@ da: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1535,6 +1585,9 @@ da: dependencies: Aflæggere activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1543,7 +1596,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: @@ -1799,6 +1852,7 @@ da: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1916,6 +1970,7 @@ da: confirmation: matcher ikke %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: findes ikke. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: kan muligvis ikke tilgås. error_readonly: was attempted to be written but is not writable. @@ -1982,6 +2037,7 @@ da: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2464,7 +2520,6 @@ da: avatar: Avatar base: 'Generel fejl:' body: Body - blocks_ids: ID'er for blokerede arbejdspakker category: Kategori comment: Kommentér comments: Kommentér @@ -2921,6 +2976,7 @@ da: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Ubegrænset already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3390,6 +3446,11 @@ da: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3452,6 +3513,72 @@ da: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Tildelt @@ -3483,6 +3610,7 @@ da: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Konto + label_actions: Handlinger label_active: Aktiv label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3523,6 +3651,7 @@ da: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Tildelt status label_archive_project: Archive project @@ -3773,7 +3902,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 @@ -3811,6 +3940,10 @@ da: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Log ud label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Sidemenu @@ -4877,6 +5010,7 @@ da: ' setting_app_subtitle: Appens undertitel setting_app_title: Appens titel + setting_organization_name: Organization name setting_attachment_max_size: Maksimal størrelse for vedhæftning setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5023,12 +5157,12 @@ da: setting_welcome_text: Velkomstblok-tekst setting_welcome_title: Velkomstblok-titel setting_welcome_on_homescreen: Vis velkomstblok på hjemmeskærm - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/de.yml b/config/locales/crowdin/de.yml index 880955ff275..08b5ca7b483 100644 --- a/config/locales/crowdin/de.yml +++ b/config/locales/crowdin/de.yml @@ -87,11 +87,11 @@ de: type_token_text: Ihr Enterprise-Token-Text token_placeholder: Enterprise-Token Text hier einfügen token_caption: Weitere Informationen über die Aktivierung der Enterprise Edition finden Sie in unserer [Dokumentation](docs_url). - add_token: Enterprise-Edition Support Token hochladen + add_token: Enterprise edition Support Token hochladen replace_token: Aktuellen Enterprise edition Support Token ersetzen order: Enterprise on-premises bestellen - paste: Enterprise-Edition Support Token hier einfügen - required_for_feature: Dieses Add-on ist nur mit einem aktiven Enterprise-Edition Support-Token verfügbar. + paste: Enterprise edition Support Token hier einfügen + required_for_feature: Dieses Add-on ist nur mit einem aktiven Enterprise edition Support-Token verfügbar. enterprise_link: Klicken Sie hier für weitere Informationen. start_trial: Kostenlose Testversion starten book_now: Jetzt buchen @@ -114,7 +114,7 @@ de: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Verwenden Sie dieses Tool, um Daten aus Ihrer Jira-Instanz zu importieren. Sie können mehrere Jira-Hosts konfigurieren und auswählen, was bei jedem Importlauf importiert werden soll. errors: cannot_delete_with_imports: Jira-Hosts mit laufenden Importen können nicht gelöscht werden @@ -125,8 +125,8 @@ de: title: Jira-Konfiguration new: Neue Konfiguration banner: - title: Begrenzter Import - description: 'Dieses Importtool befindet sich derzeit in der Betaphase und kann nur grundlegende Daten importieren: Projekte, Tickets (Name, Titel, Beschreibung, Anhänge), Nutzer (Name, E-Mail, Projektmitgliedschaft), Status und Typen. Es kann keine Workflows, benutzerdefinierten Felder, Ticketbeziehungen oder Berechtigungen importieren. Wir unterstützen derzeit nur die Jira Server/Data Center Versionen 10.x und 11.x. Cloud-Instanzen werden derzeit nicht unterstützt.' + title: Eingeschränkte Importfähigkeiten + description: 'Der Jira-Migrator 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.' form: fields: name: Name @@ -154,6 +154,7 @@ de: connection_timeout: 'Die Verbindung zum Jira-Server wurde unterbrochen: %{message}' parse_error: 'Die Jira API-Antwort konnte nicht gelesen werden: %{message}' api_error: Jira API hat den Fehlerstatus %{status} zurückgegeben + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projekte last_change: Letzte Änderung @@ -387,7 +388,7 @@ de: notification_text_default: "

Hallo,

Ein neues Projekt wurde erstellt: projectValue:name

Vielen Dank

\n" work_packages_identifier: page_header: - description: Wählen Sie zwischen einfachen numerischen Arbeitspaket-IDs oder projektspezifischen IDs, bei denen die Projektkennung der Arbeitspaket-ID vorangestellt wird. + description: Wählen Sie zwischen einfachen numerischen Arbeitspaket- oder projektspezifischen Kennungen, bei denen die Projektkennung der Arbeitspaket-ID vorangestellt wird. banner: existing_identifiers_notice: 'Vorhandene Kennungen für %{project_count} Projekte entsprechen nicht den Anforderungen für projektbasierte alphanumerische Bezeichner. OpenProject kann diese automatisch aktualisieren, so dass sie wie in den folgenden Beispielen gültig sind. Klicken Sie auf ''Beheben und speichern'', um die Kennungen für alle Projekte auf diese Weise zu aktualisieren und projektbasierte alphanumerische Bezeichner zu aktivieren. @@ -398,10 +399,14 @@ de: label_autofixed_suggestion: Zukünftige Kennung label_example_work_package_id: Beispielhafte Arbeitspaket-Kennung autofix_preview: - error_too_long: Muss weniger als 5 Zeichen haben + error_too_long: Muss 10 Zeichen oder weniger sein + error_numerical: Kann nicht rein numerisch sein + error_starts_with_number: Kann nicht mit einer Zahl beginnen error_special_characters: Sonderzeichen sind nicht erlaubt + error_not_fully_uppercased: Muss in Großbuchstaben sein error_in_use: Bereits als aktive Kennung für ein anderes Projekt verwendet error_reserved: Reserviert durch eine frühere Kennung eines anderen Projekts + error_unknown: Benötigt manuelle Überprüfung remaining_projects: one: "... 1 weiteres Projekt" other: "... %{count} weitere Projekte" @@ -416,7 +421,7 @@ de: checkbox_label: Mir ist bewusst, dass dies alle Arbeitspaket-Kennungen dauerhaft ändern wird success_banner: Das Format der Arbeitspaket-Kennung wurde erfolgreich aktualisiert. in_progress: - banner_message: Die Projektkennungen werden derzeit auf projektbasierte alphanumerische Identifikatoren aktualisiert. Dies kann einige Zeit in Anspruch nehmen. + banner_message: Die Projektkennungen werden derzeit auf projektbasierte semantische Kennungen aktualisiert. Dies kann einige Zeit in Anspruch nehmen. workflows: tabs: default_transitions: Standard-Übergänge @@ -681,6 +686,37 @@ de: danger_dialog: confirmation_live_message_checked: Die Schaltfläche zum Fortfahren ist nun aktiv. confirmation_live_message_unchecked: Die Schaltfläche zum Fortfahren ist inaktiv. Sie müssen das Kontrollkästchen ankreuzen, um fortzufahren. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Paginierung prev: Zurück @@ -1293,10 +1329,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: @@ -1339,6 +1375,20 @@ de: index: no_results_title_text: Derzeit gibt es keine Workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1350,7 +1400,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. @@ -1453,7 +1503,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 @@ -1532,6 +1582,9 @@ de: dependencies: Abhängigkeiten activerecord: attributes: + work_package_semantic_alias: + identifier: Kennung + work_package: Arbeitspaket jira_import: projects: Projekte import/jira: @@ -1540,7 +1593,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: @@ -1710,7 +1763,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 @@ -1796,6 +1849,7 @@ de: identity_url: Identity URL parent: Übergeordnete Gruppe organizational_unit: Organisationseinheit + group_users: Group users group_detail: parent: Übergeordnete Gruppe organizational_unit: Organisationseinheit @@ -1913,6 +1967,7 @@ de: confirmation: stimmt nicht mit %{attribute} überein. could_not_be_copied: "%{dependency} konnte nicht (vollständig) kopiert werden." does_not_exist: existiert nicht. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} ist nur in der OpenProject Enterprise Edition verfügbar." error_unauthorized: kann nicht zugegriffen werden. error_readonly: wurde versucht zu beschreiben, ist aber nicht beschreibbar. @@ -1979,6 +2034,7 @@ de: attributes: parent_id: circular_dependency: würde eine zirkuläre Gruppenhierarchie erstellen. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2246,7 +2302,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: @@ -2459,7 +2515,6 @@ de: avatar: Profilfoto base: 'Allgemeiner Fehler:' body: Body - blocks_ids: IDs der blockierten Arbeitspakete category: Kategorie comment: Kommentar comments: Kommentar @@ -2916,6 +2971,7 @@ de: title: Enterprise Add-on plan_title: Enterprise %{plan} Add-on plan_name: "%{plan} Enterprise Plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Beginnend mit dem %{plan_name} verfügbar. unlimited: Unbegrenzt already_have_token: 'Haben Sie bereits ein Token? Fügen Sie es über den Button unten hinzu, um zum gebuchten Enterprise-Plan zu wechseln. @@ -3065,7 +3121,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}' @@ -3353,7 +3409,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" @@ -3385,6 +3441,11 @@ de: quick_add: label: Hinzufügen… my_account: + notifications_and_email: + title: Benachrichtigungen und E-Mail + tabs: + notifications: Benachrichtigungseinstellungen + email_reminders: E-Mail-Erinnerungen 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: @@ -3447,6 +3508,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: Eine E-Mail Erinnerung an mich senden + mentioned: Benachrichtige mich, wenn ich erwähnt werde + personal_reminder: Bei persönlichen Erinnerungen benachrichtigen + daily_reminders: + title: Tägliche E-Mail-Erinnerungen für ungelesene Benachrichtigungen zusenden + caption: Sie erhalten diese Erinnerungen nur für ungelesene Benachrichtigungen und nur zu den von Ihnen angegebenen Zeiten. Solange Sie keine Zeitzone für Ihr Konto konfigurieren, werden die Zeiten in UTC interpretiert. + enabled: Tägliche E-Mail-Erinnerungen aktivieren + add_time: Zeit hinzufügen + remove_time: Zeit entfernen + time_slot_label: Erinnerungszeit (UTC) + workdays: + title: E-Mail-Erinnerungen an diesen Tagen erhalten + submit_button: Erinnerungstage aktualisieren + pause_reminders: + title: E-Mail-Benachrichtigungen pausieren + enabled: Tägliche E-Mail-Erinnerungen vorübergehend pausieren + date_range: Pausenzeiten + email_alerts: + title: E-Mail-Benachrichtigungen für andere Objekte (die keine Arbeitspakete sind) + news_added: Neuigkeit hinzugefügt + news_commented: Kommentar zu einer Neuigkeit + document_added: Dokument hinzugefügt + forum_messages: Forumsnachricht erstellt + wiki_page_added: Wiki-Seite hinzugefügt + wiki_page_updated: Wiki-Seite aktualisiert + membership_added: Mitgliedschaft hinzugefügt + membership_updated: Mitgliedschaft aktualisiert + submit_button: Erinnerungen aktualisieren + notifications: + participating: + title: Beteiligt + submit_button: Einstellungen aktualisieren + mentioned: Erwähnt + watched: Beobachtet + assignee: Zugewiesen + responsible: Verantwortlich + shared: Mit mir geteilt + date_alerts: + title: Datums-Erinnerungen + submit_button: Datums-Erinnerungen aktualisieren + start_date: Anfangstermin + due_date: Endtermin + overdue: Überfällig + times: + same_day: Am selben Tag + one_day_before: 1 Tag vorher + three_days_before: 3 Tage vorher + seven_days_before: 7 Tage vor + one_day_after: 1 Tag danach + three_days_after: 3 Tage danach + seven_days_after: 7 Tage danach + non_participating: + title: Nicht beteiligt + submit_button: Einstellungen aktualisieren + work_package_created: Neue Arbeitspakete + work_package_commented: Alle neuen Kommentare + work_package_processed: Alle Statusänderungen + work_package_prioritized: Alle Prioritätsänderungen + work_package_scheduled: Alle Datumsänderungen + project_specific_settings: + title: Projektspezifische Benachrichtigungen + add_button: Projektspezifische Benachrichtigungen hinzufügen + dialog_title: Projektspezifische Benachrichtigungen hinzufügen + list_header: Projekte mit spezifischen Benachrichtigungen notifications: reasons: assigned: Zugewiesen an @@ -3478,6 +3605,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 @@ -3518,6 +3646,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: Integrationen label_add_column: Spalte hinzufügen label_applied_status: Zugewiesener Status label_archive_project: Projekt archivieren @@ -3675,7 +3804,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 @@ -3768,7 +3897,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 @@ -3806,6 +3935,10 @@ de: label_custom_pdf_export_settings: Benutzerdefinierte PDF-Export-Einstellungen label_custom_favicon: Benutzerdefiniertes Favicon label_custom_touch_icon: Benutzerdefiniertes Touch-Icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Abmelden label_mapping_for: 'Zuordnung für: %{attribute}' label_main_menu: Nebenmenü @@ -4785,7 +4918,7 @@ de: Erhöhen Sie diesen Wert zur Verbesserung der Performance, da die Erfassung des genutzten Festplattenspeichers Ressourcen-intensiv ist. oauth_application_details_html: 'Der Client Geheimcode wird nach dem Schließen dieses Fensters nicht mehr zugänglich sein. Bitte kopieren Sie diese Werte in die Nextcloud OpenProject Integrationseinstellungen:' oauth_application_details_link_text: Zu den Einstellungen gehen - setup_documentation_details: 'Wenn Sie Hilfe bei der Konfiguration eines neuen Dateispeichers benötigen, konsultieren Sie bitte die Dokumentation: ' + setup_documentation_details: 'Wenn Sie Hilfe bei der Konfiguration eines neuen Datei-Speichers benötigen, konsultieren Sie bitte die Dokumentation: ' setup_documentation_details_link_text: Dateispeicher einrichten show_warning_details: Um diesen Dateispeicher nutzen zu können, müssen Sie das Modul und den spezifischen Speicher in den Projekteinstellungen jedes gewünschten Projekts aktivieren. subversion: @@ -4880,6 +5013,7 @@ de: ' setting_app_subtitle: Applikations-Untertitel setting_app_title: Applikations-Titel + setting_organization_name: Organization name setting_attachment_max_size: Max. Dateigröße setting_show_work_package_attachments: Anhänge standardmäßig auf dem Tab Dateien anzeigen setting_antivirus_scan_mode: Scanmodus @@ -5026,12 +5160,12 @@ de: setting_welcome_text: Text für Willkommens-Block setting_welcome_title: Titel des Willkommens-Block setting_welcome_on_homescreen: Willkommens-Block auf Startseite anzeigen - setting_work_packages_identifier_numeric: Instanzweite numerische Sequenz (Standard) - setting_work_packages_identifier_numeric_caption: 'Jedes Arbeitspaket erhält eine fortlaufende Nummer, die mit 1 beginnt und mit jedem neuen Objekt erhöht wird. Die Nummern sind innerhalb dieser Instanz eindeutig. Sie bleiben also gleich, auch wenn Arbeitspakete zwischen Projekten verschoben werden. + setting_work_packages_identifier_classic: Instanzweite numerische Sequenz (Standard) + setting_work_packages_identifier_classic_caption: 'Jedes Arbeitspaket erhält eine fortlaufende Nummer, die mit 1 beginnt und mit jedem neuen Objekt erhöht wird. Die Nummern sind innerhalb dieser Instanz eindeutig. Sie bleiben also gleich, auch wenn Arbeitspakete zwischen Projekten verschoben werden. ' - setting_work_packages_identifier_alphanumeric: Projektspezifische alphanumerische Kennungen - setting_work_packages_identifier_alphanumeric_caption: 'Jedes Projekt hat eine eindeutige Kennung, die der Arbeitspaket-ID vorangestellt ist. Wenn ein Arbeitspaket in ein anderes Projekt verschoben wird, wird eine neue Kennung generiert, aber die alte funktioniert weiter. + setting_work_packages_identifier_semantic: Projektspezifische semantische Kennungen + setting_work_packages_identifier_semantic_caption: 'Jedes Projekt hat eine eindeutige Kennung, die der Arbeitspaket-ID vorangestellt ist. Wenn ein Arbeitspaket in ein anderes Projekt verschoben wird, wird eine neue Kennung generiert, aber die alte funktioniert weiter. ' setting_work_package_list_default_highlighting_mode: Standard Hervorhebung @@ -5495,7 +5629,7 @@ de: warning_user_limit_reached_admin_html: 'Das Hinzufügen zusätzlicher Benutzer überschreitet das aktuelle Benutzerlimit. Bitte [aktualisieren Sie Ihr Abonnement](upgrade_url) um sicherzustellen, dass externe Benutzer auf diese Instanz zugreifen können. ' - warning_user_limit_reached_instructions: 'Du hast dein Nutzerlimit erreicht (%{current}/%{max} active users). Bitte kontaktiere sales@openproject.com um deinen Enterprise Edition Plan upzugraden und weitere Nutzer hinzuzufügen. + warning_user_limit_reached_instructions: 'Du hast dein Nutzerlimit erreicht (%{current}/%{max} active users). Bitte kontaktiere sales@openproject.com um deinen Enterprise edition Plan upzugraden und weitere Nutzer hinzuzufügen. ' warning_protocol_mismatch_html: '' diff --git a/config/locales/crowdin/el.yml b/config/locales/crowdin/el.yml index 011c6b862ea..97b4c4d6c38 100644 --- a/config/locales/crowdin/el.yml +++ b/config/locales/crowdin/el.yml @@ -114,7 +114,7 @@ el: import: title: Import jira: - title: Εισαγωγή από Jira + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ el: title: Jira configuration new: New configuration banner: - title: Περιορισμένη εισαγωγή - description: 'Αυτό το εργαλείο εισαγωγής βρίσκεται επί του παρόντος σε beta έκδοση και μπορεί να εισάγει μόνο βασικά δεδομένα: έργα, ζητήματα (όνομα, τίτλος, περιγραφή, συνημμένα), χρήστες (όνομα, email, συμμετοχή σε έργα), καταστάσεις και τύπους. Δεν μπορεί να εισαγάγει ροές εργασίας, προσαρμοσμένα πεδία, σχέσεις ζητημάτων ή δικαιώματα. Προς το παρόν υποστηρίζουμε μόνο τις εκδόσεις 10.x και 11.x του Jira Server/Data Center. Προς το παρόν δεν υποστηρίζονται περιπτώσεις cloud.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ el: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ el: run: title: Import run history: History - remove_error: Μια εισαγωγή Jira δεν μπορεί να αφαιρεθεί ενώ εκτελείται + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ el: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ el: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -681,6 +686,37 @@ el: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1340,6 +1376,20 @@ el: index: no_results_title_text: Δεν υπάρχουν ροές εργασίας αυτήν τη στιγμή. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1535,6 +1585,9 @@ el: dependencies: Εξαρτήσεις activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1543,7 +1596,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: @@ -1799,6 +1852,7 @@ el: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1916,6 +1970,7 @@ el: confirmation: δεν ταιριάζει με %{attribute}. could_not_be_copied: "%{dependency} δεν μπόρεσε να αντιγραφεί (πλήρως)." does_not_exist: δεν υπάρχει. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: δεν είναι δυνατή η πρόσβαση. error_readonly: επιχειρήθηκε να εγγραφεί αλλά δεν ήταν εγγράψιμο. @@ -1982,6 +2037,7 @@ el: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2464,7 +2520,6 @@ el: avatar: Άβαταρ base: 'Γενικό σφάλμα:' body: Body - blocks_ids: Ταυτότητες μπλοκαρισμένων πακέτων εργασίας category: Κατηγορία comment: Σχόλιο comments: Σχόλιο @@ -2921,6 +2976,7 @@ el: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Απεριόριστα already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3390,6 +3446,11 @@ el: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3452,6 +3513,72 @@ el: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Ανάθεση σε @@ -3483,6 +3610,7 @@ el: invalid_filter: Invalid notification filter label_accessibility: Προσβασιμότητα label_account: Λογαριασμός + label_actions: Ενέργειες label_active: Ενεργό label_activate_user: Ενεργοποίηση χρήστη label_active_in_new_projects: Ενεργό σε καινούργια έργα @@ -3523,6 +3651,7 @@ el: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Ανάκληση + label_integrations: Integrations label_add_column: Add column label_applied_status: Εφαρμόστηκε η κατάσταση label_archive_project: Αρχειοθέτηση έργου @@ -3773,7 +3902,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: Τελευταία δραστηριότητα @@ -3811,6 +3940,10 @@ el: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Προσαρμοσμένο favicon label_custom_touch_icon: Προσαρμοσμένο εικονίδιο αφής + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Αποσύνδεση label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Πλευρικό Μενού @@ -4879,6 +5012,7 @@ el: ' setting_app_subtitle: Υπότιτλος εφαρμογής setting_app_title: Τίτλος εφαρμογής + setting_organization_name: Organization name setting_attachment_max_size: Μέγιστο μέγεθος συνημμένων setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5025,12 +5159,12 @@ el: setting_welcome_text: Μπλοκ κειμένου καλωσορίσματος setting_welcome_title: Μπλοκ τίτλου καλωσορίσματος setting_welcome_on_homescreen: Εμφάνιση μπλοκ καλωσορίσματος στην αρχική σελίδα - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Προεπιλεγμένη λειτουργία επισήμανσης diff --git a/config/locales/crowdin/eo.yml b/config/locales/crowdin/eo.yml index aa66f2747ff..17f3ace6a5e 100644 --- a/config/locales/crowdin/eo.yml +++ b/config/locales/crowdin/eo.yml @@ -114,7 +114,7 @@ eo: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ eo: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ eo: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ eo: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ eo: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ eo: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ eo: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ eo: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ eo: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ eo: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ eo: confirmation: ne kongruas kun %{attribute} could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: ne ekzistas. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: ne povas esti atingita. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ eo: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ eo: avatar: Avatar base: 'General Error:' body: Body - blocks_ids: ID de baritaj laborpakaĵoj category: Kategorio comment: Komento comments: Komento @@ -2922,6 +2977,7 @@ eo: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ eo: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ eo: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Asignita al @@ -3484,6 +3611,7 @@ eo: invalid_filter: Invalid notification filter label_accessibility: Alirebleco label_account: Konto + label_actions: Agoj label_active: Aktiva label_activate_user: Aktivigi uzanton label_active_in_new_projects: Aktiva en novaj projektoj @@ -3524,6 +3652,7 @@ eo: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Eksvalidigi + label_integrations: Integrations label_add_column: Add column label_applied_status: Aplikita stato label_archive_project: Aktivi projekton @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ eo: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Adaptita retpaĝsimbolo label_custom_touch_icon: Adaptita tuŝikono + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Elsaluti label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Flanka menuo @@ -4884,6 +5017,7 @@ eo: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ eo: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/es.yml b/config/locales/crowdin/es.yml index 377b6a64e53..12afa7ac77a 100644 --- a/config/locales/crowdin/es.yml +++ b/config/locales/crowdin/es.yml @@ -114,7 +114,7 @@ es: import: title: Importar jira: - title: Importación desde Jira + title: Migrador Jira description: Utilice esta herramienta para importar datos desde su instancia de Jira. Puede configurar varios hosts de Jira y elegir qué importar en cada ejecución de importación. errors: cannot_delete_with_imports: No se puede eliminar el host de Jira con importaciones existentes @@ -125,8 +125,8 @@ es: title: Configuración de Jira new: Nueva configuración banner: - title: Importación limitada - description: 'Esta herramienta de importación se encuentra actualmente en fase beta y solo puede importar datos básicos: proyectos, incidencias (nombre, título, descripción, archivos adjuntos), usuarios (nombre, correo electrónico, pertenencia a proyectos), estados y tipos. No puede importar flujos de trabajo, campos personalizados, relaciones entre incidencias ni permisos. Actualmente solo es compatible con las versiones 10.x y 11.x de Jira Server/Data Center. Por el momento, no es compatible con instancias en la nube.' + title: Capacidades de importación limitadas + description: 'Este Migrador de Jira 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.' form: fields: name: Nombre @@ -154,6 +154,7 @@ es: connection_timeout: 'La conexión con el servidor de Jira ha expirado: %{message}' parse_error: 'Error al analizar la respuesta de la API de Jira: %{message}' api_error: La API de Jira ha devuelto el estado de error %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Proyectos last_change: Última modificación @@ -387,9 +388,9 @@ es: notification_text_default: "

Hola,

Se ha creado un nuevo proyecto: projectValue:name

Muchas gracias

\n" work_packages_identifier: page_header: - description: Elige entre identificadores numéricos básicos para los paquetes de trabajo o identificadores específicos del proyecto, en los que el identificador del proyecto se antepone al identificador del paquete de trabajo. + description: Elige entre los ID numéricos clásicos de los paquetes de trabajo o los ID semánticos específicos del proyecto, que añaden el identificador del proyecto al principio del ID del paquete de trabajo. banner: - existing_identifiers_notice: 'Los identificadores actuales de %{project_count} proyectos no cumplen los requisitos para ser identificadores alfanuméricos basados en proyectos. OpenProject puede actualizarlos automáticamente para que sean válidos, tal y como se muestra en los ejemplos siguientes. Haz clic en «Corregir automáticamente y guardar» para actualizar los identificadores de todos los proyectos de esta manera y habilitar los identificadores alfanuméricos basados en proyectos. + existing_identifiers_notice: 'Los identificadores actuales de %{project_count} proyectos no cumplen los requisitos para ser identificadores semánticos basados en proyectos. OpenProject puede actualizarlos automáticamente para que sean válidos, tal y como se muestra en los ejemplos siguientes. Haz clic en «Corregir automáticamente y guardar» para actualizar los identificadores de todos los proyectos de esta manera y habilitar los identificadores semánticos basados en proyectos. ' box_header: @@ -398,10 +399,14 @@ es: label_autofixed_suggestion: Identificador futuro label_example_work_package_id: Ejemplo de ID del paquete de trabajo autofix_preview: - error_too_long: Debe tener menos de 5 caracteres + error_too_long: Debe tener 10 caracteres o menos + error_numerical: No puede ser puramente numérico + error_starts_with_number: No puede comenzar con un número error_special_characters: No se permiten caracteres especiales + error_not_fully_uppercased: Debe estar en mayúsculas error_in_use: Ya está en uso como identificador activo de otro proyecto error_reserved: Reservado por el historial de identificadores de otro proyecto + error_unknown: Necesita revisión manual remaining_projects: one: "... 1 proyecto más" other: "... %{count} proyectos más" @@ -416,7 +421,7 @@ es: checkbox_label: Entiendo que esto cambiará de forma permanente todos los ID de los paquetes de trabajo success_banner: Se ha actualizado correctamente el formato del identificador del paquete de trabajo. in_progress: - banner_message: Actualmente se están actualizando los identificadores de proyectos para convertirlos en identificadores alfanuméricos basados en proyectos. Esto puede llevar algún tiempo. + banner_message: Actualmente se están actualizando los identificadores de proyectos para convertirlos en identificadores semánticos basados en proyectos. Esto puede llevar algún tiempo. workflows: tabs: default_transitions: Transiciones predeterminadas @@ -679,6 +684,37 @@ es: danger_dialog: confirmation_live_message_checked: El botón para continuar ya está activo. confirmation_live_message_unchecked: El botón para continuar ya está inactivo. Debe marcar la casilla para continuar. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Paginación prev: Anterior @@ -1338,6 +1374,20 @@ es: index: no_results_title_text: Actualmente no existen flujos de trabajo. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1352,7 +1402,7 @@ es: automatic_with_children: Fechas determinadas por paquetes de trabajo secundarios. automatic_with_predecessor: La fecha de inicio la fija un predecesor. manual_mobile: Programado manualmente. - manually_scheduled: Programado manualmente. No afectadas por relaciones. + manually_scheduled: Programado manualmente. Fechas no afectadas por relaciones. blankslate: title: Sin predecesores description: Para activar la programación automática, este paquete de trabajo debe tener al menos un predecesor. Entonces se programará automáticamente para que comience después del predecesor más cercano. @@ -1531,6 +1581,9 @@ es: dependencies: Dependencias activerecord: attributes: + work_package_semantic_alias: + identifier: Identificador + work_package: Paquete de trabajo jira_import: projects: Proyectos import/jira: @@ -1539,7 +1592,7 @@ es: personal_access_token: Token de acceso personal import/jira_open_project_reference: jira: Jira - jira_import: Importación desde Jira + jira_import: Migrador Jira announcements: show_until: Mostrar hasta attachment: @@ -1795,6 +1848,7 @@ es: identity_url: URL de identidad parent: Grupo principal organizational_unit: Unidad organizativa + group_users: Group users group_detail: parent: Grupo principal organizational_unit: Unidad organizativa @@ -1912,6 +1966,7 @@ es: confirmation: no coincide con %{attribute}. could_not_be_copied: "%{dependency} no se pudo copiar (en su totalidad)." does_not_exist: no existe. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} solo está disponible en OpenProject Enterprise." error_unauthorized: no se puede acceder. error_readonly: se intentó escribir pero no se puede escribir. @@ -1978,6 +2033,7 @@ es: attributes: parent_id: circular_dependency: crearía una jerarquía de grupos circular. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2458,7 +2514,6 @@ es: avatar: Avatar base: 'Error general:' body: Cuerpo - blocks_ids: Identificadores de paquetes bloqueados category: Categoría comment: Comentario comments: Comentario @@ -2915,6 +2970,7 @@ es: title: Extensión Enterprise plan_title: Extensión Enterprise de %{plan} plan_name: Plan Enterprise %{plan} + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Disponible a partir del %{plan_name}. unlimited: Ilimitado already_have_token: "¿Ya tiene un token? Añádalo utilizando el botón de abajo para actualizar al plan Enterprise reservado.\n" @@ -3382,6 +3438,11 @@ es: quick_add: label: Añadir… my_account: + notifications_and_email: + title: Notificación y correo electrónico + tabs: + notifications: Ajustes de notificación + email_reminders: Recordatorios por correo electrónico access_tokens: description: Los tokens de proveedor son emitidos por OpenProject y permiten a otras aplicaciones acceder a él. Los tokens de cliente son emitidos por otras aplicaciones y permiten a OpenProject acceder a ellos. no_results: @@ -3444,6 +3505,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: Enviarme un recordatorio por correo electrónico + mentioned: Notificarme cuando me mencionen + personal_reminder: Notificarme para recibir recordatorios personales + daily_reminders: + title: Enviarme recordatorios por correo electrónico diarios con las notificaciones sin leer + caption: Recibirás estos recordatorios solo para las notificaciones no leídas y únicamente a las horas que indiques. Hasta que configures una zona horaria para tu cuenta, se considerará que las horas están en UTC. + enabled: Habilitar recordatorios por correo electrónico diarios + add_time: Añadir tiempo + remove_time: Eliminar el tiempo + time_slot_label: Hora del recordatorio (UTC) + workdays: + title: Recibir recordatorios por correo electrónico en estos días + submit_button: Días de recordatorio de actualización + pause_reminders: + title: Pausar notificaciones por correo electrónico + enabled: Pausar temporalmente los recordatorios por correo electrónico diarios + date_range: Período de pausa + email_alerts: + title: Alertas por correo electrónico de otros elementos que no sean paquetes de trabajo + news_added: Noticias añadidas + news_commented: Comentarios en un elemento de noticias + document_added: Documento agregado + forum_messages: Mensaje publicado en el 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 + submit_button: Actualizar alertas + notifications: + participating: + title: Participando + submit_button: Actualizar preferencias + mentioned: Mencionado + watched: Observando + assignee: Asignado + responsible: Responsable + shared: Compartido conmigo + date_alerts: + title: Alertas de fecha + submit_button: Alertas de fecha de actualización + start_date: Fecha de inicio + due_date: Fecha de finalización + overdue: Vencido + times: + same_day: El mismo día + one_day_before: 1 día antes + three_days_before: 3 días antes + seven_days_before: 7 días antes + one_day_after: 1 día después + three_days_after: 3 días después + seven_days_after: 7 días después + non_participating: + title: No participando + submit_button: Actualizar preferencias + work_package_created: Nuevos paquetes de trabajo + work_package_commented: Todos los nuevos comentarios + work_package_processed: Todos los cambios de estado + work_package_prioritized: Todos los cambios de prioridad + work_package_scheduled: Todos los cambios de fecha + project_specific_settings: + title: Configuración de notificaciones específicas de proyectos + add_button: Añadir notificaciones específicas del proyecto + dialog_title: Añadir notificaciones específicas del proyecto + list_header: Proyectos con notificaciones específicas notifications: reasons: assigned: Asignado a @@ -3475,6 +3602,7 @@ es: invalid_filter: Filtro de notificación no válido label_accessibility: Accesibilidad label_account: Cuenta + label_actions: Comportamiento label_active: Activo label_activate_user: Activar usuario label_active_in_new_projects: Activo en nuevos proyectos @@ -3515,6 +3643,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: Integraciones label_add_column: Añadir columna label_applied_status: Estado aplicado label_archive_project: Archivar proyecto @@ -3765,7 +3894,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: Migrador Jira label_keyword_plural: Palabras clave label_language_based: Basado en el idioma del usuario label_last_activity: Última actividad @@ -3803,6 +3932,10 @@ es: label_custom_pdf_export_settings: Ajustes personalizados de exportación PDF label_custom_favicon: Icono de página personalizado label_custom_touch_icon: Icono táctil personalizado + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Cerrar sesión label_mapping_for: 'Asignación para: %{attribute}' label_main_menu: Menú lateral @@ -4869,6 +5002,7 @@ es: ' setting_app_subtitle: Subtítulo de aplicación setting_app_title: Título de aplicación + setting_organization_name: Organization name setting_attachment_max_size: Tamaño máximo de adjunto setting_show_work_package_attachments: Mostrar archivos adjuntos en la pestaña de archivos por defecto setting_antivirus_scan_mode: Modo de escaneo @@ -5015,12 +5149,12 @@ es: setting_welcome_text: Bloque de texto de bienvenida setting_welcome_title: Título del bloque de bienvenida setting_welcome_on_homescreen: Mostrar bloque de bienvenida en la pagina de inicio - setting_work_packages_identifier_numeric: Secuencia numérica para toda la instancia (predeterminada) - setting_work_packages_identifier_numeric_caption: 'Cada paquete de trabajo recibe un número secuencial que empieza por 1 y se incrementa con cada nuevo paquete. Los números son únicos dentro de esta instancia, por lo que siguen siendo los mismos aunque los paquetes de trabajo se trasladen de un proyecto a otro. + setting_work_packages_identifier_classic: Secuencia numérica para toda la instancia (predeterminada) + setting_work_packages_identifier_classic_caption: 'Cada paquete de trabajo recibe un número secuencial que empieza por 1 y se incrementa con cada nuevo paquete. Los números son únicos dentro de esta instancia, por lo que siguen siendo los mismos aunque los paquetes de trabajo se trasladen de un proyecto a otro. ' - setting_work_packages_identifier_alphanumeric: Identificadores alfanuméricos basados en proyectos - setting_work_packages_identifier_alphanumeric_caption: 'Cada proyecto tiene un identificador único que se añade al principio del ID del paquete de trabajo. Si un paquete de trabajo se traslada a otro proyecto, se genera un nuevo identificador, pero el antiguo sigue funcionando. + setting_work_packages_identifier_semantic: Identificadores semánticos basados en proyectos + setting_work_packages_identifier_semantic_caption: 'Cada proyecto tiene un identificador único que se añade al principio del ID del paquete de trabajo. Si un paquete de trabajo se traslada a otro proyecto, se genera un nuevo identificador, pero el antiguo sigue funcionando. ' setting_work_package_list_default_highlighting_mode: Modo de resaltado predeterminado diff --git a/config/locales/crowdin/et.yml b/config/locales/crowdin/et.yml index 6b4c80645d8..801bf891ecf 100644 --- a/config/locales/crowdin/et.yml +++ b/config/locales/crowdin/et.yml @@ -114,7 +114,7 @@ et: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ et: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ et: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ et: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ et: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ et: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ et: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ et: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ et: dependencies: Sõltuvused activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ et: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ et: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: pole olemas. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ et: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ et: avatar: Profiilipilt base: 'Üldine tõrge:' body: Body - blocks_ids: IDs of blocked work packages category: Kategooria comment: Kommentaar comments: Kommentaar @@ -2922,6 +2977,7 @@ et: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ et: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ et: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Määratud tegija @@ -3484,6 +3611,7 @@ et: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Konto + label_actions: Tegevused label_active: Aktiivne label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ et: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: viimased label_ical_access_key_revoke: Võta tagasi + label_integrations: Integrations label_add_column: Lisa veerg label_applied_status: Rakendatud olek label_archive_project: Arhiveeri projekt @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ et: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Logi välja label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Ääremenüü @@ -4882,6 +5015,7 @@ et: ' setting_app_subtitle: Rakenduse alamnimetus setting_app_title: Rakenduse nimetus + setting_organization_name: Organization name setting_attachment_max_size: Manuste suurim lubatud suurus setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5028,12 +5162,12 @@ et: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/eu.yml b/config/locales/crowdin/eu.yml index c720818a989..1018de889c1 100644 --- a/config/locales/crowdin/eu.yml +++ b/config/locales/crowdin/eu.yml @@ -114,7 +114,7 @@ eu: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ eu: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ eu: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ eu: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ eu: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ eu: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ eu: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ eu: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ eu: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ eu: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ eu: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ eu: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ eu: avatar: Abatarra base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ eu: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ eu: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ eu: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ eu: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ eu: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ eu: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ eu: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ eu: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fa.yml b/config/locales/crowdin/fa.yml index c9e7039680f..7564579e0ad 100644 --- a/config/locales/crowdin/fa.yml +++ b/config/locales/crowdin/fa.yml @@ -114,7 +114,7 @@ fa: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ fa: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ fa: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ fa: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ fa: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ fa: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ fa: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ fa: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ fa: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ fa: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ fa: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ fa: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ fa: avatar: چهرک base: 'General Error:' body: Body - blocks_ids: شناسه‌های مسدود کاربسته‌ها category: Category comment: نظر comments: نظر @@ -2922,6 +2977,7 @@ fa: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ fa: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ fa: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: نماینده @@ -3484,6 +3611,7 @@ fa: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: حساب کاربری + label_actions: اقدامات label_active: Active label_activate_user: کاربر فعال label_active_in_new_projects: فعال در پروژه های جدید @@ -3524,6 +3652,7 @@ fa: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ fa: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'نگاشت مرتبط با: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ fa: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ fa: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fi.yml b/config/locales/crowdin/fi.yml index f7ad960ff5d..2ed3aa6aa87 100644 --- a/config/locales/crowdin/fi.yml +++ b/config/locales/crowdin/fi.yml @@ -114,7 +114,7 @@ fi: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ fi: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ fi: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ fi: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ fi: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ fi: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ fi: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ fi: index: no_results_title_text: Tällä hetkellä ei ole olemassa työnkulkuja. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ fi: dependencies: Riippuvuudet activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ fi: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ fi: confirmation: ei vastaa %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: ei ole olemassa. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ fi: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2463,7 +2519,6 @@ fi: avatar: Avatar base: 'Yleinen virhe:' body: Body - blocks_ids: Estettyjen työpakettien tunnukset category: Kategoria comment: Kommentti comments: Kommentti @@ -2920,6 +2975,7 @@ fi: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Rajoittamaton already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3389,6 +3445,11 @@ fi: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3451,6 +3512,72 @@ fi: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Työn suorittaja @@ -3482,6 +3609,7 @@ fi: invalid_filter: Invalid notification filter label_accessibility: Helppokäyttötoiminnot label_account: Käyttäjätili + label_actions: Toiminnot label_active: Aktiivinen label_activate_user: Aktivoi käyttäjä label_active_in_new_projects: Active in new projects @@ -3522,6 +3650,7 @@ fi: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Käytetty tila label_archive_project: Arkisto projekti @@ -3772,7 +3901,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 @@ -3810,6 +3939,10 @@ fi: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Mukautettu kuvake label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Kirjaudu ulos label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Sivuvalikko @@ -4882,6 +5015,7 @@ fi: ' setting_app_subtitle: Ohjelman alaotsikko setting_app_title: Ohjelman otsikko + setting_organization_name: Organization name setting_attachment_max_size: Liitteen maksimikoko setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5028,12 +5162,12 @@ fi: setting_welcome_text: Tervehdysteksti setting_welcome_title: Tervehdyspalkki setting_welcome_on_homescreen: Näytä tervehdyspalkki kotinäkymässä - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fil.yml b/config/locales/crowdin/fil.yml index 5c3dede20f2..a62b3d4fd2a 100644 --- a/config/locales/crowdin/fil.yml +++ b/config/locales/crowdin/fil.yml @@ -114,7 +114,7 @@ fil: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ fil: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ fil: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ fil: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ fil: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ fil: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ fil: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ fil: index: no_results_title_text: Sa kasalukuyan ay walang mga workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ fil: dependencies: Dependencia activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ fil: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ fil: confirmation: hindi tugma %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: hindi umiiral. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ fil: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ fil: avatar: Avatar base: 'Pangkalahatang Mali:' body: Body - blocks_ids: Mga ID ng naka-block na mga pakete sa gumagawa category: Kategorya comment: Komento comments: Komento @@ -2922,6 +2977,7 @@ fil: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ fil: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ fil: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Naitalaga @@ -3484,6 +3611,7 @@ fil: invalid_filter: Invalid notification filter label_accessibility: Aksibilidad label_account: Akawnt + label_actions: Mga Aksyon label_active: Aktibo label_activate_user: Aktibong gumagamit label_active_in_new_projects: Aktibo sa bagong proyekto @@ -3524,6 +3652,7 @@ fil: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Estadong nilapat label_archive_project: I-archive ang proyekto @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ fil: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Ang icon ng kustom pindutan + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Mag-sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Slide menu @@ -4880,6 +5013,7 @@ fil: ' setting_app_subtitle: Aplikasyong subtitle setting_app_title: Aplikasyong pamagat + setting_organization_name: Organization name setting_attachment_max_size: Paglalakip ng mataas na laki setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5026,12 +5160,12 @@ fil: setting_welcome_text: Teskstong welcome block setting_welcome_title: Titulo ng welcome back setting_welcome_on_homescreen: I-display ang welcome block sa homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/fr.yml b/config/locales/crowdin/fr.yml index 33f7d6da7d2..f354a9de73c 100644 --- a/config/locales/crowdin/fr.yml +++ b/config/locales/crowdin/fr.yml @@ -114,7 +114,7 @@ fr: import: title: Importation jira: - title: Importation de Jira + title: Migrateur Jira description: Utilisez cet outil pour importer des données à partir de votre instance Jira. Vous pouvez configurer plusieurs hôtes Jira et choisir les données à importer à chaque cycle d'importation. errors: cannot_delete_with_imports: Impossible de supprimer un hôte Jira avec des importations existantes @@ -125,8 +125,8 @@ fr: title: Configuration de Jira new: Nouvelle configuration banner: - title: Importation limitée - description: 'Cet outil d''importation est actuellement en version bêta et ne peut importer que des données de base : projets, tickets (nom, titre, description, pièces jointes), utilisateurs (nom, e-mail, appartenance à un projet), statuts et types. Il ne peut pas importer les flux de travail, les champs personnalisés, les relations entre les tickets ou les autorisations. Nous ne prenons actuellement en charge que les versions 10.x et 11.x de Jira Server/Data Center. Les instances cloud ne sont pas prises en charge pour le moment.' + title: Capacités d'importation limitées + description: 'Cet outil Jora Migrator est actuellement en version bêta et ne peut importer que des données de base : projets, tickets (nom, titre, description, pièces jointes), utilisateurs (nom, e-mail, appartenance à un projet), statuts et types. Il ne peut pas importer les flux de travail, les champs personnalisés, les relations entre les tickets ou les autorisations. Nous ne prenons actuellement en charge que les versions 10.x et 11.x de Jira Server/Data Center. Les instances cloud ne sont pas prises en charge pour le moment.' form: fields: name: Nom @@ -154,6 +154,7 @@ fr: connection_timeout: 'La connexion au serveur Jira a expiré : %{message}' parse_error: 'Échec de l''analyse de la réponse de l''API Jira : %{message}' api_error: L'API Jira a renvoyé le statut d'erreur %{status} + 401_error: L'API Jira a renvoyé une erreur 401. Votre jeton d'authentification peut avoir expiré ou ne pas avoir les autorisations requises. Veuillez vous assurer que le jeton appartient à un administrateur Jira. columns: projects: Projets last_change: Dernière modification @@ -162,7 +163,7 @@ fr: run: title: Cycle d'importation history: Historique - remove_error: Une importation Jira ne peut pas être supprimée lorsqu'elle est en cours d'exécution + remove_error: Une exécution d'importation Jira ne peut pas être supprimée lorsqu'elle est en cours d'exécution import_blocked_error: Un autre cycle d'importation Jira est actuellement en cours ou en attente de révision. Veuillez le terminer ou l'annuler avant de commencer une nouvelle importation. project_identifier_taken: 'Vous essayez d''importer un projet avec un identifiant déjà utilisé : %{taken_identifier}. Veuillez mettre à jour l''identifiant du projet dans Jira, puis cliquez sur Réessayer.' blank: @@ -387,9 +388,9 @@ fr: notification_text_default: "

Bonjour,

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

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

\n" work_packages_identifier: page_header: - description: Vous avez le choix entre des identifiants numériques de base pour les lots de travail et des identifiants spécifiques aux projets qui ajoutent l'identifiant du projet à l'identifiant du lot de travail. + description: Vous avez le choix entre des identifiants numériques classiques pour les lots de travaux et des identifiants sémantiques spécifiques aux projets, qui ajoutent l'identifiant du projet à l'identifiant du lot de travaux. banner: - existing_identifiers_notice: 'Les identifiants existants de %{project_count} projets ne répondent pas aux exigences des identifiants alphanumériques basés sur les projets. OpenProject peut les mettre à jour automatiquement pour qu''ils soient valides, comme dans les exemples ci-dessous. Cliquez sur "Autofixer et sauvegarder" pour mettre à jour les identifiants de tous les projets de cette manière et activer les identifiants alphanumériques basés sur le projet. + existing_identifiers_notice: 'Les identifiants existants de %{project_count} projets ne répondent pas aux exigences des identifiants sémantiques basés sur les projets. OpenProject peut les mettre à jour automatiquement pour qu''ils soient valides, comme dans les exemples ci-dessous. Cliquez sur « Corriger automatiquement et enregistrer » pour mettre à jour les identifiants de tous les projets de cette manière et activer les identifiants sémantiques basés sur les projets. ' box_header: @@ -398,10 +399,14 @@ fr: label_autofixed_suggestion: Futur identifiant label_example_work_package_id: Exemple d'identifiant de lot de travaux autofix_preview: - error_too_long: Doit avoir moins de 5 caractères + error_too_long: Doit être composé de 10 caractères ou moins + error_numerical: Ne peut être purement numérique + error_starts_with_number: Ne peut pas commencer par un chiffre error_special_characters: Caractères spéciaux non autorisés + error_not_fully_uppercased: Doit être en majuscules error_in_use: Déjà utilisé comme alias actif d'un autre projet error_reserved: Réservé par l'historique des alias d'un autre projet + error_unknown: Nécessite une vérification manuelle remaining_projects: one: "... 1 projet supplémentaire" other: "... %{count} projets supplémentaires" @@ -416,7 +421,7 @@ fr: checkbox_label: Je comprends que cela changera définitivement tous les identifiants du lot de travaux success_banner: Le format de l'identifiant du lot de travaux a été mis à jour. in_progress: - banner_message: Les identifiants de projet sont actuellement mis à jour pour devenir des identifiants alphanumériques basés sur les projets. Cela peut prendre un certain temps. + banner_message: Les identifiants de projet sont actuellement mis à jour pour devenir des identifiants sémantiques basés sur les projets. Cela peut prendre un certain temps. workflows: tabs: default_transitions: Transitions par défaut @@ -682,6 +687,37 @@ fr: danger_dialog: confirmation_live_message_checked: Le bouton pour continuer est maintenant actif. confirmation_live_message_unchecked: Le bouton pour continuer est maintenant inactif. Vous devez cocher la case pour continuer. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Précédent @@ -1334,6 +1370,20 @@ fr: index: no_results_title_text: Il n'y a actuellement aucun flux de travail. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1529,6 +1579,9 @@ fr: dependencies: Dépendances activerecord: attributes: + work_package_semantic_alias: + identifier: Identifiant + work_package: Lot de travaux jira_import: projects: Projets import/jira: @@ -1537,7 +1590,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: @@ -1793,6 +1846,7 @@ fr: identity_url: URL d'identité parent: Groupe parent organizational_unit: Unité organisationnelle + group_users: Group users group_detail: parent: Groupe parent organizational_unit: Unité organisationnelle @@ -1910,6 +1964,7 @@ fr: confirmation: ne correspond pas à %{attribute}. could_not_be_copied: "%{dependency} n'a pas pu être copié (entièrement)." does_not_exist: n'existe pas. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} n'est disponible que dans l'édition Enterprise d'OpenProject." error_unauthorized: est interdit d'accès. error_readonly: a tenté d'être écrit mais n'est pas accessible en écriture. @@ -1976,6 +2031,7 @@ fr: attributes: parent_id: circular_dependency: créerait une hiérarchie de groupe circulaire. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2458,7 +2514,6 @@ fr: avatar: Avatar base: 'Erreur générale :' body: Corps - blocks_ids: IDs des lots de travaux bloqués category: Catégorie comment: Commentaire comments: Commentaire @@ -2915,6 +2970,7 @@ fr: title: Module Enterprise plan_title: Add-on Enterprise %{plan} plan_name: Abonnement Enterprise %{plan} + trial_text: Cette fonctionnalité est incluse dans votre essai en cours d'entreprise. plan_text_html: Disponible à partir de l'abonnement %{plan_name}. unlimited: Illimité already_have_token: 'Vous avez déjà un jeton ? Ajoutez-le en utilisant le bouton ci-dessous pour passer à l''abonnement Enterprise réservé. @@ -3384,6 +3440,11 @@ fr: quick_add: label: Ajouter… my_account: + notifications_and_email: + title: Notification et e-mail + tabs: + notifications: Paramètres de notifications + email_reminders: Rappels par e-mail access_tokens: description: Les jetons de fournisseur sont émis par OpenProject, ce qui permet à d'autres applications d'y accéder. Les jetons clients sont émis par d'autres applications, permettant à OpenProject d'y accéder. no_results: @@ -3446,6 +3507,72 @@ fr: disabled_text: Les jetons RSS ne sont pas activés par l'administrateur. Veuillez contacter votre administrateur pour utiliser cette fonctionnalité. storages: unknown_storage: Espace de stockage inconnu + email_reminders: + immediate_reminders: + title: M'envoyer un rappel par e-mail + mentioned: M'avertir lorsque je suis mentionné(e) + personal_reminder: M'avertir pour les rappels personnels + daily_reminders: + title: M'envoyer des rappels quotidiens par e-mail pour les notifications non lues + caption: Vous ne recevrez ces rappels que pour les notifications non lues et seulement aux heures que vous aurez spécifiées. Tant que vous n'aurez pas configuré un fuseau horaire pour votre compte, les heures seront interprétées comme étant en UTC. + enabled: Activer les rappels quotidiens par e-mail + add_time: Ajouter une heure + remove_time: Supprimer l'heure + time_slot_label: Heure de rappel (UTC) + workdays: + title: Recevoir des rappels par e-mail aux jours spécifiés + submit_button: Mettre à jour les jours de rappel + pause_reminders: + title: Suspendre les notifications par e-mail + enabled: Suspendre temporairement les rappels quotidiens par e-mail + date_range: Période de pause + email_alerts: + title: Alertes par e-mail pour les autres éléments qui ne sont pas des lots de travaux + news_added: Actualités ajoutées + news_commented: Commenter un article d'actualité + document_added: Document ajouté + forum_messages: Message de forum publié + wiki_page_added: Page wiki ajoutée + wiki_page_updated: Page wiki mise à jour + membership_added: Adhésion ajoutée + membership_updated: Adhésion mise à jour + submit_button: Mettre à jour les alertes + notifications: + participating: + title: Participant + submit_button: Mettre à jour les préférences + mentioned: Mentionné + watched: En cours + assignee: Personne assignée + responsible: Responsable + shared: Partagé avec moi + date_alerts: + title: Alertes de date + submit_button: Mettre à jour les alertes de date + start_date: Date de début + due_date: Date de fin + overdue: En retard + times: + same_day: Le même jour + one_day_before: 1 jour avant + three_days_before: 3 jours avant + seven_days_before: 7 jours avant + one_day_after: 1 jour après + three_days_after: 3 jours après + seven_days_after: 7 jours après + non_participating: + title: Non participant + submit_button: Mettre à jour les préférences + work_package_created: Nouveaux lots de travaux + work_package_commented: Tous les nouveaux commentaires + work_package_processed: Tous les changements de statut + work_package_prioritized: Tous les changements de priorité + work_package_scheduled: Tous les changements de date + project_specific_settings: + title: Paramètres de notification spécifiques au projet + add_button: Ajouter des notifications spécifiques au projet + dialog_title: Ajouter des notifications spécifiques au projet + list_header: Projets avec notifications spécifiques notifications: reasons: assigned: Personne assignée @@ -3477,6 +3604,7 @@ fr: invalid_filter: Filtre de notification invalide label_accessibility: Accessibilité label_account: Compte + label_actions: Actions label_active: Actif label_activate_user: Activer l’utilisateur label_active_in_new_projects: Activer dans de nouveaux projets @@ -3517,6 +3645,7 @@ fr: label_ical_access_key_generation_hint: Généré automatiquement lors de l'abonnement à un calendrier. label_ical_access_key_latest: Dernier label_ical_access_key_revoke: Révoquer + label_integrations: Intégrations label_add_column: Ajouter une colonne label_applied_status: Statut appliqué label_archive_project: Archiver le projet @@ -3767,7 +3896,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é @@ -3805,6 +3934,10 @@ fr: label_custom_pdf_export_settings: Paramètres d'exportation PDF personnalisés label_custom_favicon: Favicon personnalisé label_custom_touch_icon: Icône de contact personnalisé + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Déconnexion label_mapping_for: 'Mapping pour : %{attribute}' label_main_menu: Menu principal @@ -4875,6 +5008,7 @@ fr: ' setting_app_subtitle: Sous-titre de l'Application setting_app_title: Titre de l'Application + setting_organization_name: Organization name setting_attachment_max_size: Taille maximale de la pièce jointe setting_show_work_package_attachments: Afficher les pièces jointes dans l'onglet des fichiers par défaut setting_antivirus_scan_mode: Mode analyse @@ -5021,12 +5155,12 @@ fr: setting_welcome_text: Bloc de texte de bienvenue setting_welcome_title: Bloc de titre de bienvenue setting_welcome_on_homescreen: Afficher le bloc de bienvenue sur l'écran d'accueil - setting_work_packages_identifier_numeric: Séquence numérique à l'échelle de l'instance (par défaut) - setting_work_packages_identifier_numeric_caption: 'Chaque lot de travaux obtient un numéro séquentiel commençant par 1 et incrémenté à chaque nouveau. Les nombres sont uniques dans cette instance, donc ils restent les mêmes même si les lots de travaux sont déplacés entre les projets. + setting_work_packages_identifier_classic: Séquence numérique à l'échelle de l'instance (par défaut) + setting_work_packages_identifier_classic_caption: 'Chaque lot de travaux obtient un numéro séquentiel commençant par 1 et incrémenté à chaque nouveau. Les nombres sont uniques dans cette instance, donc ils restent les mêmes même si les lots de travaux sont déplacés entre les projets. ' - setting_work_packages_identifier_alphanumeric: Identifiants alphanumériques basés sur le projet - setting_work_packages_identifier_alphanumeric_caption: 'Chaque projet a un identifiant unique qui est préfixé à l''ID du lot de travaux. Si un lot de travaux a été déplacé vers un autre projet, un nouvel identifiant est généré, mais l''ancien continue de fonctionner. + setting_work_packages_identifier_semantic: Identifiants sémantiques basés sur le projet + setting_work_packages_identifier_semantic_caption: 'Chaque projet a un identifiant unique qui est préfixé à l''ID du lot de travaux. Si un lot de travaux a été déplacé vers un autre projet, un nouvel identifiant est généré, mais l''ancien continue de fonctionner. ' setting_work_package_list_default_highlighting_mode: Mode de surbrillance par défaut diff --git a/config/locales/crowdin/he.yml b/config/locales/crowdin/he.yml index 78cc43c89f3..ec32b9c80cd 100644 --- a/config/locales/crowdin/he.yml +++ b/config/locales/crowdin/he.yml @@ -114,7 +114,7 @@ he: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ he: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ he: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ he: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +400,9 @@ he: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +411,14 @@ he: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" two: "... %{count} more projects" @@ -430,7 +435,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 @@ -698,6 +703,37 @@ he: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1375,6 +1411,20 @@ he: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1572,6 +1622,9 @@ he: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1580,7 +1633,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: @@ -1836,6 +1889,7 @@ he: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1953,6 +2007,7 @@ he: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2019,6 +2074,7 @@ he: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2541,7 +2597,6 @@ he: avatar: Avatar base: 'שגיאה כללית:' body: Body - blocks_ids: המזהים של חבילות עבודה חסומים category: קטגוריה comment: תגובה comments: תגובה @@ -3038,6 +3093,7 @@ he: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3509,6 +3565,11 @@ he: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3571,6 +3632,72 @@ he: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: משויך אל @@ -3602,6 +3729,7 @@ he: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: חשבון + label_actions: Actions label_active: פעיל label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3642,6 +3770,7 @@ he: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: מצב יישומית label_archive_project: Archive project @@ -3892,7 +4021,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 @@ -3930,6 +4059,10 @@ he: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: התנתק label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: תפריט צד @@ -5012,6 +5145,7 @@ he: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5158,12 +5292,12 @@ he: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/hi.yml b/config/locales/crowdin/hi.yml index b2a678ee29f..98538de2d33 100644 --- a/config/locales/crowdin/hi.yml +++ b/config/locales/crowdin/hi.yml @@ -114,7 +114,7 @@ hi: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ hi: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ hi: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ hi: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ hi: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ hi: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ hi: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ hi: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ hi: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ hi: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ hi: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ hi: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ hi: avatar: अवतार base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: श्रेणी comment: टिप्पणी comments: टिप्पणी @@ -2922,6 +2977,7 @@ hi: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ hi: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ hi: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: अनुदिष्ट @@ -3484,6 +3611,7 @@ hi: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: खाता + label_actions: क्रियाएँ label_active: सक्रिय label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ hi: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: लागू की गई स्थिति label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ hi: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: विशेष या कस्टम favicon label_custom_touch_icon: कस्टम प्रतीक चिह्न + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ hi: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ hi: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: डिफ़ॉल्ट हाइलाइटिंग तरीका diff --git a/config/locales/crowdin/hr.yml b/config/locales/crowdin/hr.yml index 1524d801db2..52700e5be2a 100644 --- a/config/locales/crowdin/hr.yml +++ b/config/locales/crowdin/hr.yml @@ -114,7 +114,7 @@ hr: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ hr: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ hr: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ hr: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +394,9 @@ hr: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +405,14 @@ hr: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -423,7 +428,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 @@ -690,6 +695,37 @@ hr: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1358,6 +1394,20 @@ hr: index: no_results_title_text: Trenutno ne postoji niti jedan workflow. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1554,6 +1604,9 @@ hr: dependencies: Ovisnosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1615,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: @@ -1818,6 +1871,7 @@ hr: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1935,6 +1989,7 @@ hr: confirmation: ne odgovara %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: ne postoji. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2001,6 +2056,7 @@ hr: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2501,7 +2557,6 @@ hr: avatar: Avatar base: 'Opća greška:' body: Body - blocks_ids: ID-ovi onemogućenih radnih paketa category: Kategorija comment: Komentar comments: Komentar @@ -2978,6 +3033,7 @@ hr: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3448,6 +3504,11 @@ hr: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3510,6 +3571,72 @@ hr: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Opunomoćeno @@ -3541,6 +3668,7 @@ hr: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Korisnički račun + label_actions: Actions label_active: Aktivno label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3581,6 +3709,7 @@ hr: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Dodijeljeni status label_archive_project: Archive project @@ -3831,7 +3960,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 @@ -3869,6 +3998,10 @@ hr: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Odjavi se label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Izbornik sa strane @@ -4944,6 +5077,7 @@ hr: ' setting_app_subtitle: Podnaslov aplikacije setting_app_title: Naslov aplikacije + setting_organization_name: Organization name setting_attachment_max_size: Max. veličina privitka setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5090,12 +5224,12 @@ hr: setting_welcome_text: Tekst dobrodošlice setting_welcome_title: Naslov dobrodošlice setting_welcome_on_homescreen: Prikaži tekst dobrodošlice na početnoj stranici - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/hu.yml b/config/locales/crowdin/hu.yml index 651df1ab955..18e2f3ea738 100644 --- a/config/locales/crowdin/hu.yml +++ b/config/locales/crowdin/hu.yml @@ -114,7 +114,7 @@ hu: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ hu: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ hu: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ hu: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ hu: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ hu: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -684,6 +689,37 @@ hu: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1361,6 +1397,20 @@ hu: index: no_results_title_text: Jelenleg nincsenek munkafolyamatok. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1556,6 +1606,9 @@ hu: dependencies: Szükséges összetevők activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1564,7 +1617,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: @@ -1822,6 +1875,7 @@ hu: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1943,6 +1997,7 @@ hu: confirmation: "%{attribute} nem egyezik." could_not_be_copied: A (z) %{dependency} nem másolható (teljesen). does_not_exist: nem létezik. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: lehet, hogy nem érhető el. error_readonly: írása megkísérelve, de nem írható. @@ -2013,6 +2068,7 @@ hu: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2517,7 +2573,6 @@ hu: avatar: Profilkép base: 'Általános hiba:' body: Body - blocks_ids: Lezárt munkacsomagok azonosítója category: Kategória comment: Komment comments: Vélemény @@ -2976,6 +3031,7 @@ hu: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Korlátlan already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3453,6 +3509,11 @@ hu: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3515,6 +3576,72 @@ hu: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Megbízott @@ -3548,6 +3675,7 @@ hu: invalid_filter: Invalid notification filter label_accessibility: Kisegítő lehetőségek label_account: Felhasználói fiók + label_actions: Műveletek label_active: aktív label_activate_user: Felhasználó aktiválása label_active_in_new_projects: Új projektekben aktív @@ -3588,6 +3716,7 @@ hu: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Visszavonás + label_integrations: Integrations label_add_column: Add column label_applied_status: Alkalmazott státusz label_archive_project: Projekt archiválása @@ -3838,7 +3967,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 @@ -3876,6 +4005,10 @@ hu: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Egyedi favicon label_custom_touch_icon: Egyedi "touch-icon" + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Kijelentkezés label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Oldalsó menü @@ -4970,6 +5103,7 @@ hu: ' setting_app_subtitle: Alkalmazás felirat setting_app_title: Alkalmazás címe + setting_organization_name: Organization name setting_attachment_max_size: Melléklet max. méret setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5120,12 +5254,12 @@ hu: setting_welcome_text: Üdvözlő blokk szöveg setting_welcome_title: Üdvözlő blokk címe setting_welcome_on_homescreen: Üdvözlő blokk megjelenítése a kezdőképernyőn - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Alapértelmezett kiemelési mód diff --git a/config/locales/crowdin/id.yml b/config/locales/crowdin/id.yml index a8339b34b6f..af46bb0b12a 100644 --- a/config/locales/crowdin/id.yml +++ b/config/locales/crowdin/id.yml @@ -114,7 +114,7 @@ id: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ id: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ id: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ id: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +382,9 @@ id: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +393,14 @@ id: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +414,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 @@ -673,6 +678,37 @@ id: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1323,6 +1359,20 @@ id: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1522,6 +1572,9 @@ id: dependencies: Dependensi activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1530,7 +1583,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: @@ -1786,6 +1839,7 @@ id: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1903,6 +1957,7 @@ id: confirmation: tidak sesuai dengan %{attribute}. could_not_be_copied: "%{dependency} tidak dapat (sepenuhnya) disalin." does_not_exist: tidak ditemukan. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: mungkin tidak dapat diakses. error_readonly: Mencoba untuk ditulis tetapi tidak dapat ditulis. @@ -1969,6 +2024,7 @@ id: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2436,7 +2492,6 @@ id: avatar: Avatar base: 'Error :' body: Body - blocks_ids: List ID dari work package yang diblokir category: Kategori comment: Komentar comments: Komentar @@ -2875,6 +2930,7 @@ id: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3343,6 +3399,11 @@ id: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3405,6 +3466,72 @@ id: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Pelimpahan @@ -3436,6 +3563,7 @@ id: invalid_filter: Invalid notification filter label_accessibility: Kemampuan akses label_account: Akun + label_actions: Tindakan label_active: Aktif label_activate_user: Pengguna diaktifkan label_active_in_new_projects: Aktif dalam proyek-proyek baru @@ -3476,6 +3604,7 @@ id: label_ical_access_key_generation_hint: Dibuat secara otomatis saat berlangganan kalender. label_ical_access_key_latest: terbaru label_ical_access_key_revoke: Menarik kembali + label_integrations: Integrations label_add_column: Add column label_applied_status: Status berjalan label_archive_project: Proyek arsip @@ -3726,7 +3855,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 @@ -3764,6 +3893,10 @@ id: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Favicon sesuai keinginan label_custom_touch_icon: Ikon sentuh sesuai keinginan + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Logout label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Sidemenu @@ -4821,6 +4954,7 @@ id: ' setting_app_subtitle: Subjudul aplikasi setting_app_title: Judul aplikasi + setting_organization_name: Organization name setting_attachment_max_size: Ukuran lampiran maks. setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -4967,12 +5101,12 @@ id: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/it.yml b/config/locales/crowdin/it.yml index 30d79da22dc..20a6b262c6e 100644 --- a/config/locales/crowdin/it.yml +++ b/config/locales/crowdin/it.yml @@ -114,7 +114,7 @@ it: import: title: Importa jira: - title: Importazione di Jira + title: Jira Migrator description: Utilizza questo strumento per importare dati dalla tua istanza Jira. Puoi configurare più host Jira e scegliere cosa importare a ogni importazione. errors: cannot_delete_with_imports: Impossibile eliminare l'host Jira con importazioni esistenti @@ -125,8 +125,8 @@ it: title: Configurazione Jira new: Nuova configurazione banner: - title: Importazione limitata - description: 'Questo strumento di importazione è attualmente in versione beta e può importare solo dati di base: progetti, issue (nome, titolo, descrizione, allegati), utenti (nome, email, appartenenza al progetto), stati e tipi. Non può importare flussi di lavoro, campi personalizzati, relazioni tra issue o autorizzazioni. Al momento supportiamo solo le versioni 10.x e 11.x di Jira Server/Data Center. Le istanze cloud non sono supportate al momento.' + title: Funzionalità di importazione limitate + description: 'Jira Migrator è 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.' form: fields: name: Nome @@ -154,6 +154,7 @@ it: connection_timeout: 'La connessione al server Jira è scaduta: %{message}' parse_error: 'Impossibile analizzare la risposta dell''API Jira: %{message}' api_error: Jira API ha restituito lo stato di errore %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Progetti last_change: Ultima modifica @@ -387,9 +388,9 @@ it: notification_text_default: "

Ciao,

È stato creato un nuovo progetto: projectValue:name

Grazie

\n" work_packages_identifier: page_header: - description: Scegli tra ID numerici di base delle macro-attività oppure ID specifici del progetto che antepongono l'identificatore del progetto all'ID della macro-attività. + description: Scegli tra ID numerici classici delle macro-attività oppure ID semantici specifici del progetto che antepongono l'identificatore del progetto all'ID della macro-attività. banner: - existing_identifiers_notice: 'Gli identificatori esistenti per %{project_count} progetti non soddisfano i requisiti per gli identificatori alfanumerici basati sul progetto. OpenProject può aggiornarli automaticamente per renderli validi, come negli esempi sotto. Fai clic su "Correggi automaticamente e salva" per aggiornare gli identificatori di tutti i progetti in questo modo e abilitare gli identificatori alfanumerici basati sul progetto. + existing_identifiers_notice: 'Gli identificatori esistenti per %{project_count} progetti non soddisfano i requisiti per gli identificatori semantici basati sul progetto. OpenProject può aggiornarli automaticamente per renderli validi, come negli esempi sotto. Fai clic su "Correggi automaticamente e salva" per aggiornare gli identificatori di tutti i progetti in questo modo e abilitare gli identificatori semantici basati sul progetto. ' box_header: @@ -398,10 +399,14 @@ it: label_autofixed_suggestion: Identificativo futuro label_example_work_package_id: Esempio di ID macro-attività autofix_preview: - error_too_long: Deve contenere meno di 5 caratteri + error_too_long: Deve essere di 10 caratteri o meno + error_numerical: Non può essere puramente numerico + error_starts_with_number: Non può iniziare con un numero error_special_characters: I caratteri speciali non sono consentiti + error_not_fully_uppercased: Deve essere maiuscolo error_in_use: Già in uso come identificatore attivo di un altro progetto error_reserved: Riservato dalla cronologia degli identificatori di un altro progetto + error_unknown: Necessita di controllo manuale remaining_projects: one: "... 1 altro progetto" other: "... altri %{count} progetti" @@ -416,7 +421,7 @@ it: checkbox_label: Comprendo che questa operazione modificherà in modo permanente tutti gli ID delle macro-attività success_banner: Formato degli identificatori delle macro-attività aggiornato correttamente. in_progress: - banner_message: Gli identificatori dei progetti sono attualmente in fase di aggiornamento agli identificatori alfanumerici basati sul progetto. Questa operazione potrebbe richiedere un po' di tempo. + banner_message: Gli identificatori dei progetti sono attualmente in fase di aggiornamento agli identificatori semantici basati sul progetto. Questa operazione potrebbe richiedere un po' di tempo. workflows: tabs: default_transitions: Transizioni predefinite @@ -681,6 +686,37 @@ it: danger_dialog: confirmation_live_message_checked: Il pulsante per procedere è ora attivo. confirmation_live_message_unchecked: Il pulsante per procedere è ora inattivo. Devi spuntare la casella per continuare. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Paginazione prev: Precedente @@ -1339,6 +1375,20 @@ it: index: no_results_title_text: Al momento non vi sono flussi di lavoro. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1534,6 +1584,9 @@ it: dependencies: Dipendenze activerecord: attributes: + work_package_semantic_alias: + identifier: Identificatore + work_package: Macro-attività jira_import: projects: Progetti import/jira: @@ -1542,7 +1595,7 @@ it: personal_access_token: Token di accesso personale import/jira_open_project_reference: jira: Jira - jira_import: Importazione di Jira + jira_import: Jira Migrator announcements: show_until: Visualizza fino a attachment: @@ -1798,6 +1851,7 @@ it: identity_url: URL identificativo parent: Gruppo padre organizational_unit: Unità organizzativa + group_users: Group users group_detail: parent: Gruppo padre organizational_unit: Unità organizzativa @@ -1915,6 +1969,7 @@ it: confirmation: non coincide con %{attribute}. could_not_be_copied: "%{dependency} non può essere (completamente) copiato." does_not_exist: non esiste. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} è disponibile solo in OpenProject Enterprise edition." error_unauthorized: potrebbe non essere accessibile. error_readonly: è in sola lettura, pertanto non è stato possibile modificarlo. @@ -1981,6 +2036,7 @@ it: attributes: parent_id: circular_dependency: creerebbe una gerarchia circolare dei gruppi. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ it: avatar: Avatar base: 'Errore Generale:' body: Corpo - blocks_ids: ID delle macro-attività bloccate category: Categoria comment: Commento comments: Commento @@ -2922,6 +2977,7 @@ it: title: Componente aggiuntivo Enterprise plan_title: Addon del %{plan} Enterprise plan_name: "%{plan} piano Enterprise" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Disponibile a partire dal %{plan_name}. unlimited: Illimitato already_have_token: 'Hai già un token? Aggiungilo usando il pulsante qui sotto per aggiornare al piano Enterprise prenotato. @@ -3391,6 +3447,11 @@ it: quick_add: label: Aggiungi… my_account: + notifications_and_email: + title: Notifiche ed email + tabs: + notifications: Impostazioni delle notifiche + email_reminders: Promemoria email 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: @@ -3453,6 +3514,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: Inviami un promemoria email + mentioned: Avvisami quando qualcuno mi menziona + personal_reminder: Notificami i promemoria personali + daily_reminders: + title: Inviami promemoria email giornalieri per le notifiche non lette + caption: Riceverai questi promemoria solo per le notifiche non lette e solo negli orari che specifichi. Finché non configuri un fuso orario per il tuo account, gli orari saranno interpretati come UTC. + enabled: Abilita promemoria email giornalieri + add_time: Aggiungi orario + remove_time: Rimuovi orario + time_slot_label: Ora del promemoria (UTC) + workdays: + title: Ricevi promemoria email in questi giorni + submit_button: Aggiorna i giorni di promemoria + pause_reminders: + title: Sospendi le notifiche email + enabled: Sospendi temporaneamente i promemoria email giornalieri + date_range: Periodo di sospensione + email_alerts: + title: Avvisi email per altri elementi che non sono macro-attività + news_added: Notizie aggiunte + news_commented: Commento su un articolo di notizie + document_added: Documento aggiunto + forum_messages: Messaggio del forum pubblicato + wiki_page_added: Pagina wiki aggiunta + wiki_page_updated: Pagina wiki aggiornata + membership_added: Iscrizione aggiunta + membership_updated: Iscrizione aggiornata + submit_button: Aggiorna gli avvisi + notifications: + participating: + title: Partecipazione + submit_button: Aggiorna impostazioni + mentioned: Menzionato + watched: Osservazione + assignee: Assegnatario + responsible: Responsabile + shared: Condivisi con me + date_alerts: + title: Avvisi sulle date + submit_button: Aggiorna gli avvisi sulle date + start_date: Data di inizio + due_date: Data di fine + overdue: In ritardo + times: + same_day: Lo stesso giorno + one_day_before: 1 giorno prima + three_days_before: 3 giorni prima + seven_days_before: 7 giorni prima + one_day_after: 1 giorno dopo + three_days_after: 3 giorni dopo + seven_days_after: 7 giorni dopo + non_participating: + title: Non partecipante + submit_button: Aggiorna impostazioni + work_package_created: Nuove macro-attività + work_package_commented: Tutti i nuovi commenti + work_package_processed: Tutte le modifiche di stato + work_package_prioritized: Tutte le modifiche prioritarie + work_package_scheduled: Tutte le modifiche della data + project_specific_settings: + title: Impostazioni di notifica specifiche del progetto + add_button: Aggiungi notifiche specifiche al progetto + dialog_title: Aggiungi notifiche specifiche al progetto + list_header: Progetti con notifiche specifiche notifications: reasons: assigned: Assegnatario @@ -3484,6 +3611,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 @@ -3524,6 +3652,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: Integrazioni label_add_column: Aggiungi colonna label_applied_status: Stato applicato label_archive_project: Archivia progetto @@ -3774,7 +3903,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à @@ -3812,6 +3941,10 @@ it: label_custom_pdf_export_settings: Impostazioni di esportazione PDF personalizzate label_custom_favicon: Favicon personalizzata label_custom_touch_icon: Icona del touch personalizzata + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Disconnetti label_mapping_for: 'Mappatura per: %{attribute}' label_main_menu: Menu laterale @@ -4882,6 +5015,7 @@ it: ' setting_app_subtitle: Sottotitolo applicazione setting_app_title: Titolo applicazione + setting_organization_name: Organization name setting_attachment_max_size: Dimensione max. dell'allegato setting_show_work_package_attachments: Mostra gli allegati nella scheda File per impostazione predefinita setting_antivirus_scan_mode: Modalità di scansione @@ -5028,12 +5162,12 @@ it: setting_welcome_text: Blocco di testo di benvenuto setting_welcome_title: Blocco di testo del titolo setting_welcome_on_homescreen: Mostra il blocco testo di benvenuto nella pagina home - setting_work_packages_identifier_numeric: Sequenza numerica a livello di istanza (predefinita) - setting_work_packages_identifier_numeric_caption: 'Ogni macro-attività riceve un numero sequenziale a partire da 1, incrementato a ogni nuova creazione. I numeri sono univoci all''interno di questa istanza, quindi restano invariati anche se le macro-attività vengono spostate tra progetti. + setting_work_packages_identifier_classic: Sequenza numerica a livello di istanza (predefinita) + setting_work_packages_identifier_classic_caption: 'Ogni macro-attività riceve un numero sequenziale a partire da 1, incrementato a ogni nuova creazione. I numeri sono univoci all''interno di questa istanza, quindi restano invariati anche se le macro-attività vengono spostate tra progetti. ' - setting_work_packages_identifier_alphanumeric: Identificatori alfanumerici basati su progetti - setting_work_packages_identifier_alphanumeric_caption: 'Ogni progetto ha un identificatore univoco che viene anteposto all''ID della macro-attività. Se una macro-attività viene spostata in un altro progetto, viene generato un nuovo identificatore, ma quello precedente continua a funzionare. + setting_work_packages_identifier_semantic: Identificatori semantici basati su progetti + setting_work_packages_identifier_semantic_caption: 'Ogni progetto ha un identificatore univoco che viene anteposto all''ID della macro-attività. Se una macro-attività viene spostata in un altro progetto, viene generato un nuovo identificatore, ma quello precedente continua a funzionare. ' setting_work_package_list_default_highlighting_mode: Modalità evidenziazione predefinita diff --git a/config/locales/crowdin/ja.yml b/config/locales/crowdin/ja.yml index 08fda701046..49694b2a9ad 100644 --- a/config/locales/crowdin/ja.yml +++ b/config/locales/crowdin/ja.yml @@ -114,7 +114,7 @@ ja: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ja: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: 名前 @@ -154,6 +154,7 @@ ja: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: プロジェクト last_change: Last change @@ -162,7 +163,7 @@ ja: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -324,13 +325,13 @@ ja: success: MCP configuration was updated successfully. scim_clients: authentication_methods: - sso: IDプロバイダーからのJWT - oauth2_client: OAuth 2.0クライアント認証情報 + sso: アイデンティティプロバイダからのJWT + oauth2_client: OAuth 2.0 クライアント資格情報 oauth2_token: 静的アクセストークン created_client_credentials_dialog_component: - title: クライアント認証情報の作成 - heading: クライアント認証情報が生成されました - one_time_hint: クライアント・シークレットが表示されるのはこの時だけです。必ずコピーしてください。 + title: クライアントの資格情報が作成されました + heading: クライアントの資格情報が生成されました + one_time_hint: クライアントのシークレットが表示される唯一の時間です。今すぐコピーしてください。 created_token_dialog_component: title: トークンを作成しました heading: トークンが生成されました @@ -343,21 +344,21 @@ ja: edit: label_delete_scim_client: SCIM クライアントを削除 form: - auth_provider_description: これは、SCIM プロバイダによって追加されたユーザが OpenProject で認証するために使用するサービスです。 - authentication_method_description_html: これは SCIM クライアントが OpenProject で認証する方法です。OAuth トークンにscim_v2スコープが含まれていることを確認してください。 - description: これらの設定オプションの詳細については、[SCIMクライアントの設定に関する文書](docs_url)を参照してください。 + auth_provider_description: これは、SCIMプロバイダが追加したユーザーがOpenProjectでの認証に使用するサービスです。 + authentication_method_description_html: これは SCIM クライアントが OpenProject で認証する方法です。OAuth トークンに scim_v2 スコープが含まれていることを確認してください。 + description: 設定オプションの詳細については、[SCIM クライアントの設定に関するドキュメント](docs_url)を参照してください。 jwt_sub_description: 例えば、Keycloakの場合、これはSCIMクライアントに関連付けられたサービスアカウントのUUIDです。あなたのユースケースにあった Subject claim を見つける方法については [ドキュメント](docs_url) を参照してください。 - name_description: このクライアントが設定された理由を他の管理者が理解しやすい名前を選んでください。 + name_description: 他の管理者がこのクライアントが設定された理由を理解するのに役立つ名前を選択してください。 index: - description: ここで設定された SCIM クライアントは、OpenProject SCIM サーバ API と対話し、ユーザアカウントやグループのプロビジョニング、更新、デプロビジョニングを行うことができます。 - label_create_button: SCIMクライアントの追加 + description: ここで設定されたSCIMクライアントは、OpenProjectのSCIMサーバー APIと相互作用して、ユーザーアカウントとグループのプロビジョニング、更新、およびデプロビジョニングを行うことができます。 + label_create_button: SCIMクライアントを追加 new: title: 新しいSCIMクライアント revoke_static_token_dialog_component: confirm_button: 取り消す - title: 静的トークンの失効 - heading: このトークンを本当に取り消しますか? - description: このトークンを使っている SCIM クライアントは、OpenProject の SCIM サーバ API にアクセスできなくなります。 + title: 静的トークンを取り消す + heading: このトークンを取り消してもよろしいですか? + description: このトークンを使用する SCIM クライアントは、OpenProject の SCIM サーバ API にアクセスできなくなります。 table_component: blank_slate: title: SCIMクライアントがまだ設定されていません @@ -381,9 +382,9 @@ ja: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +393,14 @@ ja: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +414,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 @@ -674,6 +679,37 @@ ja: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -996,26 +1032,26 @@ ja: other: また、 %{shared_work_packages_link} はこのユーザーと共有されています。 remove_project_membership_or_work_package_shares_too: 直接のメンバーとしてのユーザーだけを削除したい(および共有を維持したい)、またはワークパッケージの共有も削除しますか? will_remove_all_user_access_priveleges: このメンバーを削除すると、プロジェクトへのユーザーのすべてのアクセス権が削除されます。ユーザーはまだサイトの一部として存在します。 - will_remove_all_group_access_priveleges: このメンバを削除すると、プロジェクトに対するグループのすべてのアクセス権が削除されます。グループはサイトの一部としてまだ存在します。 - cannot_delete_inherited_membership: このプロジェクトのメンバーであるグループに所属しているため、このメンバーを削除することはできません。 - cannot_delete_inherited_membership_note_admin_html: "%{administration_settings_link}で、プロジェクトのメンバーとしてグループを削除することも、特定のメンバーをグループから削除することもできます。" - cannot_delete_inherited_membership_note_non_admin: プロジェクトのメンバーとしてグループを削除するか、管理者に連絡してこの特定のメンバーをグループから削除することができます。 + will_remove_all_group_access_priveleges: このメンバーを削除すると、グループのすべてのアクセス権がプロジェクトに削除されます。グループはサイトの一部として存在します。 + cannot_delete_inherited_membership: このメンバーはこのプロジェクトのメンバーであるグループに属しているため、削除できません。 + cannot_delete_inherited_membership_note_admin_html: プロジェクトのメンバーとしてグループを削除するか、 %{administration_settings_link} のグループからこの特定のメンバーを削除することができます。 + cannot_delete_inherited_membership_note_non_admin: プロジェクトのメンバーとしてグループを削除するか、管理者に問い合わせてグループから特定のメンバーを削除することができます。 delete_work_package_shares_dialog: - title: ワーク・パッケージ・シェアの破棄 + title: ワークパッケージの共有の取り消し shared_with_this_user_html: other: "%{all_shared_work_packages_link} はこのユーザーと共有されています。" shared_with_this_group_html: other: "%{all_shared_work_packages_link} はこのグループと共有されています。" shared_with_permission_html: other: "%{shared_work_packages_link} のみが %{shared_role_name} 権限と共有されています。" - revoke_all_or_with_role: すべての共有ワークパッケージ、または %{shared_role_name} 権限を持つワークパッケージのみへのアクセス権を剥奪しますか? - will_not_affect_inherited_shares: "(これは、そのグループと共有しているワークパッケージには影響しません)。" - cannot_remove_inherited: グループで共有されたワークパッケージの共有は削除できません。 - cannot_remove_inherited_with_role: ロール %{shared_role_name} で共有されるワークパッケージは、グループを介して共有され、削除することはできません。 - cannot_remove_inherited_note_admin_html: "%{administration_settings_link}、グループへの共有を取り消すか、グループからこの特定のメンバーを削除することができます。" - cannot_remove_inherited_note_non_admin: グループへの共有を取り消すか、管理者に連絡して特定のメンバーをグループから削除することができます。 - will_revoke_directly_granted_access: このアクションは、グループと共有されているワークパッケージ以外の、すべてのワークパッケージへのアクセス権を剥奪する。 - will_revoke_access_to_all: このアクションは、すべてのアクセス権を剥奪する。 + revoke_all_or_with_role: 共有されたワークパッケージ、または %{shared_role_name} 権限を持つワークパッケージのみへのアクセスを取り消しますか? + will_not_affect_inherited_shares: "(これはグループと共有されているワークパッケージには影響しません)。" + cannot_remove_inherited: グループ間で共有されるワークパッケージは削除できません。 + cannot_remove_inherited_with_role: ワークパッケージとロール %{shared_role_name} が共有されているため、削除できません。 + cannot_remove_inherited_note_admin_html: あなたは、グループへの共有を取り消すか、 %{administration_settings_link} のグループからこの特定のメンバーを削除することができます。 + cannot_remove_inherited_note_non_admin: 共有をグループに取り消すか、管理者に問い合わせてグループから特定のメンバーを削除することができます。 + will_revoke_directly_granted_access: このアクションは、すべてのユーザーへのアクセスを取り消しますが、グループと共有されているワークパッケージです。 + will_revoke_access_to_all: このアクションは、すべてのユーザーへのアクセスを取り消します。 my: access_token: dialog: @@ -1039,7 +1075,7 @@ ja: no_results_title_text: 現在、有効なアクセス トークンはありません。 notice_api_token_revoked: APIトークンが削除されました。新しいトークンを作成するには、APIセクションの作成ボタンを使用してください。 notice_rss_token_revoked: RSSトークンが削除されました。新しいトークンを作成するには、RSSセクションのリンクを使用してください。 - notice_ical_token_revoked: プロジェクト "%{project_name}" のカレンダー "%{calendar_name}" の iCalendar トークン "%{token_name}" が失効しました。このトークンを持つiCalendar URLは無効になりました。 + notice_ical_token_revoked: プロジェクト "%{token_name}" のカレンダー "%{calendar_name}" の iCalendar トークン "%{project_name}" が取り消されました。 このトークンのiCalendar URLは無効です。 password_confirmation_dialog: confirmation_required: You need to enter your account password to confirm this change. title: Confirm your password to continue @@ -1060,7 +1096,7 @@ ja: matrix_check_uncheck_all_in_col_label_html: Toggle all %{module} permissions for %{role} role users: autologins: - prompt: ログインしたまま %{num_days} + prompt: "%{num_days} のログインを維持" sessions: session_name: "%{browser_name} %{browser_version} の %{os_name}" browser: ブラウザ @@ -1074,17 +1110,17 @@ ja: current: Current (this device) title: セッション管理 instructions: You are logged in to your account through the following devices. Revoke sessions that you do not recognise or from devices you do not control. - may_not_delete_current: 現在のセッションを削除することはできません。 + may_not_delete_current: 現在のセッションは削除できません。 deletion_warning: Are you sure you want to revoke this session? You will be logged out on this device. groups: member_in_these_groups: このユーザーは現在以下のグループのメンバーです: no_results_title_text: このユーザーは現在どのグループのメンバーでもありません。 summary_with_more_html: Member of %{names} and %{count_link}. - more: "%{count} もっと見る" + more: "%{count} 以上" summary_html: Member of %{names}. memberships: no_results_title_text: このユーザは現在プロジェクトのメンバーではありません。 - open_profile: プロフィール + open_profile: プロファイルを開く invite_user_modal: invite: 招待 title: @@ -1201,7 +1237,7 @@ ja: right_to_manage_members_missing: 'プレースホルダーユーザを削除する権限がありません。 プレースホルダー ユーザーがメンバーであるすべてのプロジェクトのメンバーを管理する権利はありません。 ' - delete_tooltip: プレースホルダー・ユーザーの削除 + delete_tooltip: プレースホルダー ユーザーを削除 deletion_info: heading_html: Delete placeholder user %{name} data_consequences: 'プレースホルダー ユーザのすべての発生(担当者、担当者、その他のユーザ値など)は、「削除されたユーザー」というアカウントに再割り当てられます。 削除されたすべてのアカウントのデータがこのアカウントに再割り当てられるため、ユーザーが作成したデータと別の削除されたアカウントのデータを区別することはできません。 @@ -1220,11 +1256,11 @@ ja: reactions: action_title: リアクト add_reaction: リアクションを追加 - react_with: "%{reaction} と リアクト" - and_user: および %{user} + react_with: "%{reaction} で反応する" + and_user: と %{user} and_others: other: と %{count} その他 - reaction_by: "%{reaction} によって" + reaction_by: "%{reaction} による" reportings: index: no_results_title_text: 現在、ステータス報告はありません。 @@ -1234,19 +1270,20 @@ ja: status_color_text: | このステータスの色を割り当てたり変更する場合にクリックします。 ステータスボタンに表示され、テーブル内のワークパッケージを強調表示するために使用できます。 - status_default_text: 新しいワークパッケージは、デフォルトでこのタイプに設定される。読み取り専用にはできない。 + status_default_text: 新しいワークパッケージはデフォルトでこのタイプに設定されています。読み取り専用にすることはできません。 status_excluded_from_totals_text: |- - このステータスを持つワークパッケージを、階層内の「作業」、「 - 残作業」、「完了率」の合計から除外するには、このオプションをオンにします。 + このオプションをオンにすると、このステータスのワークパッケージを合計作業量、 + 残作業量、および階層構造で完了させることができます。 status_percent_complete_text_html: |- In [status-based progress calculation mode](setting_url), the % Complete of a work package is automatically set to this value when this status is selected. Ignored in work-based mode. status_readonly_html: | - このステータスを持つワークパッケージを読み取り専用としてマークするには、このオプションをチェックする。 - ステータス以外の属性は変更できません。 + ワークパッケージを読み取り専用としてマークするには、このオプションをオンにしてください。 + ステータスを除いて変更することはできません。 +
- 注意: 継承された値 (子やリレーションなど) は適用されます。 + メモ: 継承された値 (例えば、子や関連) が適用されます。 index: no_results_title_text: 現在、ワークパッケージのステータスはありません。 no_results_content_text: 新しいステータスを追加 @@ -1256,7 +1293,7 @@ ja: is_readonly: 読み取り専用 excluded_from_totals: 合計から除外 themes: - dark: 暗い + dark: ダーク light: ライト sync_with_os: 自動(OSのテーマ設定に追従) types: @@ -1324,6 +1361,20 @@ ja: index: no_results_title_text: 現在、ワークフローはありません。 work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1374,15 +1425,15 @@ ja: could_not_be_saved: '次のワークパッケージを保存できませんでした:' none_could_be_saved: "%{total} ワークパッケージのどれも更新できませんでした。" x_out_of_y_could_be_saved: "%{failing} の %{total} ワークパッケージのうち、 %{success} を更新できませんでした。" - selected_because_descendants: "%{selected} のワークパッケージが選択されたが、合計 %{total} のワークパッケージが影響を受け、その中には子孫も含まれる。" - descendant: 選択された子孫 + selected_because_descendants: "%{selected} ワークパッケージが選択されている間、合計で %{total} ワークパッケージが子孫を含む影響を受けます。" + descendant: 選択された子孫です move: no_common_statuses_exists: 選択されたすべてのワークパッケージに利用できるステータスはありません。 それらの状態は変更できません。 unsupported_for_multiple_projects: 複数のプロジェクトからのワークパッケージの一括移動 / コピーはサポートされていません - current_type_not_available_in_target_project: 'ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + current_type_not_available_in_target_project: 'ターゲット プロジェクトで現在のワークパッケージのタイプが有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 ' - bulk_current_type_not_available_in_target_project: 'ワークパッケージの現在のタイプがターゲットプロジェクトで有効になっていません。変更しない場合は、ターゲットプロジェクトでタイプを有効にしてください。そうでない場合は、リストからターゲットプロジェクトで使用可能なタイプを選択してください。 + bulk_current_type_not_available_in_target_project: '現在のタイプのワークパッケージはターゲット プロジェクトで有効になっていません。 変更を行わないようにしたい場合は、対象プロジェクトのタイプを有効にしてください。 それ以外の場合は、リストからターゲット プロジェクトで使用可能なタイプを選択します。 ' sharing: @@ -1408,9 +1459,9 @@ ja: no_results_title_text: 現在、有効なバージョンはありません。 work_package_relations_tab: index: - action_bar_title: 他のワークパッケージとのリレーションを追加して、それらの間にリンクを作成する。 - no_results_title_text: 現在、利用可能な関係はない。 - blankslate_heading: 関係なし + action_bar_title: 他のワークパッケージにリレーションを追加して、その間にリンクを作成します。 + no_results_title_text: 現在利用可能なリレーションはありません。 + blankslate_heading: リレーションなし blankslate_description: このワークパッケージにはまだリレーションがありません。 label_add_child_button: 子要素 label_add_x: "%{x} を追加" @@ -1518,6 +1569,9 @@ ja: dependencies: 依存関係 activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1526,7 +1580,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: @@ -1782,6 +1836,7 @@ ja: identity_url: アイデンティティ URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1901,6 +1956,7 @@ ja: confirmation: は%{attribute} と一致しません。 could_not_be_copied: "%{dependency} を(完全に)コピーできませんでした。" does_not_exist: は存在しません。 + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: アクセスできません。 error_readonly: 書けませんでした。 @@ -1967,6 +2023,7 @@ ja: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2429,7 +2486,6 @@ ja: avatar: アバター base: '一般的なエラー:' body: 本文 - blocks_ids: ブロックされているワークパッケージのID category: カテゴリ comment: コメント comments: コメント @@ -2866,6 +2922,7 @@ ja: title: エンタープライズアドオン plan_title: エンタープライズ%{plan}アドオン plan_name: "%{plan}のエンタープライズプラン" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: "%{plan_name} から利用できます。" unlimited: 無制限です already_have_token: 'すでにトークンをお持ちですか?予約済みのエンタープライズプランにアップグレードするには、下のボタンを使用して追加してください。 @@ -3334,6 +3391,11 @@ ja: quick_add: label: 追加 my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3396,6 +3458,72 @@ ja: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: 不明なストレージ + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: 担当者 @@ -3427,6 +3555,7 @@ ja: invalid_filter: 無効な通知フィルター label_accessibility: アクセシビリティ label_account: アカウント + label_actions: 操作 label_active: アクティブ label_activate_user: ユーザーのアクティベート label_active_in_new_projects: 新しいプロジェクトでアクティブ @@ -3467,6 +3596,7 @@ ja: label_ical_access_key_generation_hint: カレンダーを購読するときに自動的に生成されます。 label_ical_access_key_latest: 最新 label_ical_access_key_revoke: 取り消し + label_integrations: Integrations label_add_column: カラムを追加 label_applied_status: 適用されるステータス label_archive_project: プロジェクトをアーカイブ @@ -3717,7 +3847,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: 最新の活動 @@ -3755,6 +3885,10 @@ ja: label_custom_pdf_export_settings: PDF エクスポートの設定 label_custom_favicon: カスタムの favicon label_custom_touch_icon: カスタムのタッチアイコン + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: ログアウト label_mapping_for: 'マッピング: %{attribute}' label_main_menu: サイドメニュー @@ -4818,6 +4952,7 @@ ja: ' setting_app_subtitle: アプリケーションのサブタイトル setting_app_title: アプリケーションのタイトル + setting_organization_name: Organization name setting_attachment_max_size: 添付ファイルの最大サイズ setting_show_work_package_attachments: デフォルトで添付ファイルをファイルタブに表示 setting_antivirus_scan_mode: スキャンモード @@ -4962,12 +5097,12 @@ ja: setting_welcome_text: ようこそブロックのテキスト setting_welcome_title: ようこそブロックのタイトル setting_welcome_on_homescreen: ホーム画面にようこそブロックを表示 - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: デフォルトの強調表示モード diff --git a/config/locales/crowdin/js-af.yml b/config/locales/crowdin/js-af.yml index f7b34cc5e9a..39be3096870 100644 --- a/config/locales/crowdin/js-af.yml +++ b/config/locales/crowdin/js-af.yml @@ -515,12 +515,6 @@ af: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ af: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Gedelegeerde - responsible: Accountable - shared: Shared - watched: Dophouer - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ af: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Nuus bygevoeg - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ af: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-ar.yml b/config/locales/crowdin/js-ar.yml index 3eebc12b88a..7bca67352c4 100644 --- a/config/locales/crowdin/js-ar.yml +++ b/config/locales/crowdin/js-ar.yml @@ -515,12 +515,6 @@ ar: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "... وحدد إدخال لوحة المهام ." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -596,49 +590,6 @@ ar: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: المُسند إليه - responsible: Accountable - shared: Shared - watched: المشاهد - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: أنت في الصفحة فقط. pages_skipped: Pages skipped. @@ -662,39 +613,6 @@ ar: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: تم إضافة أحداث - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: تم إضافة صفحة wiki - wiki_page_updated: تم تحديث صفحة wiki - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: هل أنت متأكد؟ text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1067,13 +985,6 @@ ar: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-az.yml b/config/locales/crowdin/js-az.yml index 76c557aa86b..4079c5005f6 100644 --- a/config/locales/crowdin/js-az.yml +++ b/config/locales/crowdin/js-az.yml @@ -515,12 +515,6 @@ az: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ az: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ az: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ az: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-be.yml b/config/locales/crowdin/js-be.yml index 34de2459891..715f8014922 100644 --- a/config/locales/crowdin/js-be.yml +++ b/config/locales/crowdin/js-be.yml @@ -515,12 +515,6 @@ be: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -594,49 +588,6 @@ be: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Прызначаная асоба - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Паведамленні аб актыўнасці ў працоўных пакетах, у якіх вы ўдзельнічаеце (адказны, падсправаздачны або назіральнік). - delayed: - title: Non-participating - description: Дадатковыя паведамленні аб актыўнасці ва ўсіх праектах. - date_alerts: - title: Date alerts - description: Аўтаматычныя паведамленні аб важных падыходзячых датах у адчыняннях працоўных пакетах, у якіх вы ўдзельнічаеце (адказны, падсправаздачны або назіральнік). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: Гэтыя спецыфічныя налады праекта перапісваюць налады па ўмаўчанні вышэй. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -660,39 +611,6 @@ be: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1065,13 +983,6 @@ be: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-bg.yml b/config/locales/crowdin/js-bg.yml index f890525f205..7a84a3882b6 100644 --- a/config/locales/crowdin/js-bg.yml +++ b/config/locales/crowdin/js-bg.yml @@ -515,12 +515,6 @@ bg: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ bg: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Изпълнител - responsible: Accountable - shared: Shared - watched: Наблюдател - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ bg: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Добавени новини - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki страница добавена - wiki_page_updated: Wiki страница обновена - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Сигурни ли сте? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ bg: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-ca.yml b/config/locales/crowdin/js-ca.yml index 28b3fb06363..a8234eddcfa 100644 --- a/config/locales/crowdin/js-ca.yml +++ b/config/locales/crowdin/js-ca.yml @@ -109,7 +109,7 @@ ca: button_save: Desa button_settings: Configuració button_uncheck_all: Desmarca-ho tot - button_update: Actualitzar + button_update: Actualitza button_export-atom: Descarregar Atom button_generate_pdf: Generate PDF button_create: Crear @@ -515,12 +515,6 @@ ca: sidebar_arrow: Utilitza la fletxa de retorn a la cantonada esquerra per tornar al menú principal del projecte. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Dintre de la wiki pots documentar i compartir el coneixement conjuntament amb el teu equip. - backlogs: - overview: Administra la teva feina en vistes de treballs pendents. - sprints: A la dreta hi tens els treballs pendents del producte i errors, a l'esquerra hi tens els "sprint" respectius. Aquí pots crear èpiques, històries d'usuari i errors, prioritza'ls arrossegant-los i afegeix-los a un "sprint". - task_board_arrow: Per veure el teu Tauler de tasques, obri el desplegable "sprint"... - task_board_select: "... i seleccioni l'entrada del tauler de tasca." - task_board: En el tauler de tasques es pot visualitzar el progrés d'aquest "sprint". Fes clic a la icona més (+) al costat d'una història d'usuari per afegir noves tasques o impediments.
L'estat es pot actualitzar arrossegant. boards: overview: Selecciona els taulers per canviar la vista i administra el teu projecte utilitza la vista de taulers "agile". lists_kanban: Aquí pots crear múltiples llistes (columnes) en el teu taulell. Aquesta funció, per exemple, permet crear un taulell Kanban. @@ -592,49 +586,6 @@ ca: settings: change_notification_settings: Pots modificar la teva configuració de notificacions per assegurar-te que mai et perds informació important. title: Configuració de notificacions - notify_me: Notifica’m - reminders: - no_notification: Sense notificació - timeframes: - normal: - PT0S: el mateix dia - P1D: 1 dia abans - P3D: 3 dies abans - P7D: una setmana abans - overdue: - P1D: cada dia - P3D: cada 3 dies - P7D: cada setmana - reasons: - mentioned: - title: Mencionat - description: Rep una notificació cada vegada que algú em menciona a qualsevol lloc - assignee: Assignat a - responsible: Responsable - shared: Compartida - watched: Observador - work_package_commented: Tots els nous comentaris - work_package_created: Paquets de treball nou - work_package_processed: Tots els canvis d'estat - work_package_prioritized: Tots els canvis de prioritat - work_package_scheduled: Tots els canvis de data - global: - immediately: - title: Participant - description: Notificacions per a totes les activitats en els paquets de treball en els que estàs involucrat (assignat, responsable o observador). - delayed: - title: No participant - description: Notificacions addicionals per activitats en tots els projectes. - date_alerts: - title: Alertes per dates - description: Notificacions automàtiques quan dates importants s'aproximen per a paquets de treball oberts en els quals estàs involucrat (assignat, responsable o observador). - overdue: Quan vençut - project_specific: - title: Configuracions de notificacions específiques del projecte - description: Aquestes configuracions específiques de projecte anul·len les configuracions per defecte especificades a sobre. - add: Afegeix configuracions pel projecte - already_selected: Aquest projecte ja està seleccionat - remove: Elimina les configuracions del projecte pagination: no_other_page: Estàs en la pàgina única. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ ca: required_outside_context: 'Si us plau, selecciona un projecte on crear el paquet de treball per a veure tots els atributs. Només pots seleccionar projectes que tinguin activat la classe indicada anteriorment. ' - reminders: - settings: - daily: - add_time: Afegeix temps - enable: Activa els correus electrònics recordatori diaris - explanation: Rebràs aquests recordatoris per a notificacions no llegides a les hores que especifiquis. %{no_time_zone} - no_time_zone: Fins que no configuris una zona horària per al teu compte, els temps seran interpretats en UTC. - time_label: 'Temps %{counter}:' - title: Envia'm correus electrònics recordatori diaris per a notificacions no llegides. - workdays: - title: Rep recordatoris de correus electrònics recordatori aquests dies - immediate: - title: Envia'm un correu electrònic recordatori - mentioned: Immediatament quan algú em @menciona - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Alertes en correu electrònic per altres elements (que no són paquets de treball) - explanation: 'Ara mateix les notificacions estan limitades als paquets de treball. Pots seleccionar continuar rebent alertes en correus electrònics per aquells esdeveniments fins que s''incloguin a les notificacions: - - ' - news_added: Notícies afegides - news_commented: Comenta a un element de notícia - document_added: Documents afegits - forum_messages: Nou missatges de fòrum - wiki_page_added: S'ha afegit la pàgina wiki - wiki_page_updated: S'ha actualitzat la pàgina wiki - membership_added: Afiliació afegida - membership_updated: Afiliació actualitzada - title: Correus electrònics recordatori - pause: - label: Pausa els correu electrònic recordatori temporalment - first_day: Primer dia - last_day: Últim dia text_are_you_sure: N'esteu segur? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ ca: form_submit: title: Confirma per continuar text: Estàs segur que vols realitzar aquesta acció? - destroy_work_package: - title: Confirma l'eliminació de %{label} - single_text: Estàs segur que vols eliminar aquest paquet de treball - bulk_text: Estàs segur que vols eliminar el següent %{label}? - has_children: 'Aquest paquet de treball té %{childUnits}:' - confirm_deletion_children: Entenc que TOTS els descendents dels paquets de treball llistats seran eliminats recursivament. - deletes_children: Tots els paquets de treball fills i els seus descendents també seran eliminats recursivament. destroy_time_entry: title: Confirma l'eliminació de l'entrada temporal text: Estàs segur que vols eliminar la següent l'entrada temporal? diff --git a/config/locales/crowdin/js-ckb-IR.yml b/config/locales/crowdin/js-ckb-IR.yml index dd8da879f22..79a003cdf4c 100644 --- a/config/locales/crowdin/js-ckb-IR.yml +++ b/config/locales/crowdin/js-ckb-IR.yml @@ -515,12 +515,6 @@ ckb-IR: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ ckb-IR: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ ckb-IR: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ ckb-IR: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-cs.yml b/config/locales/crowdin/js-cs.yml index 6b8e5cd449d..576c363463a 100644 --- a/config/locales/crowdin/js-cs.yml +++ b/config/locales/crowdin/js-cs.yml @@ -515,12 +515,6 @@ cs: sidebar_arrow: Pomocí šipky zpět v levém horním rohu se vrátíte do hlavního menu projektu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: V rámci Wiki můžete dokumentovat a sdílet znalosti společně se svým týmem. - backlogs: - overview: Spravujte svou práci v backlogs zobrazení. - sprints: Napravo máte nevyřízené produkty a nevyřízené chyby, vlevo máte příslušné sprinty. Zde můžete vytvořit epiky, uživatelské příběhy a chyby, upřednostnit přetažením a přidat je do sprintu. - task_board_arrow: Chcete-li vidět Váš seznam úkolů, otevřete Sprint drop-down menu... - task_board_select: "...a vyberte vstup do tabule." - task_board: Pracovní panel vizualizuje postup pro tento sprint. Kliknutím na ikonu plus (+) vedle uživatelského příběhu přidáte nové úkoly nebo překážky.
Stav lze aktualizovat přetažením. boards: overview: Vyberte tabule pro posun zobrazení a správu vašeho projektu pomocí agilního zobrazení desek. lists_kanban: Zde si můžete vytvořit více seznamů (sloupce) ve vaší tabuli. Tato funkce vám umožní například vytvořit Kanban desku. @@ -598,51 +592,6 @@ cs: settings: change_notification_settings: Můžete změnit nastavení oznámení , abyste se ujistili, že nikdy nezmeškáte důležitou aktualizaci. title: Nastavení oznámení - notify_me: Upozornit mě - reminders: - no_notification: Žádné oznámení - timeframes: - normal: - PT0S: Týž den - P1D: před 1 dnem - P3D: 3 dny před - P7D: před týdnem - overdue: - P1D: každý den - P3D: Každé 3 dny - P7D: každý týden - reasons: - mentioned: - title: Zmíněné - description: Dostat upozornění, kdykoli mě někdo kdekoli zmíní - assignee: Řešitel - responsible: Odpovědný - shared: Sdílené - watched: Sledující - work_package_commented: Všechny nové komentáře - work_package_created: Nový pracovní balíček - work_package_processed: Všechny změny stavu - work_package_prioritized: Všechny prioritní změny - work_package_scheduled: Všechny změny data - global: - immediately: - title: Participace - description: Upozornění pro všechny aktivity v pracovních balíčcích, do kterých jste zapojeni (Řešitel, odpovědný nebo sledující). - delayed: - title: Neúčast - description: Další oznámení pro činnosti ve všech projektech. - date_alerts: - title: Upozornění na datum - description: Automatická oznámení, pokud se blíží důležitá data pro otevřené pracovní balíčky, které jste zapojeni (přiřazený, zodpovědný nebo sledovaný). - overdue: Po termínu - project_specific: - title: 'Nastavení upozornění pro konkrétní projekt - - ' - description: Toto specifické nastavení projektu přepíše výchozí nastavení výše. - add: Přidat nastavení pro projekt - already_selected: Tento projekt je již vybrán - remove: Odstranit nastavení projektu pagination: no_other_page: Jste na jediné stránce. pages_skipped: Stránky přeskočeny. @@ -666,39 +615,6 @@ cs: required_outside_context: 'Vyberte projekt pro vytvoření pracovního balíčku, abyste viděli všechny atributy. Můžete vybrat pouze projekty, které mají výše uvedený typ aktivován. ' - reminders: - settings: - daily: - add_time: Přidat čas - enable: Zapnout denní posílání oznámení e-mailem - explanation: Tyto připomínky obdržíte pouze pro nepřečtená oznámení a pouze v hodinách, které zadáte. %{no_time_zone} - no_time_zone: Dokud nenastavíte časové pásmo pro svůj účet, budou časy interpretovány v UTC. - time_label: 'Čas %{counter}:' - title: 'Posílat mi denní souhrn nepřečtených oznámení e-mailem ' - workdays: - title: posílat e-mailem v těchto dnech - immediate: - title: Pošlete mi připomenutí e-mailem - mentioned: V momente když mě někdo @zmiňuje - personal_reminder: Okamžitě po osobní připomenutí - alerts: - title: E-mailová upozornění pro ostatní položky (které nejsou pracovní balíčky) - explanation: 'Oznámení jsou dnes omezena na pracovní balíčky. Můžete se rozhodnout nadále dostávat e-mailová upozornění pro tyto události, dokud nebudou zahrnuta do oznámení: - - ' - news_added: Přidané novinky - news_commented: Komentář k novince - document_added: Dokumenty přidány - forum_messages: Nové zprávy o fóru - wiki_page_added: Přidána stránka wiki - wiki_page_updated: Wiki stránka aktualizována - membership_added: Členství přidáno - membership_updated: Členství bylo aktualizováno - title: E-mailová upozornění - pause: - label: Dočasně pozastavit denní připomenutí e-mailem - first_day: První den - last_day: Poslední den text_are_you_sure: Jste si jisti? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Navigační lišta @@ -1071,13 +987,6 @@ cs: form_submit: title: Potvrdit a pokračovat text: Opravdu chcete provést tuto akci? - destroy_work_package: - title: Potvrdit smazání %{label} - single_text: Opravdu chcete odstranit tento balíček? - bulk_text: Opravdu chcete odstranit %{label}? - has_children: 'Pracovní balíček má %{childUnits}:' - confirm_deletion_children: Jsem si vědom, že všechny potomky uvedených pracovních balíčků budou rekurzivně odstraněny. - deletes_children: Všechny podřízené pracovní balíčky a jejich potomci budou také rekurzivně odstraněny. destroy_time_entry: title: Potvrdit odstranění záznamu času text: Opravdu chcete odstranit následující časovou položku? diff --git a/config/locales/crowdin/js-da.yml b/config/locales/crowdin/js-da.yml index edb28721551..6337f9f4311 100644 --- a/config/locales/crowdin/js-da.yml +++ b/config/locales/crowdin/js-da.yml @@ -515,12 +515,6 @@ da: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ da: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Tildelt - responsible: Accountable - shared: Shared - watched: Tilsynsførende - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ da: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Nyheder tilføjet - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wikiside tilføjet - wiki_page_updated: Wikiside opdateret - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Sikker? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ da: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-de.yml b/config/locales/crowdin/js-de.yml index 3e151196b1a..86a946bc6f1 100644 --- a/config/locales/crowdin/js-de.yml +++ b/config/locales/crowdin/js-de.yml @@ -143,7 +143,7 @@ de: description_available_columns: Verfügbare Spalten description_current_position: 'Sie sind hier: ' description_select_work_package: 'Arbeitspaket #%{id} auswählen' - description_subwork_package: 'Unteraufgabe von Arbeitspaket #%{id}' + description_subwork_package: 'Kind von Arbeitspaket #%{id}' editor: revisions: Lokale Änderungen anzeigen no_revisions: Keine lokalen Änderungen gefunden @@ -460,7 +460,7 @@ de: label_total_progress: "%{percent}% Gesamtfortschritt" label_total_amount: 'Gesamt: %{amount}' label_updated_on: aktualisiert am - label_value_derived_from_children: "(aggregierter Wert von Unteraufgaben)" + label_value_derived_from_children: "(aggregierter Wert von Kindelementen)" label_children_derived_duration: Aggregierte Dauer der Unteraufgaben label_warning: Warnung label_work_package: Arbeitspaket @@ -515,12 +515,6 @@ de: sidebar_arrow: Benutzen Sie den Pfeil in der oberen linken Ecke um zum Hauptmenü des Projekts zurückzukehren. welcome: Lernen Sie in drei Minuten die wichtigen Funktionen kennen.
Wir empfehlen Ihnen, die Tour vollständig abzuschließen. Sie können diese jederzeit wieder neu starten. wiki: Im Wiki können Sie gemeinsam mit dem Team Informationen dokumentieren und eine Wissensdatenbank aufbauen. - backlogs: - overview: Verwalten Sie Ihre Arbeit in der -Backlogs Ansicht. - sprints: Rechts befinden sich das Product Backlog und das Bug Backlog, links die jeweiligen Sprints. Hier können Sie Epics, User Stories und Bugs (Fehler) anlegen, mit Drag and Drop priorisieren und einem Sprint zuweisen. - task_board_arrow: Um das Taskboard zu sehen, öffnen Sie das Sprint Drop-down Menü... - task_board_select: "… und wählen Sie den Menüpunkt Taskboard." - task_board: Das Aufgabenfeld visualisiert den Fortschritt für diesen Sprint. Klicken Sie auf das Plus (+) Symbol neben einer User Story, um neue Aufgaben oder Hindernisse hinzuzufügen.
Der Status kann per Drag & Drop aktualisiert werden. boards: overview: Wählen Sie Boards aus, um eine andere Sicht auf Ihr Projekt zu erhalten und es in der agilen Board-Ansicht zu verwalten. lists_kanban: Hier können Sie mehrere Listen (Spalten) innerhalb Ihres Boards erstellen. Mit dieser Funktion können Sie zum Beispiel ein Kanban Board erstellen. @@ -594,49 +588,6 @@ de: settings: change_notification_settings: Sie können Ihre Benachrichtigungseinstellungen ändern, um sicherzustellen, dass Sie keine wichtige Aktualisierung verpassen. title: Benachrichtigungseinstellungen - notify_me: Benachrichtige mich - reminders: - no_notification: Keine Benachrichtigung - timeframes: - normal: - PT0S: am selben Tag - P1D: 1 Tag vorher - P3D: 3 Tage vorher - P7D: eine Woche vorher - overdue: - P1D: jeden Tag - P3D: alle 3 Tage - P7D: jede Woche - reasons: - mentioned: - title: Erwähnt - description: Jedes Mal eine Benachrichtigung erhalten, wenn ich irgendwo erwähnt werde - assignee: Zugewiesen an - responsible: Verantwortlich - shared: Geteilt - watched: Beobachter - work_package_commented: Alle neuen Kommentare - work_package_created: Neue Arbeitspakete - work_package_processed: Änderungen am Status - work_package_prioritized: Änderungen der Priorität - work_package_scheduled: Alle Datumsänderungen - global: - immediately: - title: Beteiligt - description: Benachrichtigungen für alle Aktivitäten in Arbeitspaketen, an denen Sie beteiligt sind (Zugewiesen an, Verantwortlicher oder Beobachter). - delayed: - title: Nicht beteiligt - description: Zusätzliche Benachrichtigungen für Aktivitäten in allen Projekten. - date_alerts: - title: Datums-Erinnerungen - description: Automatische Benachrichtigungen, wenn wichtige Termine für offene Arbeitspakete bevorstehen, an denen Sie beteiligt (z. B. zugewiesen, verantwortlich oder Beobachter) sind. - overdue: Wenn überfällig - project_specific: - title: Projektspezifische Benachrichtigungseinstellungen - description: Diese projektspezifischen Einstellungen überschreiben die Standardeinstellungen oben - add: Einstellung für Projekt hinzufügen - already_selected: Dieses Projekt ist bereits ausgewählt - remove: Projektspezifische Einstellungen entfernen pagination: no_other_page: Sie befinden sich auf der einzigen Seite. pages_skipped: Seiten übersprungen. @@ -660,39 +611,6 @@ de: required_outside_context: 'Bitte wählen Sie ein Projekt für das Arbeitspaket, um alle Attribute anzuzeigen. Sie können nur Projekte auswählen, für die der ausgewählte Typ oben aktiviert ist. ' - reminders: - settings: - daily: - add_time: Zeit hinzufügen - enable: Tägliche E-Mail-Erinnerungen aktivieren - explanation: Sie erhalten diese Erinnerungen nur für ungelesene Benachrichtigungen und nur zu Uhrzeiten, die Sie angegeben haben. %{no_time_zone} - no_time_zone: Bis Sie eine Zeitzone für Ihr Konto konfigurieren, sind diese Zeiten als UTC (GMT+0) zu verstehen. - time_label: 'Zeit %{counter}:' - title: Tägliche E-Mail-Erinnerungen für ungelesene Benachrichtigungen zusenden - workdays: - title: E-Mail-Erinnerungen an diesen Tagen erhalten - immediate: - title: Eine E-Mail Erinnerung an mich senden - mentioned: Sofort, wenn mich jemand @erwähnt - personal_reminder: Sofort, wenn ich eine persönliche Erinnerung erhalte - alerts: - title: E-Mail-Benachrichtigungen für andere Objekte (die keine Arbeitspakete sind) - explanation: 'Die Benachrichtigungen sind aktuell auf Arbeitspakete beschränkt. Sie können die E-Mail-Benachrichtigungen für diese Ereignisse so lange erhalten, bis sie in den Benachrichtigungen enthalten sind: - - ' - news_added: Neuigkeiten hinzugefügt - news_commented: Kommentar zu einer Neuigkeit - document_added: Dokumente hinzugefügt - forum_messages: Neue Nachrichten im Forum - wiki_page_added: Wiki-Seite hinzugefügt - wiki_page_updated: Wiki-Seite aktualisiert - membership_added: Mitgliedschaft hinzugefügt - membership_updated: Mitgliedschaft aktualisiert - title: E-Mail-Erinnerungen - pause: - label: Tägliche E-Mail-Erinnerungen vorübergehend pausieren - first_day: Erster Tag - last_day: Letzter Tag text_are_you_sure: Sind Sie sicher? text_are_you_sure_to_cancel: Sie haben ungespeicherte Änderungen auf dieser Seite. Sind Sie sicher, dass Sie diese verwerfen möchten? breadcrumb: Navigationspfad @@ -872,7 +790,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 @@ -1065,13 +983,6 @@ de: form_submit: title: Bestätigen, um fortzufahren text: Sind Sie sicher, dass Sie diese Aktion durchführen möchten? - destroy_work_package: - title: Löschen von %{label} bestätigen - 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. - deletes_children: Alle Unteraufgaben und deren Nachkommen werden auch rekursiv gelöscht. destroy_time_entry: title: Löschen der Zeitbuchung bestätigen text: Sind Sie sicher, dass Sie die folgende Zeitbuchung löschen möchten? diff --git a/config/locales/crowdin/js-el.yml b/config/locales/crowdin/js-el.yml index b0d35cfaa7a..47e4b8f2af0 100644 --- a/config/locales/crowdin/js-el.yml +++ b/config/locales/crowdin/js-el.yml @@ -515,12 +515,6 @@ el: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ el: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Ανάθεση σε - responsible: Υπόλογος - shared: Shared - watched: Παρατηρητής - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Βρίσκεστε στη μοναδική σελίδα. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ el: required_outside_context: 'Παρακαλούμε επιλέξτε ένα έργο μέσα στο οποίο θα δημιουργήσετε το πακέτο εργασίας για να δείτε όλα τα χαρακτηριστικά. Μπορείτε να επιλέξετε μόνο έργα που έχουν τον παραπάνω τύπο ενεργό. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Νέα προστέθηκαν - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Η σελίδα Wiki προστέθηκε - wiki_page_updated: Η σελίδα Wiki ενημερώθηκε - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Είστε σίγουρος/η; text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ el: form_submit: title: Επιβεβαιώστε για να συνεχίσετε text: Είστε βέβαιοι ότι θέλετε να προβείτε αυτήν την ενέργεια; - destroy_work_package: - title: Επιβεβαίωση διαγραφής του %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'Το πακέτο εργασίας έχει %{childUnits}:' - confirm_deletion_children: Αναγνωρίζω ότι ΌΛΟΙ οι απόγονοι από τα αναφερόμενα πακέτα εργασίας θα αφαιρεθούν αναδρομικά. - deletes_children: Όλα τα παιδιά πακέτα εργασίας και οι απόγονοι τους επίσης θα διαγραφούν αναδρομικά. destroy_time_entry: title: Επιβεβαίωση διαγραφής καταχώρησης χρόνου text: Είστε βέβαιοι ότι θέλετε να διαγράψετε την ακόλουθη καταχώρηση χρόνου diff --git a/config/locales/crowdin/js-eo.yml b/config/locales/crowdin/js-eo.yml index 0dbf981dd45..aa809a8f2b6 100644 --- a/config/locales/crowdin/js-eo.yml +++ b/config/locales/crowdin/js-eo.yml @@ -515,12 +515,6 @@ eo: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ eo: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Asignita al - responsible: Respondeculo - shared: Shared - watched: Atentanto - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ eo: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Novaĵoj aldonitaj - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ eo: form_submit: title: Konfirmi por daŭrigi text: Are you sure you want to perform this action? - destroy_work_package: - title: Konfirmu forigon de %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'La laborpakaĵo estis %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-es.yml b/config/locales/crowdin/js-es.yml index bdde5c5579f..06e82c44b0a 100644 --- a/config/locales/crowdin/js-es.yml +++ b/config/locales/crowdin/js-es.yml @@ -513,12 +513,6 @@ es: sidebar_arrow: Usa la flecha de retorno en la esquina superior izquierda para regresar al menú principal del proyecto. welcome: Realice un recorrido de introducción de tres minutos para conocer las funciones más importantes.
Le recomendamos que complete todos los pasos. Puede volver a iniciar el paseo en cualquier momento. wiki: En la wiki, puede elaborar documentos y compartir conocimientos con su equipo. - backlogs: - overview: Administre su trabajo en la vista de trabajos pendientes. - sprints: A la derecha verá los trabajos pendientes de productos y errores de código, y a la izquierda se muestran los sprints correspondientes. Aquí puede crear épicas, historias de usuario y errores de código, asignar prioridades mediante funciones de arrastrar y colocar, y añadir elementos a un sprint. - task_board_arrow: Para ver el tablero de tareas, abra el menú desplegable sprint… - task_board_select: "… y seleccione la entrada del tablero de tareas." - task_board: En el tablero de tareas, se muestra el progreso de este sprint. Haga clic en el icono del signo más (+) junto a una historia de usuario para añadir nuevas tareas o impedimentos.
Puede actualizar el estado mediante las funciones de arrastrar y colocar. boards: overview: Seleccione los tableros para cambiar la vista y gestione su proyecto con la vista de tableros Agile. lists_kanban: Aquí puede crear varias listas (columnas) en el tablero. Por ejemplo, esta función le permite crear un tablero Kanban. @@ -590,49 +584,6 @@ es: settings: change_notification_settings: Puede modificar su configuración de notificaciones para asegurarse que nunca se pierda una actualización importante. title: Ajustes de notificación - notify_me: Notifícame - reminders: - no_notification: Sin notificación - timeframes: - normal: - PT0S: mismo día - P1D: 1 día antes - P3D: 3 días antes - P7D: una semana antes - overdue: - P1D: todos los días - P3D: cada 3 días - P7D: todas las semanas - reasons: - mentioned: - title: Mencionado - description: Recibir una notificación cada vez que un usuario me mencione - assignee: Asignado a - responsible: Responsable - shared: Compartido - watched: Observador - work_package_commented: Todos los nuevos comentarios - work_package_created: Nuevos paquetes de trabajo - work_package_processed: Todos los cambios de estado - work_package_prioritized: Todos los cambios de prioridad - work_package_scheduled: Todos los cambios de fecha - global: - immediately: - title: Participado - description: Notificaciones para todas las actividades en paquetes de trabajo en los que usted está involucrado (asignado, responsable u observador). - delayed: - title: No participando - description: Notificaciones adicionales para actividades en todos los proyectos. - date_alerts: - title: Alertas de fecha - description: Notificaciones automáticas cuando se acercan fechas importantes para paquetes de trabajo abiertos en los que está involucrado (asignado, responsable u observador). - overdue: Cuando vencido - project_specific: - title: Configuración de notificaciones específicas de proyectos - description: Esta configuración específica de proyectos reemplaza los ajustes predeterminados anteriores. - add: Añadir configuración para el proyecto - already_selected: Este proyecto ya está seleccionado - remove: Eliminar configuración del proyecto pagination: no_other_page: Usted está en la única página. pages_skipped: Páginas omitidas. @@ -656,39 +607,6 @@ es: required_outside_context: 'Seleccione el proyecto donde quiere crear el paquete de trabajo para ver todos los atributos. Solo puede seleccionar proyectos que tengan activado el tipo de paquete utilizado. ' - reminders: - settings: - daily: - add_time: Añadir tiempo - enable: Habilitar recordatorios por correo electrónico diarios - explanation: Solo recibirá estos recordatorios para las notificaciones sin leer y en las horas que especifique. %{no_time_zone} - no_time_zone: Hasta que configure una zona horaria para su cuenta, las horas se interpretarán en UTC. - time_label: 'Tiempo %{counter}:' - title: Enviarme recordatorios por correo electrónico diarios con las notificaciones sin leer - workdays: - title: Recibir recordatorios por correo electrónico en estos días - immediate: - title: Enviarme un recordatorio por correo electrónico - mentioned: De inmediato cuando alguien me @mencione - personal_reminder: Inmediatamente al recibir un recordatorio personal - alerts: - title: Alertas por correo electrónico de otros elementos (que no sean paquetes de trabajo) - explanation: 'Las notificaciones por ahora están limitadas a los paquetes de trabajo. Puede elegir seguir recibiendo alertas por correo electrónico de estos eventos hasta que se incluyan en las notificaciones: - - ' - news_added: Noticias añadidas - news_commented: Comentarios en un elemento de noticias - document_added: Documentos añadidos - forum_messages: Nuevos mensajes en foro - wiki_page_added: Página wiki añadida - wiki_page_updated: Página wiki actualizada - membership_added: Subscripción añadida - membership_updated: Subscripción actualizada - title: Recordatorios por correo electrónico - pause: - label: Pausar temporalmente los recordatorios por correo electrónico diarios - first_day: Primer día - last_day: Último día text_are_you_sure: "¿Estás seguro?" text_are_you_sure_to_cancel: Tiene cambios sin guardar en esta página. ¿Seguro que quiere descartarlos? breadcrumb: Ruta de navegación @@ -1061,13 +979,6 @@ es: form_submit: title: Confirme para continuar text: "¿Seguro que desea realizar esta acción?" - destroy_work_package: - title: Confirmar la eliminación de %{label} - single_text: Está seguro que desea eliminar este paquete de trabajo - bulk_text: "¿Estás seguro de que quiere eliminar el siguiente %{label}?" - has_children: 'El paquete de trabajo tiene %{childUnits}:' - confirm_deletion_children: Reconozco que TODOS los descendientes de los paquetes de trabajo enumerados se eliminarán recursivamente. - deletes_children: También se eliminarán de forma recursiva todos los sub-paquetes de trabajo y sus descendientes. destroy_time_entry: title: Confirmar la eliminación de la entrada de tiempo text: "¿Realmente quiere eliminar la siguiente entrada de tiempo?" diff --git a/config/locales/crowdin/js-et.yml b/config/locales/crowdin/js-et.yml index 2c43842063e..1777a5cfcf6 100644 --- a/config/locales/crowdin/js-et.yml +++ b/config/locales/crowdin/js-et.yml @@ -515,12 +515,6 @@ et: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ et: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mainitud - description: Receive a notification every time someone mentions me anywhere - assignee: Määratud tegija - responsible: Accountable - shared: Jagatud - watched: Jälgija - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ et: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Uudis lisatud - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Viki leht on lisatud - wiki_page_updated: Viki leht on uuendatud - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Kas oled kindel? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ et: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-eu.yml b/config/locales/crowdin/js-eu.yml index d5a00cb840d..5849d78b37d 100644 --- a/config/locales/crowdin/js-eu.yml +++ b/config/locales/crowdin/js-eu.yml @@ -515,12 +515,6 @@ eu: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ eu: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ eu: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ eu: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-fa.yml b/config/locales/crowdin/js-fa.yml index ef7ba039e6f..36c4f820b8e 100644 --- a/config/locales/crowdin/js-fa.yml +++ b/config/locales/crowdin/js-fa.yml @@ -515,12 +515,6 @@ fa: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: کار خود را در نمای backlogs مدیریت کنید. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ fa: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: نماینده - responsible: Accountable - shared: Shared - watched: ناظر - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: اطلاعیه (اعلان) برای تمام فعالیت‌ها در پکیج‌های کاری که در آن‌ها شرکت دارید (به عنوان مسئول، متولی یا تماشاگر). - delayed: - title: Non-participating - description: اعلان‌های اضافی برای فعالیت‌ها در تمام پروژه‌ها - date_alerts: - title: Date alerts - description: اعلان‌های خودکار هنگام نزدیک شدن تاریخ‌های مهم برای پکیج‌های کاری باز که در آن‌ها شرکت دارید (به عنوان مسئول، متولی یا تماشاگر). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: این تنظیمات خاص پروژه تنظیمات پیش‌فرض بالا را بازنویسی می‌کنند. - add: Add setting for project - already_selected: این پروژه قبلاً انتخاب شده است - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ fa: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: آیا مطمئن هستید؟ text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ fa: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: تایید حذف %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'بسته کار دارای %{childUnits} است:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-fi.yml b/config/locales/crowdin/js-fi.yml index 053f8f05791..f2d8ae4680d 100644 --- a/config/locales/crowdin/js-fi.yml +++ b/config/locales/crowdin/js-fi.yml @@ -515,12 +515,6 @@ fi: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ fi: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Työn suorittaja - responsible: Vastuuhenkilö - shared: Shared - watched: Seuraajat - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Olet jo ainoalla sivulla. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ fi: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Uutinen lisätty - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki-sivu lisätty - wiki_page_updated: Wiki-sivu päivitetty - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Oletko varma? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ fi: form_submit: title: Vahvista jatkaaksesi text: Oletko varma, että haluat suorittaa tämän toiminnon? - destroy_work_package: - title: 'Vahvista poisto: %{label}' - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Vahvista text: Haluatko varmasti poistaa tämän aikakirjauksen? diff --git a/config/locales/crowdin/js-fil.yml b/config/locales/crowdin/js-fil.yml index f14b44cfc35..cdce47f357a 100644 --- a/config/locales/crowdin/js-fil.yml +++ b/config/locales/crowdin/js-fil.yml @@ -515,12 +515,6 @@ fil: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ fil: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Naitalaga - responsible: Accountable - shared: Shared - watched: Tagapagmasid - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Ikaw ay nasa pahina lamang. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ fil: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Bagong idinagdag - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Ang idinagdag na wiking pahina - wiki_page_updated: Naka-update na wiking pahina - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Sigurado ka ba? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ fil: form_submit: title: Kumpirmahin upang magpatuloy text: Sigurado ka bang na gusto mong magtanghal ng aksyon ito? - destroy_work_package: - title: Kumpirmahin ang pagbubura ng %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'Ang work package ay mayroong %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-fr.yml b/config/locales/crowdin/js-fr.yml index 28213a57b9c..fe6129b1443 100644 --- a/config/locales/crowdin/js-fr.yml +++ b/config/locales/crowdin/js-fr.yml @@ -515,12 +515,6 @@ fr: sidebar_arrow: Utilisez la flèche de retour dans le coin supérieur gauche pour retourner au menu principal du projet. welcome: Faites une visite d’introduction de trois minutes pour apprendre les fonctionnalités les plus importantes.
Il est recommandé d’effectuer les étapes jusqu'à la fin. Vous pouvez redémarrer cette visite à tout moment. wiki: Dans le wiki vous pouvez documenter et partager vos connaissances avec votre équipe. - backlogs: - overview: Gérez votre travail dans la vue backlogs. - sprints: Sur la droite, vous avez le backlog de produit et le backlog des bugs. Sur la gauche, vous avez leurs sprints respectifs. Ici, vous pouvez créer des epics, user stories et bugs, les prioriser via glisser-déposer et les ajouter à un sprint. - task_board_arrow: Pour voir votre Tableau de bord des Tâches, ouvrez le menu déroulant Sprint... - task_board_select: "…et sélectionnez l'entrée Tableau de bord des Tâches." - task_board: Le tableau des tâches visualise la progression pour ce sprint. Cliquez sur l'icône plus (+) à côté d'une user story pour ajouter de nouvelles tâches ou de nouveaux obstacles.
Le statut peut être mis à jour par glisser-déposer. boards: overview: Sélectionnez tableaux pour déplacer la vue et gérer votre projet en utilisant la vue des tableaux agiles. lists_kanban: Ici, vous pouvez créer plusieurs listes (colonnes) dans votre tableau. Cette fonctionnalité vous permet de créer un tableau Kanban, par exemple. @@ -592,49 +586,6 @@ fr: settings: change_notification_settings: Vous pouvez modifier vos paramètres de notification pour vous assurer de ne jamais manquer une mise à jour importante. title: Paramètres de notifications - notify_me: M'alerter - reminders: - no_notification: Aucune notification - timeframes: - normal: - PT0S: le jour même - P1D: 1 jour avant - P3D: 3 jours avant - P7D: une semaine avant - overdue: - P1D: tous les jours - P3D: tous les 3 jours - P7D: toutes les semaines - reasons: - mentioned: - title: Mentionné - description: Recevoir une notification chaque fois que quelqu'un me mentionne n'importe où - assignee: Assigné à - responsible: Responsable - shared: Partagé - watched: Observateur - work_package_commented: Tous les nouveaux commentaires - work_package_created: Nouveaux lots de travaux - work_package_processed: Tous les changements de statut - work_package_prioritized: Tous les changements de priorité - work_package_scheduled: Tous les changements de date - global: - immediately: - title: Participant - description: Notifications pour toute activité sur les lots de travaux vous concernant (assigné, responsable ou observateur). - delayed: - title: Non participant - description: Notifications additionnelles lors d'activités sur tous les projets. - date_alerts: - title: Alertes de date - description: Notifications automatiques lorsque des dates importantes approchent pour les lots de travaux ouverts vous concernant (assigné, responsable ou observateur). - overdue: En cas de retard - project_specific: - title: Paramètres de notification spécifiques au projet - description: Ces paramètres spécifiques au projet remplacent les paramètres par défaut ci-dessus. - add: Ajouter un paramètre pour le projet - already_selected: Ce projet est déjà sélectionné - remove: Suppression des paramètres de projet pagination: no_other_page: Vous êtes sur la seule page. pages_skipped: Pages ignorées. @@ -658,39 +609,6 @@ fr: required_outside_context: 'Veuillez choisir un projet pour créer le lot de travaux et voir tous les attributs. Vous pouvez seulement sélectionner des projets ayant le type ci-dessus activé. ' - reminders: - settings: - daily: - add_time: Ajouter une heure - enable: Activer les rappels quotidiens par e-mail - explanation: Vous ne recevrez ces rappels que pour les notifications non lues et seulement aux heures que vous spécifiez. %{no_time_zone} - no_time_zone: Jusqu'à ce que vous configurez un fuseau horaire pour votre compte, les temps seront interprétés en UTC. - time_label: 'Temps %{counter} :' - title: Envoyez-moi des rappels quotidiens par e-mail pour les notifications non lues - workdays: - title: Recevoir des rappels par e-mail ces jours - immediate: - title: Envoyez-moi un rappel par e-mail - mentioned: Immédiatement quand quelqu'un me @mentionne - personal_reminder: Immédiatement lorsque je reçois un rappel personnel - alerts: - title: Alertes par e-mail pour les autres éléments (qui ne sont pas des lots de travaux) - explanation: 'Les notifications d''aujourd''hui sont limitées aux lots de travaux. Vous pouvez choisir de continuer à recevoir des alertes par e-mail pour ces événements jusqu''à ce qu''elles soient incluses dans les notifications: - - ' - news_added: Actualités ajoutées - news_commented: Commenter sur une actualité - document_added: Documents ajoutés - forum_messages: Nouveaux messages du forum - wiki_page_added: Page wiki ajoutée - wiki_page_updated: Page wiki mise à jour - membership_added: Adhésion ajoutée - membership_updated: Adhésion mise à jour - title: Rappels par e-mail - pause: - label: Mettre temporairement en pause les rappels quotidiens par e-mail - first_day: Premier jour - last_day: Dernier jour text_are_you_sure: Êtes-vous sûr ? text_are_you_sure_to_cancel: Vous avez des modifications non enregistrées sur cette page. Voulez-vous vraiment les supprimer ? breadcrumb: Fil d'Ariane @@ -1063,13 +981,6 @@ fr: form_submit: title: Confirmez pour continuer text: Êtes-vous sûr de vouloir effectuer cette action ? - destroy_work_package: - title: Confirmer la suppression de %{label} - single_text: Voulez-vous vraiment supprimer le lot de travaux - bulk_text: Voulez-vous vraiment supprimer le %{label} suivant ? - has_children: 'Le lot de travaux a %{childUnits} :' - confirm_deletion_children: Je reconnais que TOUS les descendants des lots de travaux de la liste seront supprimés récursivement. - deletes_children: Tous les lots de travaux enfants et leurs descendants seront également supprimés récursivement. destroy_time_entry: title: Confirmer la suppression de l’entrée de temps text: Êtes-vous sur de vouloir supprimer l'entrée de temps suivante ? diff --git a/config/locales/crowdin/js-he.yml b/config/locales/crowdin/js-he.yml index 71b534d073a..dd7cbcb4097 100644 --- a/config/locales/crowdin/js-he.yml +++ b/config/locales/crowdin/js-he.yml @@ -515,12 +515,6 @@ he: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -594,49 +588,6 @@ he: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: משויך אל - responsible: Accountable - shared: Shared - watched: צופה - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -660,39 +611,6 @@ he: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: האם הינך בטוח? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1065,13 +983,6 @@ he: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-hi.yml b/config/locales/crowdin/js-hi.yml index c27c925e6e5..642a717e2db 100644 --- a/config/locales/crowdin/js-hi.yml +++ b/config/locales/crowdin/js-hi.yml @@ -515,12 +515,6 @@ hi: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ hi: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: अनुदिष्ट - responsible: जवाबदेह - shared: Shared - watched: वॉचर - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ hi: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: विकी पृष्ठ अद्यतित - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ hi: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-hr.yml b/config/locales/crowdin/js-hr.yml index ac3ad1133a1..67c0035186c 100644 --- a/config/locales/crowdin/js-hr.yml +++ b/config/locales/crowdin/js-hr.yml @@ -515,12 +515,6 @@ hr: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -593,49 +587,6 @@ hr: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Opunomoćeno - responsible: Accountable - shared: Shared - watched: Nadglednik - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -659,39 +610,6 @@ hr: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Vijest dodana - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki stranica dodana - wiki_page_updated: Wiki stranica ažurirana - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Da li ste sigurni? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1064,13 +982,6 @@ hr: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-hu.yml b/config/locales/crowdin/js-hu.yml index 628eef119fd..3e630f2f4ce 100644 --- a/config/locales/crowdin/js-hu.yml +++ b/config/locales/crowdin/js-hu.yml @@ -527,16 +527,6 @@ hu: sidebar_arrow: Használja a vissza nyilat bal felül hogy visszatérjen a projektekhez main menu.. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: A Wiki modulban tudását dokumentálhatja és megoszthatja a csapatával. - backlogs: - overview: Munka kezelése a backlogs nézetben - sprints: 'Jobbra a termék- és a hibatár, balra pedig a megfelelő sprintek. Itt létrehozhatsz képeket, felhasználói történeteket és hibákat, drag & drop segítségével priorizálhatsz, és hozzáadhatod őket egy sprinthez. - - ' - task_board_arrow: A Feladatlap megjelenítéséhez nyissa meg a Sprint legördülő listát... - task_board_select: "... és válassza ki a Feladatlap bejegyzést." - task_board: 'A feladattábla megjeleníti a sprint haladását . Új feladatok vagy akadályok hozzáadásához kattintson a felhasználói történet melletti plusz (+) ikonra.
Az állapot húzással frissíthető. - - ' boards: overview: 'Válassza a táblák lehetőséget a nézetváltáshoz, és kezelje projektjét az agilis táblák nézetben. @@ -626,49 +616,6 @@ hu: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Értesítési beállítások - notify_me: Értesítést kérek - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Megemlített - description: Értesítés fogadása minden alkalommal, amikor bárki bárhol megemlít - assignee: Megbízott - responsible: Felelős - shared: Megosztva - watched: Megfigyelő - work_package_commented: Új megjegyzések - work_package_created: Új munkacsomagok - work_package_processed: Összes státusz változás - work_package_prioritized: Összes prioritás változás - work_package_scheduled: Összes dátum változás - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Projekt-specifikus értesítések beállításai - description: These project-specific settings override default settings above. - add: Beállítás hozzáadása a projekthez - already_selected: A projekt már ki lett választva - remove: Projekt beállítások eltávolítása pagination: no_other_page: Csak egyetlen oldalon tartózkodik. pages_skipped: Pages skipped. @@ -692,47 +639,6 @@ hu: required_outside_context: 'Kérjük, válasszon egy projektet a munkacsomag létrehozásához az összes attribútum megtekintéséhez. Csak olyan projekteket választhat ki, amelyek aktiválták a fenti típust. ' - reminders: - settings: - daily: - add_time: 'Idő hozzáadása - - ' - enable: 'Napi e-mail emlékeztetők engedélyezése - - ' - explanation: 'Ezeket az emlékeztetőket csak az olvasatlan értesítésekről kapja, és csak az Ön által megadott órákban. %{no_time_zone} - - ' - no_time_zone: Amíg a fiókjához nincs időzóna beállítva, addig az időpontok UTC szerint értendők. - time_label: 'Idő%{counter}:' - title: 'Napi e-mail emlékeztető küldése az olvasatlan értesítésekről - - ' - workdays: - title: Email emlékeztetők fogadása ezeken a napokon - immediate: - title: Kérek email emlékeztetőt! - mentioned: Amint valaki @megemlít engem - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Hírek hozzáadva - news_commented: Megjegyzés egy hír elemhez - document_added: Dokumentum hozzáadva - forum_messages: Új fórum üzenetek - wiki_page_added: Wiki oldal hozzáadva - wiki_page_updated: Wiki oldal frissítve - membership_added: Felhasználó hozzáadva - membership_updated: Felhasználó frissítve - title: E-mail emlékeztetők - pause: - label: Napi email emlékeztetők ideiglenes szüneteltetése - first_day: Első nap - last_day: Utolsó nap text_are_you_sure: Biztos vagy benne? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1113,15 +1019,6 @@ hu: form_submit: title: Megerősítés a folytatáshoz text: Biztosan végre szeretné hajtani ezt a műveletet? - destroy_work_package: - title: Jóváhagyja %{label} törlését - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'A feladatcsoportnak van %{childUnits}:' - confirm_deletion_children: 'Tudomásul veszem, hogy a felsorolt munkacsomagok MINDEN leszármazottja rekurzív módon eltávolításra kerül. - - ' - deletes_children: Minden al feladatcsoportot és leszármazottait is rekurzívan törli. destroy_time_entry: title: Időbejegyzés törlésének jóváhagyása text: Biztosan törölni akarja a következő időbejegyzést? diff --git a/config/locales/crowdin/js-id.yml b/config/locales/crowdin/js-id.yml index a20613bbfef..292ae98be47 100644 --- a/config/locales/crowdin/js-id.yml +++ b/config/locales/crowdin/js-id.yml @@ -515,12 +515,6 @@ id: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -591,49 +585,6 @@ id: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Pelimpahan - responsible: Akuntabel - shared: Shared - watched: Pemantau - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -657,39 +608,6 @@ id: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News ditambahkan - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Halaman Wiki ditambahkan - wiki_page_updated: Halaman Wiki diupdate - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Apakah anda yakin? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1062,13 +980,6 @@ id: form_submit: title: Konfirmasi untuk melanjutkan text: Apakah Anda yakin untuk melakukan tindakan ini? - destroy_work_package: - title: Mengkonfirmasi penghapusan %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'Paket kerja memiliki %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Apakah Anda ingin menghapus entri waktu berikut? diff --git a/config/locales/crowdin/js-it.yml b/config/locales/crowdin/js-it.yml index a976edd5da5..510689b4e83 100644 --- a/config/locales/crowdin/js-it.yml +++ b/config/locales/crowdin/js-it.yml @@ -515,12 +515,6 @@ it: sidebar_arrow: Utilizza la freccia di ritorno nell'angolo in alto a sinistra per tornare al menu principale del progetto. welcome: Fai un tour introduttivo di tre minuti per apprendere le funzioni importanti.
Consigliamo di completare i passaggi fino alla fine. Puoi riavviare il tour in qualsiasi momento. wiki: Nella wiki puoi documentare e condividere la conoscenza con il tuo team. - backlogs: - overview: Gestisci il tuo lavoro nella vista backlog. - sprints: A destra hai il product backlog e il bug backlog, a sinistra i rispettivi sprint. Qui puoi creare epics, storie utente e bug, assegnare priorità tramite trascinamento e aggiungerli a uno sprint. - task_board_arrow: Per vedere il tuo pannello delle attività, apri il menu a cascata Sprint... - task_board_select: "...e seleziona la voce pannello delle attività." - task_board: Il pannello delle attività visualizza l'avanzamento dello sprint. Fai clic sull'icona più (+) accanto a una storia utente per aggiungere nuove attività o impedimenti.
Lo stato può essere aggiornato trascinandolo. boards: overview: Seleziona board per cambiare visualizzazione e gestire il tuo progetto utilizzando la visualizzazione agile delle schede. lists_kanban: Qui puoi creare più liste (colonne) all'interno della tua board. Questa funzione ti consente di creare una board Kanban, ad esempio. @@ -592,49 +586,6 @@ it: settings: change_notification_settings: Puoi modificare le tue impostazioni di notifica per assicurarti di non perderti mai un aggiornamento importante. title: Impostazioni notifiche - notify_me: Avvisami - reminders: - no_notification: Nessuna notifica - timeframes: - normal: - PT0S: stesso giorno - P1D: 1 giorno prima - P3D: 3 giorni prima - P7D: una settimana prima - overdue: - P1D: ogni giorno - P3D: ogni 3 giorni - P7D: ogni settimana - reasons: - mentioned: - title: Menzionato - description: Ricevi una notifica ogni volta che qualcuno ti menziona ovunque - assignee: Assegnatario - responsible: Responsabile - shared: Condiviso - watched: Osservatore - work_package_commented: Tutti i nuovi commenti - work_package_created: Nuove macro-attività - work_package_processed: Tutte le modifiche di stato - work_package_prioritized: Tutte le modifiche prioritarie - work_package_scheduled: Tutte le modifiche della data - global: - immediately: - title: Partecipazione - description: Notifiche per tutte le attività nelle macro-attività in cui sei coinvolto (assegnatario, responsabile od osservatore) - delayed: - title: Non partecipante - description: Notifiche aggiuntive sulle attività in tutti i progetti - date_alerts: - title: Avvisi data - description: Notifiche automatiche quando si avvicinano date importanti per le macro-attività aperte in cui sei coinvolto (assegnatario, responsabile od osservatore) - overdue: Alla scadenza - project_specific: - title: Impostazioni di notifica specifiche del progetto - description: Queste impostazioni specifiche del progetto sostituiscono le impostazioni predefinite sopra. - add: Aggiungi impostazione per il progetto - already_selected: Questo progetto è già selezionato - remove: Rimuovi impostazioni progetto pagination: no_other_page: Sei nella pagina unica. pages_skipped: Pagine saltate. @@ -658,39 +609,6 @@ it: required_outside_context: 'Scegli un progetto per creare una macro-attività e vederne tutti gli attributi. Puoi selezionare solo progetti che hanno il tipo sopra indicato attivato. ' - reminders: - settings: - daily: - add_time: Aggiungi tempo - enable: Abilita promemoria email giornalieri - explanation: Riceverai questi promemoria solo per le notifiche non lette e solo nelle ore specificate. %{no_time_zone} - no_time_zone: Fino a quando non configuri un fuso orario per il tuo account, gli orari verranno interpretati in UTC. - time_label: 'Tempo %{counter}:' - title: Inviami promemoria email giornalieri per le notifiche non lette - workdays: - title: Ricevi promemoria email in questi giorni - immediate: - title: Inviami un promemoria email - mentioned: Subito quando qualcuno mi @menziona - personal_reminder: Immediatamente quando ricevo un promemoria personale - alerts: - title: Avvisi email per altri elementi (che non sono macro-attività) - explanation: 'Le notifiche oggi sono limitate ai pacchetti di lavoro. Puoi scegliere di continuare a ricevere avvisi email per questi eventi finché non vengono inclusi nelle notifiche: - - ' - news_added: Notizie aggiunte - news_commented: Commento su un articolo di notizie - document_added: Documenti aggiunti - forum_messages: Nuovi messaggi del forum - wiki_page_added: Pagina wiki aggiunta - wiki_page_updated: Pagina wiki aggiornata - membership_added: Iscrizione aggiunta - membership_updated: Iscrizione aggiornata - title: Promemoria email - pause: - label: Sospendi temporaneamente i promemoria email giornalieri - first_day: 'Primo giorno ' - last_day: Ultimo giorno text_are_you_sure: Sei sicuro/a? text_are_you_sure_to_cancel: Ci sono modifiche non salvate in questa pagina. Vuoi davvero eliminarle? breadcrumb: Percorso di navigazione @@ -1063,13 +981,6 @@ it: form_submit: title: Conferma per continuare text: Eseguire questa azione? - destroy_work_package: - title: Conferma l'eliminazione di %{label} - single_text: Vuoi davvero eliminare questa macro-attività? - bulk_text: Eliminare il seguente %{label}? - has_children: 'Questa macro-attività ha %{childUnits}:' - confirm_deletion_children: Prendo atto che TUTTI i discendenti delle macro-attività elencate verranno eliminati in modo ricorsivo. - deletes_children: Tutte le macro-attività ed i relativi discendenti verranno eliminati ricorsivamente. destroy_time_entry: title: Conferma l'eliminazione della voce temporale text: Eliminare la seguente voce temporale? diff --git a/config/locales/crowdin/js-ja.yml b/config/locales/crowdin/js-ja.yml index a28fa088b5a..e92442dacf1 100644 --- a/config/locales/crowdin/js-ja.yml +++ b/config/locales/crowdin/js-ja.yml @@ -34,12 +34,12 @@ ja: draggable_hint: | 埋め込み画像または添付ファイルをエディタにドラッグします。 ドラッグしつづけると閉じているエディタ領域が開きます。 - quarantined_hint: ウイルスが発見されたように、ファイルは隔離されています。ダウンロードできません。 + quarantined_hint: ウイルスが発見されたため,ファイルは隔離されています。ダウンロードできません。 autocomplete_ng_select: - add_tag: アイテムを追加 + add_tag: 項目を追加 clear_all: すべてクリア loading: 読み込み中... - not_found: アイテムが見つかりません + not_found: 見つかりませんでした type_to_search: 検索キーワードを入力 autocomplete_select: placeholder: @@ -71,7 +71,7 @@ ja: button_back_to_list_view: リスト表示に戻る button_cancel: キャンセル button_close: 閉じる - button_change_project: 別のプロジェクトに移動 + button_change_project: 他のプロジェクトに移る button_check_all: 全てを選択 button_configure-form: フォームを設定 button_confirm: 確認 @@ -79,7 +79,7 @@ ja: button_copy: コピー button_copy_to_clipboard: クリップボードにコピー button_copy_link_to_clipboard: クリップボードにリンクをコピー - button_copy_to_other_project: 別のプロジェクトで複製 + button_copy_to_other_project: 別のプロジェクトで複製する button_custom-fields: カスタムフィールド button_delete: 削除 button_delete_watcher: ウォッチャーを削除 @@ -101,7 +101,7 @@ ja: button_open_fullscreen: 全画面表示を開く button_show_cards: カードビュー表示 button_show_list: リストビュー表示 - button_show_table: テーブルビューを表示 + button_show_table: テーブル表示 button_show_gantt: ガントビューを表示 button_show_fullscreen: 全画面表示 button_more_actions: その他の操作 @@ -111,7 +111,7 @@ ja: button_uncheck_all: 全てを選択解除 button_update: 更新 button_export-atom: Atomをダウンロード - button_generate_pdf: PDFを生成 + button_generate_pdf: PDF作成 button_create: 作成 card: add_new: 新規カード追加 @@ -145,8 +145,8 @@ ja: description_select_work_package: 'ワークパッケージを選択 #%{id}' description_subwork_package: 'ワークパッケージの子 #%{id}' editor: - revisions: ローカルの変更を表示 - no_revisions: ローカルの変更は見つかりませんでした + revisions: ローカルの修正を表示 + no_revisions: ローカルでの修正は見つからず preview: プレビューモードの切り替え source_code: Markdown ソースモードの切り替え error_saving_failed: '次のエラーで文書を保存するのに失敗しました: %{error}' @@ -159,7 +159,7 @@ ja: attribute_reference: macro_help_tooltip: このテキストセグメントはマクロによって動的にレンダリングされています。 not_found: 要求されたリソースが見つかりませんでした - nested_macro: このマクロは %{model} %{id} を再帰的に参照しています。 + nested_macro: このマクロは %{model} %{id}を再帰的に参照している。 invalid_attribute: 選択した属性 '%{name}' は存在しません。 child_pages: button: 子ページへのリンク @@ -216,10 +216,10 @@ ja: calendar: empty_state_header: 休業日 empty_state_description: 休業日が定義されていません。「休業日を追加」ボタンをクリックして日付を追加してください。 - new_date: "(新規)" + new_date: "(新)" add_non_working_day: 休業日を追加 - already_added_error: この日付の非作業日はすでに存在します。それぞれの日付に1つの非作業日が作成されます。 - change_button: 保存してスケジュールを変更 + already_added_error: この日付の非営業日はすでに存在します。一意の日付に対して作成できる非営業日は1つだけです。 + change_button: 保存して再スケジュール change_title: 営業日を変更する removed_title: 以下の日を非稼働日リストから削除します: change_description: 営業日とみなす曜日を変更すると、このサイト内のすべてのプロジェクトのすべてのワークパッケージの開始日と終了日に影響を与える可能性があります。 @@ -301,14 +301,14 @@ ja: ical_sharing_modal: title: カレンダーを購読する inital_setup_error_message: データ取得中にエラーが発生しました。 - description: URL(iCalendar)を使って外部クライアントでこのカレンダーを購読し、そこから最新のワークパッケージ情報を見ることができます。 - warning: このURLを他のユーザーと共有しないでください。このリンクがあれば、誰でもアカウントやパスワードなしでワークパッケージの詳細を見ることができます。 - token_name_label: どこで使うのですか? + description: URL(iCalendar)を使用して、外部クライアントでこのカレンダーを購読し、そこから最新のワークパッケージ情報を表示することができます。 + warning: このURLを他のユーザーと共有しないでください。このリンクを持つ誰でもアカウントやパスワードなしでワークパッケージの詳細を表示することができます。 + token_name_label: どこで使うのですか?? token_name_placeholder: 名前を入力してください。例:"電話" token_name_description_text: If you subscribe to this calendar from multiple devices, this name will help you distinguish between them in your access tokens list. copy_url_label: URLをコピー - ical_generation_error_text: カレンダー URL の生成中にエラーが発生しました。 - success_message: URL "%{name}" は正常にクリップボードにコピーされました。サブスクリプションを完了するためにカレンダークライアントに貼り付けてください。 + ical_generation_error_text: カレンダーのURL生成時にエラーが発生しました。 + success_message: URL "%{name}" がクリップボードにコピーされました。カレンダークライアントに貼り付けて購読を完了してください。 label_activate: 有効にする label_assignee: 担当者 label_assignee_alt_text: This work package is assigned to %{name} @@ -321,7 +321,7 @@ ja: label_add_row_before: 前に行を追加 label_add_selected_columns: 選択した列を追加 label_added_by: 追加した人 - label_added_time_by: %{author} が %{age} に追加しました + label_added_time_by: 追加 %{author} %{age} label_ago: "○日前" label_all: 全て label_all_projects: すべてのプロジェクト @@ -432,7 +432,7 @@ ja: label_repository_plural: リポジトリ label_resize_project_menu: Resize project menu label_save_as: 名前をつけて保存 - label_search_columns: 列を検索 + label_search_columns: 列を検索する label_select_watcher: ウォッチャーを選択... label_selected_filter_list: 選択されたフィルタ label_show_attributes: すべての属性を表示 @@ -470,8 +470,8 @@ ja: label_watch_work_package: ワークパッケージをウォッチ label_watcher_added_successfully: ウォッチャーが正常に追加されました ! label_watcher_deleted_successfully: ウォッチャーが正常に削除されました ! - label_work_package_details_you_are_here: あなたは %{tab} %{type} %{subject} のタブにいます。 - label_work_package_context_menu: ワークパッケージのコンテキスト メニュー + label_work_package_details_you_are_here: あなたは %{type} %{subject}の %{tab} タブを表示しています。 + label_work_package_context_menu: ワークパッケージのコンテキストメニュー label_unwatch: ウォッチしない label_unwatch_work_package: ワークパッケージのウォッチを削除 label_uploaded_by: アップロードした人 @@ -502,7 +502,7 @@ ja: label_version_plural: バージョン label_view_has_changed: このビューには未保存の変更があります。 クリックすると保存します。 help_texts: - show_modal: ヘルプテキストを表示 + show_modal: ヘルプテキストを表示する onboarding: buttons: skip: スキップ @@ -510,17 +510,11 @@ ja: got_it: 了承 steps: help_menu: ヘルプ(?)メニューは、その他のヘルプリソースを提供します。ここでは、ユーザーガイド、役立つハウツービデオなどを見つけることができます。
OpenProjectでの作業をお楽しみください! - members: 新しい メンバー をプロジェクトに招待します。 + members: 新しいメンバーをプロジェクトに招待する。 quick_add_button: ヘッダーナビゲーションにあるプラス(+)アイコンをクリックして、新規プロジェクトを作成したり、同僚を招待したりできます。 sidebar_arrow: プロジェクトのメインメニューに戻るには、左上の矢印を使います。 welcome: 3分間のイントロダクションツアーで、最も重要な機能を学びましょう。
最後までステップを完了することをお勧めします。ツアーはいつでも再開できます。 wiki: "Wiki内では、チームと一緒に知識を文書化し、共有することができます。" - backlogs: - overview: "バックログビューで仕事を管理する。" - sprints: 右側にはプロダクトバックログとバグバックログがあり、左側にはそれぞれのスプリントがあります。ここでエピック、ユーザーストーリー、バグを作成し、ドラッグ&ドロップで優先順位をつけ、スプリントに追加することができます。 - task_board_arrow: "タスクボードを表示するには、スプリントドロップダウンを開きます…" - task_board_select: "...そして、 タスク ボード エントリを選択します。" - task_board: タスクボードは、このスプリントの進捗を視覚化します。ユーザーストーリーの横にあるプラス(+)アイコンをクリックすると、新しいタスクや障害を追加できます。
ステータスはドラッグ&ドロップで更新できます。 boards: overview: "ボードを選択してビューをシフトし、アジャイルボードビューを使ってプロジェクトを管理する。" lists_kanban: ここでは、ボード内に複数のリスト(列)を作成することができます。この機能により、例えば、カンバンボードを作成することができます。 @@ -591,57 +585,14 @@ ja: settings: change_notification_settings: 通知設定を変更することで、重要なアップデートを見逃すことはありません。 title: 通知設定 - notify_me: 通知する - reminders: - no_notification: 通知なし - timeframes: - normal: - PT0S: 同じ日 - P1D: 1 日前 - P3D: 3 日前 - P7D: 週間前 - overdue: - P1D: 毎日 - P3D: 3日ごと - P7D: 毎週 - reasons: - mentioned: - title: メンションされた - description: メンションされたときに通知を受け取る - assignee: 担当者 - responsible: 責任者 - shared: 共有 - watched: ウォッチャー - work_package_commented: すべての新着コメント - work_package_created: 新しいワークパッケージ - work_package_processed: すべてのステータス変更 - work_package_prioritized: すべての優先度の変更 - work_package_scheduled: すべての日付の変更 - global: - immediately: - title: 参加 - description: 自分が関与しているワークパッケージのすべてのアクティビティに関する通知(アサイニー、アカウンタブル、ウォッチャー)。 - delayed: - title: 不参加 - description: すべてのプロジェクトでのアクティビティの追加通知。 - date_alerts: - title: 日付アラート - description: あなたが関与している(アサイニー、アカウンタブル、ウォッチャー)オープンワークパッケージの重要な日付が近づくと自動通知。 - overdue: 期限を過ぎた場合 - project_specific: - title: プロジェクト固有の通知設定 - description: これらのプロジェクト固有の設定は、上記のデフォルト設定を上書きする。 - add: プロジェクトの設定を追加する - already_selected: このプロジェクトは既に選択されています - remove: プロジェクトの設定を削除する pagination: no_other_page: このページだけです。 - pages_skipped: ページがスキップされました。 + pages_skipped: ページスキップ。 page_navigation: ページネーション・ナビゲーション per_page_navigation: ページ毎のアイテム選択 pages: page_number: ページ %{number} - show_per_page: ページあたり %{number} を表示 + show_per_page: ページごとに %{number} placeholders: default: "-" subject: ここにタイトルを入力します @@ -651,45 +602,12 @@ ja: project: autocompleter: label: プロジェクト名の入力補完 - click_to_switch_to_project: 'プロジェクト: %{projectname}' + click_to_switch_to_project: プロジェクト: %{projectname} context: プロジェクトのコンテキスト not_available: プロジェクトなし required_outside_context: 'ワークパッケージを作成するプロジェクトを選択して、すべての属性を確認してください。 上記で有効になっているタイプのプロジェクトのみ選択できます。 ' - reminders: - settings: - daily: - add_time: 時間を追加 - enable: 毎日のEメールリマインダーを有効にする - explanation: このリマインダーは、未読の通知に対してのみ、指定した時間帯にのみ届きます。 %{no_time_zone} - no_time_zone: アカウントにタイムゾーンを設定するまでは、時間はUTCで解釈されます。 - time_label: 時間 %{counter}: - title: 未読の通知を毎日メールで通知する - workdays: - title: これらの日にリマインダーメールを受け取る - immediate: - title: 電子メールのリマインダーを送信 - mentioned: "@mentionするとすぐに" - personal_reminder: 個人的なリマインダーを受け取ったら直ちに - alerts: - title: その他の項目(ワークパッケージではないもの)に対する電子メールアラート - explanation: '本日の通知はワークパッケージに限定されています。これらのイベントが通知に含まれるようになるまで、Eメールアラートを受信し続けることを選択できます: - - ' - news_added: ニュースが追加されました。 - news_commented: ニュースへのコメント - document_added: 追加された書類 - forum_messages: 新しいフォーラムメッセージ - wiki_page_added: Wikiページが追加されました。 - wiki_page_updated: Wikiページが更新されました。 - membership_added: メンバーシップが追加されました - membership_updated: メンバーシップ更新 - title: 電子メールによるリマインダー - pause: - label: 毎日のEメールリマインダーを一時停止する - first_day: 初日 - last_day: 最終日 text_are_you_sure: よろしいですか? text_are_you_sure_to_cancel: このページには未保存の変更があります。本当に破棄しますか? breadcrumb: パンくず @@ -1062,13 +980,6 @@ ja: form_submit: title: 続行の確認 text: この操作を実行してもよろしいですか? - destroy_work_package: - title: "%{label} の削除を確認" - single_text: ワークパッケージを削除してもよろしいですか? - bulk_text: "%{label}を削除してもよろしいですか?" - has_children: ワークパッケージは %{childUnits} あります。 - confirm_deletion_children: 列挙されたワークパッケージのすべての子孫が再帰的に削除されることを承認します。 - deletes_children: すべての子ワークパッケージとその子孫も再帰的に削除されます。 destroy_time_entry: title: タイムエントリの削除を確認 text: 次のタイムエントリを削除してもよろしいですか? @@ -1176,7 +1087,7 @@ ja: toggle_title: ベースライン clear: クリア apply: 適用 - header_description: 過去のいずれかの時点からこのリストに加えられた変更を強調する。 + header_description: 過去の選択した時点からこのリストに加えられた変更をハイライト show_changes_since: 以降の変更を表示する help_description: ベースラインの基準タイムゾーン。 time_description: '現地時間: %{datetime}' diff --git a/config/locales/crowdin/js-ka.yml b/config/locales/crowdin/js-ka.yml index c0e2885ad6d..5d5a8338b65 100644 --- a/config/locales/crowdin/js-ka.yml +++ b/config/locales/crowdin/js-ka.yml @@ -515,12 +515,6 @@ ka: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ ka: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: შემატყობინე - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: იგივე დღე - P1D: 1 დღით ადრე - P3D: 3 დღით ადრე - P7D: 1 კვირით ადრე - overdue: - P1D: ყოველდღე - P3D: ყოველ 3 დღეში - P7D: ყოველკვირა - reasons: - mentioned: - title: მოხსენიებულები - description: Receive a notification every time someone mentions me anywhere - assignee: დამსაქმებელი - responsible: ანგარიშვალდებული - shared: გაზიარებული - watched: მეთვალყურე - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: მონაწილეობა - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: განგაშების თარიღი - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: როცა ვადა გადაცილდება - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ ka: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: დროის დამატება - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: სიახლე დამატებულია - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: პირველი დღე - last_day: ბოლო დღე text_are_you_sure: დარწმუნებული ბრძანდებით? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ ka: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-kk.yml b/config/locales/crowdin/js-kk.yml index 67ca4f89984..35baaecdaa8 100644 --- a/config/locales/crowdin/js-kk.yml +++ b/config/locales/crowdin/js-kk.yml @@ -515,12 +515,6 @@ kk: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ kk: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ kk: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ kk: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-ko.yml b/config/locales/crowdin/js-ko.yml index ed4186c89c7..f7c1dfe2074 100644 --- a/config/locales/crowdin/js-ko.yml +++ b/config/locales/crowdin/js-ko.yml @@ -515,12 +515,6 @@ ko: sidebar_arrow: 프로젝트의 기본 메뉴로 돌아가려면 왼쪽 상단의 뒤로 화살표를 사용하세요. welcome: 3분 소개 투어를 보고 가장 중요한 기능에 대해 알아보세요.
끝까지 단계를 완료하는 것이 좋습니다. 언제든지 투어를 다시 시작할 수 있습니다. wiki: "Wiki에서 문서화하고 팀과 함께 지식을 공유할 수 있습니다." - backlogs: - overview: "백로그 보기에서 작업을 관리하세요." - sprints: 오른쪽에 제품 백로그 및 버그 백로그가 있고, 왼쪽에 해당 스프린트가 있습니다. 여기에서 에픽, 사용자 이야기 및 버그를 작성하고 드래그 앤 드롭으로 우선 순위를 정하고 스프린트에 추가할 수 있습니다. - task_board_arrow: "작업 보드를 보려면 스프린트 드롭다운을 여세요..." - task_board_select: "...그리고 작업보드 항목을 선택하세요." - task_board: 작업 보드는 이 스프린트의 진행률을 시각화합니다. 사용자 이야기 옆에 있는 더하기(+) 아이콘을 클릭하여 새 작업이나 제한을 추가하세요.
드래그 앤 드롭으로 상태를 업데이트할 수 있습니다. boards: overview: "보드를 선택하여 보기를 전환하고 애자일 보드 보기를 사용하여 프로젝트를 관리하세요." lists_kanban: 여기에서 보드 내 여러 목록(열)을 만들 수 있습니다. 예를 들어, 이 기능을 사용하면 Kanban 보드를 만들 수 있습니다. @@ -591,49 +585,6 @@ ko: settings: change_notification_settings: 중요 업데이트를 놓치지 않도록 알림 설정을 수정할 수 있습니다. title: 알림 설정 - notify_me: 알림 받기 - reminders: - no_notification: 알림 없음 - timeframes: - normal: - PT0S: 같은 날 - P1D: 1일 전 - P3D: 3일 전 - P7D: 1주 전 - overdue: - P1D: 매일 - P3D: 3일마다 - P7D: 매주 - reasons: - mentioned: - title: 멘션됨 - description: 어디에서든 누군가가 나를 멘션할 때마다 알림 받기 - assignee: 담당자 - responsible: 책임자 - shared: 공유됨 - watched: 주시자 - work_package_commented: 모든 새로운 코멘트 - work_package_created: 새 작업 패키지 - work_package_processed: 모든 상태 변경 사항 - work_package_prioritized: 모든 우선 순위 변경 사항 - work_package_scheduled: 모든 날짜 변경 사항 - global: - immediately: - title: 참여 - description: 귀하가 참여하고 있는 작업 패키지의 모든 활동에 대한 알림(담당자, 책임자 또는 주시자). - delayed: - title: 참여하지 않음 - description: 모든 프로젝트의 활동에 대한 추가 알림. - date_alerts: - title: 날짜 경보 - description: 귀하가 참여하고 있는 오픈 작업 패키지에 대한 중요 날짜가 다가올 때 자동 알림(담당자, 책임자 또는 주시자). - overdue: 기한이 지난 경우 - project_specific: - title: 프로젝트별 알림 설정 - description: 이러한 프로젝트별 설정은 위의 기본 설정을 재정의합니다. - add: 프로젝트에 대한 설정 추가 - already_selected: 이 프로젝트는 이미 선택되었습니다. - remove: 프로젝트 설정 제거 pagination: no_other_page: 유일한 페이지에 있습니다. pages_skipped: 페이지를 건너뛰었습니다. @@ -657,39 +608,6 @@ ko: required_outside_context: '모든 특성을 보려면 작업 패키지를 만들 프로젝트를 선택하십시오. 위 유형이 활성화된 프로젝트만 선택할 수 있습니다. ' - reminders: - settings: - daily: - add_time: 시간 추가 - enable: 일일 이메일 미리 알림 사용 - explanation: 읽지 않은 알림에 대해서만 그리고 지정된 시간에만, 이러한 미리 알림이 전송됩니다. %{no_time_zone} - no_time_zone: 계정의 시간대를 구성할 때까지 시간이 UTC로 표시됩니다. - time_label: '시간 %{counter}:' - title: 읽지 않은 알림에 대해 내게 일일 이메일 미리 알림 보내기 - workdays: - title: 해당 요일에 이메일 미리 알림 받기 - immediate: - title: 내게 이메일 미리 알림 보내기 - mentioned: 누군가 나를 @멘션할 때 즉시 - personal_reminder: 개인 미리 알림을 받는 즉시 - alerts: - title: 기타 항목에 대한 이메일 알림(작업 패키지 외) - explanation: '오늘 알림은 작업 패키지로 제한됩니다. 알림에 포함될 때까지 이러한 이벤트에 대한 이메일 알림을 계속 받도록 선택할 수 있습니다. - - ' - news_added: 뉴스 추가됨 - news_commented: 뉴스 항목의 코멘트 - document_added: 문서 추가됨 - forum_messages: 새 포럼 메시지 - wiki_page_added: 위키 페이지 추가됨 - wiki_page_updated: 위키 페이지 업데이트됨 - membership_added: 멤버십 추가됨 - membership_updated: 멤버십 업데이트됨 - title: 이메일 미리 알림 - pause: - label: 임시로 일일 이메일 미리 알림 일시 중지 - first_day: 첫날 - last_day: 마지막 날 text_are_you_sure: 계속하시겠습니까? text_are_you_sure_to_cancel: 이 페이지에 저장되지 않은 변경 사항이 있습니다. 변경 사항을 취소하시겠습니까? breadcrumb: 이동 경로 @@ -1062,13 +980,6 @@ ko: form_submit: title: 계속하려면 확인하세요 text: 정말로 이 작업을 수행하기를 원하시나요? - destroy_work_package: - title: "%{label} 삭제 확인" - single_text: 작업 패키지를 삭제하시겠습니까? - bulk_text: 다음 %{label}을(를) 삭제하시겠습니까? - has_children: 작업 패키지에 %{childUnits}이(가) 있습니다. - confirm_deletion_children: 나는 작업 패키지의 모든 하위 목록이 재귀적으로 제거되는 것을 인정합니다. - deletes_children: 모든 하위 작업 패키지 및 그 하위 항목도 재귀적으로 삭제됩니다. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-lt.yml b/config/locales/crowdin/js-lt.yml index c154110b0b1..a2123dcb83a 100644 --- a/config/locales/crowdin/js-lt.yml +++ b/config/locales/crowdin/js-lt.yml @@ -515,12 +515,6 @@ lt: sidebar_arrow: Pasinaudokite grįžimo rodykle viršutiniame kairiajame kampe grįžimui į projekto pagrindinį meniu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: "Wiki'yje galite dokumentuoti ir dalintis žiniomis su savo komandos nariais." - backlogs: - overview: Valdykite savo darbus Darbų sąrašo vaizde. - sprints: Kairėje jūs matote produkto darbų sąrašą ir klaidų darbų sąrašą, dešinėje -- atitinkamus sprintus. Čia jūs galite kurti epics, user stories ir bugs, prioritetizuoti vilkdami į reikiamą vietą ir priskirti sprintui. - task_board_arrow: Norėdami matyti jūsų užduočių lentą, atverkite sprinto iškrentantį meniu... - task_board_select: "... ir pasirinkite task board elementą." - task_board: Užduočių lenta vaizduoja sprinto progresą. Nuspauskite pliuso (+) ženklą prie user story norėdami pridėti naujas užduotis arba trukdžius.
Būsena gali būti pakeista nuvelkant į reikiamą vietą. boards: overview: Pasirinkite lentas siekdami pakeisti vaizdą ir valdyti projektą naudodami agile lentų vaizdą. lists_kanban: Lentoje galite sukurti keletą sąrašų (stulpelių) . Ši funkcija leidžia sukurti Kanban lentą. @@ -594,49 +588,6 @@ lt: settings: change_notification_settings: Jūs galite keisti savo pranešimų nustatymus, kad užtikrintumėte, jog niekada nepraleisite svarbaus atnaujinimo. title: Pranešimų nustatymai - notify_me: Pranešti man - reminders: - no_notification: Jokio pranešimo - timeframes: - normal: - PT0S: tą pačią dieną - P1D: prieš 1 dieną - P3D: prieš 3 dienas - P7D: prieš savaitę - overdue: - P1D: kiekvieną dieną - P3D: kas 3 dienas - P7D: kiekvieną savaitę - reasons: - mentioned: - title: Paminėtas - description: Gauti pranešimą kiekvieną kartą, kai kas nors mane bet kur pamini - assignee: Paskirtas - responsible: Atsakingas - shared: Bendrinta - watched: Stebėtojas - work_package_commented: Visi nauji komentarai - work_package_created: Nauji darbų paketai - work_package_processed: Visi būsenų pakeitimai - work_package_prioritized: Visi prioritetų pakeitimai - work_package_scheduled: Visi datų pakeitimai - global: - immediately: - title: Dalyvaujate - description: Pranešimai apie visus veiksmus darbo paketuose, su kuriais jūs susiję (priskirtasis, atsakingas ar stebėtojas). - delayed: - title: Nedalyvaujate - description: Papildomi pranešimai apie veiksmus visuose projektuose. - date_alerts: - title: Datos įspėjimai - description: Automatiniai pranešimai apie artėjančias svarbias atvirų darbo paketų, su kuriais jūs susiję (paskirtasis, atsakingas ar stebėtojas), datas. - overdue: Kai pavėluota - project_specific: - title: Konkrečių projektų pranešimų nustatymai - description: Šie konkrečių projektų nustatymai permuša aukščiau esančius numatytuosius nustatymus. - add: Pridėti nustatymą projektui - already_selected: Šis projektas jau pažymėtas - remove: Išimti projekto nustatymus pagination: no_other_page: Jūs esate vieninteliame puslapyje. pages_skipped: Pages skipped. @@ -660,39 +611,6 @@ lt: required_outside_context: 'Prašau pasirinkti projektą, kuriame turi būti sukurtas darbo paketas, kad matytumėte visus atributus. Jūs galite pasirinkti tik tuos projektus, kurie turi aukščiau nurodytą tipą. ' - reminders: - settings: - daily: - add_time: Pridėti laiką - enable: Įjungti kasdienius el.pašto priminimus - explanation: Jūs gausite šiuos priminimus tik neskaitytiems pranešimams ir tik jūsų nurodytomis valandomis. %{no_time_zone} - no_time_zone: Iki jūs sukonfigūruosite jūsų paskyros laiko juostą, šie laikai bus interpretuojami kaip UTC. - time_label: 'Laikas %{counter}:' - title: Siųskite man kasdienius el.pašto priminimus apie neskaitytus pranešimus - workdays: - title: Gauti e-pašto priminimus šiomis dienomis - immediate: - title: Siųsti man e-pašto priminimą - mentioned: Iš karto, kai kažkas @pamini mane - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: E-pašto pranešimai kitiems (ne darbo paketų) elementams - explanation: 'Šiandien pranešimai galimi tik darbų paketams. Jūs galite pasirinkti gauti įspėjimus laiškais apie šiuos įvykius, kol jie dar įtraukiami į pranešimus: - - ' - news_added: Pridėta naujiena - news_commented: Komentuoti naujienų elementą - document_added: Dokumentai pridėti - forum_messages: Nauji forumo pranešimai - wiki_page_added: Wiki puslapis pridėtas - wiki_page_updated: Wiki puslapis atnaujintas - membership_added: Narystė pridėta - membership_updated: Narystė atnaujinta - title: El.pašto priminimai - pause: - label: Laikinai stabdyti kasdieninius e-pašto priminimus - first_day: Pirma diena - last_day: Paskutinė diena text_are_you_sure: Ar esate įsitikinę? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Susijusi informacija @@ -1065,13 +983,6 @@ lt: form_submit: title: Norėdami tęsti, patvirtinkite text: Ar tikrai norite atlikti šį veiksmą? - destroy_work_package: - title: Patvirtinkite, jog %{label} ištrinamas - single_text: Ar tikrai norite ištrinti darbo paketą - bulk_text: Ar tikrai norite ištrinti %{label}? - has_children: 'Darbų paketas turi %{childUnits}:' - confirm_deletion_children: Aš sutinku, kad VISI šio darbų paketo palikuoniai bus rekursiškai ištrinti. - deletes_children: Visi darbų paketo vaikai ir jų palikuoniai taip pat bus rekursiškai ištrinti. destroy_time_entry: title: Patvirtinkite laiko įrašo trynimą. text: Ar Jūs tikrai norite ištrinti šį laiko įrašą? diff --git a/config/locales/crowdin/js-lv.yml b/config/locales/crowdin/js-lv.yml index b828313761b..1d0e6703022 100644 --- a/config/locales/crowdin/js-lv.yml +++ b/config/locales/crowdin/js-lv.yml @@ -515,12 +515,6 @@ lv: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -593,49 +587,6 @@ lv: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Pašreizējais atbildīgais - responsible: Accountable - shared: Shared - watched: Sekotājs - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Jūs esat vienīgajā lapā. pages_skipped: Pages skipped. @@ -659,39 +610,6 @@ lv: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Vai esat pārliecināts? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1064,13 +982,6 @@ lv: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-mn.yml b/config/locales/crowdin/js-mn.yml index f4495277453..14ec7f4832a 100644 --- a/config/locales/crowdin/js-mn.yml +++ b/config/locales/crowdin/js-mn.yml @@ -515,12 +515,6 @@ mn: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ mn: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Даалгагч - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ mn: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ mn: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-ms.yml b/config/locales/crowdin/js-ms.yml index 008af2c06be..6de174f5ed9 100644 --- a/config/locales/crowdin/js-ms.yml +++ b/config/locales/crowdin/js-ms.yml @@ -515,12 +515,6 @@ ms: sidebar_arrow: Guna anak panah kembali di sudut kiri atas untuk kembali ke menu utama projek. welcome: Ikuti lawatan pengenalan selama tiga minit untuk mempelajari ciri penting yang paling.
Kami mengesyorkan anda melengkapkan langkah sehingga tamat. Anda boleh memulakan semula lawatan pada bila-bila masa. wiki: Dalam wiki anda boleh mendokumentasikan dan kongsi pengetahuan bersama pasukan anda. - backlogs: - overview: Kendalikan kerja anda dalam paparan tunggakan. - sprints: Di kanan anda mempunyai tunggakan produk dan tunggakan bug, di kiri anda mempunyai pecutan yang berkenaan. Di sini anda boleh mencipta epik, cerita pengguna, dan bug, utamakan dengan menarik & melepaskan dan tambah mereka ke pecutan. - task_board_arrow: Untuk lihat papan tugasan anda, buka pecutan drop-down... - task_board_select: "...dan pilih entri papan tugasan." - task_board: Papan tugasan menggambarkan secara visual perkembangan untuk pecutan ini. Klik ikon tambah (+) di sebelah cerita pengguna untuk tambah tugasan baru atau halangan.
Status tersebut boleh dikemas kini dengan tarik dan lepas. boards: overview: Pilih board untuk tukar paparan dan uruskan projek anda menggunakan paparan board agile. lists_kanban: Disini anda boleh mencipta pelbagai senarai (kolum) dalam board anda. Fitur ini membenarkan anda untuk mencipta, sebagai contoh, board Kanban. @@ -591,49 +585,6 @@ ms: settings: change_notification_settings: Anda boleh mengubah suai tetapan pemberitahuan anda untuk memastikan anda tidak terlepas tarikh yang penting. title: Tetapan pemberitahuan - notify_me: Maklumkan saya - reminders: - no_notification: Tiada pemberitahuan - timeframes: - normal: - PT0S: hari yang sama - P1D: 1 hari sebelum - P3D: 3 hari sebelum - P7D: 1 minggu sebelum - overdue: - P1D: setiap hari - P3D: setiap 3 hari - P7D: setiap minggu - reasons: - mentioned: - title: Disebutkan - description: Terima pemberitahuan setiap kali sesiapa menyebut saya di mana saja - assignee: Penerima tugasan - responsible: Bertanggungjawab - shared: Dikongsi - watched: Pemerhati - work_package_commented: Semua komen baharu - work_package_created: Pakej kerja baharu - work_package_processed: Semua perubahan status - work_package_prioritized: Semua perubahan utama - work_package_scheduled: Semua perubahan tarikh - global: - immediately: - title: Menyertai - description: Pemberitahuan untuk semua aktiviti dalam pakej kerja yang anda terlibat (penerima tugasan, bertanggungjawab, atau pemerhati). - delayed: - title: Tidak menyertai - description: Pemberitahuan tambahan untuk aktiviti dalam semua projek. - date_alerts: - title: Peringatan tarikh - description: Pemberitahuan secara automatik apabila tarikh penting menghampiri untuk pakej kerja terbuka yang anda terlibat (penerima tugasan, bertanggungjawab atau pemerhati). - overdue: Apabila tertunggak - project_specific: - title: Tetapan pemberitahuan khusus projek - description: Tetapan pemberitahuan khusus projek mengatasi tetapan default diatas. - add: Tambah tetapan untuk projek - already_selected: Projek ini telah dipilih - remove: Keluarkan tetapan projek pagination: no_other_page: Anda berada di satu-satunya halaman. pages_skipped: Pages skipped. @@ -657,39 +608,6 @@ ms: required_outside_context: 'Sila pilih projek untuk mencipta pakej kerja untuk melihat semua atribut. Anda hanya boleh memilih projek yang mempunyai jenis di atas yang telah diaktifkan. ' - reminders: - settings: - daily: - add_time: Tambah masa - enable: Benarkan peringatan e-mel harian - explanation: Anda akan menerima peringatan hanya untuk pemberitahuan yang belum dibaca dan hanya pada jam yang anda tentukan. %{no_time_zone} - no_time_zone: Sehingga anda konfigurasi zon masa untuk akaun anda, masa akan ditafsirkan dalam UTC. - time_label: 'Masa %{counter}:' - title: Hantar saya peringatan e-mel harian untuk pemberitahuan yang belum dibaca - workdays: - title: Terima peringatan e-mel pada hari-hari ini - immediate: - title: Hantar saya peringatan e-mel - mentioned: Serta merta apabila sesiapa @menyebut saya - personal_reminder: Serta-merta apabila saya menerima peringatan peribadi - alerts: - title: Peringatan e-mel untuk item lain (yang bukan pakej kerja) - explanation: 'Pemberitahuan hari ini adalah terhad untuk pakej kerja. Anda boleh memilih untuk terus menerima makluman e-mel untuk kejadian ini sehingga mereka disertakan dalam pemberitahuan: - - ' - news_added: Berita yang ditambah - news_commented: Komen pada item berita - document_added: Dokumen ditambah - forum_messages: Mesej forum baharu - wiki_page_added: Halaman wiki ditambah - wiki_page_updated: Halaman wiki dikemas kini - membership_added: Keahlian ditambah - membership_updated: Keahlian dikemas kini - title: Peringatan e-mel - pause: - label: Hentikan seketika peringatan e-mel harian - first_day: Hari pertama - last_day: Hari terakhir text_are_you_sure: Adakah anda pasti? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1062,13 +980,6 @@ ms: form_submit: title: Sahkan untuk teruskan text: Adakah anda pasti anda ingin melaksanakan tindakan ini? - destroy_work_package: - title: Sahkan pemadaman %{label} - single_text: Adakah anda pasti anda ingin memadam pakej kerja - bulk_text: Adakah anda pasti anda ingin memadam %{label} yang berikut? - has_children: 'Pakej kerja mempunyai %{childUnits}:' - confirm_deletion_children: Saya mengesahkan semua keturunan pakej kerja yang tersenarai akan dikeluarkan secara berulang-ulang. - deletes_children: Semua pakej kerja anak dan keturunan mereka akan dipadamkan secara berulang-ulang. destroy_time_entry: title: Sahkan pemadaman entri masa text: Adakah anda pasti anda ingin memadam entri masa yang berikut? diff --git a/config/locales/crowdin/js-ne.yml b/config/locales/crowdin/js-ne.yml index b8671dac6fb..2cccded24bc 100644 --- a/config/locales/crowdin/js-ne.yml +++ b/config/locales/crowdin/js-ne.yml @@ -515,12 +515,6 @@ ne: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ ne: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ ne: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ ne: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-nl.yml b/config/locales/crowdin/js-nl.yml index d5660e9b5b3..ec460500b61 100644 --- a/config/locales/crowdin/js-nl.yml +++ b/config/locales/crowdin/js-nl.yml @@ -515,12 +515,6 @@ nl: sidebar_arrow: Gebruik de terug pijl in de linkerbovenhoek om terug te keren naar het project hoofdmenu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Binnen de wiki kunt u kennis delen en documenteren met uw team. - backlogs: - overview: Beheer uw werk in de backlogs weergave. - sprints: Aan de rechterkant hebt u de product backlog en de bug backlog, aan de linkerkant de respectievelijke sprints. Hier kunt u epics, user stories en bugs maken, prioriteren door ze te slepen en ze toevoegen aan een sprint. - task_board_arrow: Om uw taakbordte zien, opent u de sprint drop-down... - task_board_select: "...en selecteer het taakbord item." - task_board: Het taakbord geeft de vooruitgang voor deze sprint weer. Klik op het plus (+) pictogram naast een verhaal om nieuwe taken of belemmeringen toe te voegen.
De status kan worden bijgewerkt door slepen en neerzetten. boards: overview: Selecteer borden om het overzicht te verschuiven en uw project te beheren met behulp van de agile borden weergave. lists_kanban: Hier kunt u meerdere lijsten (kolommen) binnen uw bord maken. Met deze functie kunt u bijvoorbeeld een Kanban boardmaken. @@ -592,49 +586,6 @@ nl: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Instellingen voor meldingen - notify_me: Waarschuw mij - reminders: - no_notification: Geen melding - timeframes: - normal: - PT0S: dezelfde dag - P1D: 1 dag ervoor - P3D: 3 dagen ervoor - P7D: een week ervoor - overdue: - P1D: elke dag - P3D: elke 3 dagen - P7D: elke week - reasons: - mentioned: - title: Genoemd - description: Ontvang een melding elke keer dat iemand mij ergens noemt - assignee: Toegewezene - responsible: Verantwoording afleggen - shared: Gedeeld - watched: Kijker - work_package_commented: Alle nieuwe reacties - work_package_created: Nieuwe werkpakketten - work_package_processed: Alle statuswijzigingen - work_package_prioritized: Alle prioriteitswijzigingen - work_package_scheduled: 'Alle datum wijzigingen ' - global: - immediately: - title: Deelnemend - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Niet-deelnemend - description: Extra meldingen voor activiteiten in alle projecten. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: Wanneer achterstallig - project_specific: - title: Project-specifieke meldingsinstellingen - description: These project-specific settings override default settings above. - add: Instelling voor project toevoegen - already_selected: Dit project is al geselecteerd - remove: Projectinstellingen verwijderen pagination: no_other_page: U bent op de enige pagina. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ nl: required_outside_context: 'Kies een project om het werkpakket in te creëren om alle attributen te zien. U kunt alleen projecten selecteren waarvan het type hierboven is geactiveerd. ' - reminders: - settings: - daily: - add_time: Tijd toevoegen - enable: Dagelijkse e-mailherinneringen inschakelen - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Tijd %{counter}:' - title: Stuur me dagelijkse herinneringen voor ongelezen meldingen - workdays: - title: Ontvang herinneringen per e-mail op deze dagen - immediate: - title: Stuur me een e-mail herinnering - mentioned: Onmiddellijk wanneer iemand @vermeldt - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: E-mail meldingen voor andere items (die geen werkpakketten zijn) - explanation: 'Meldingen vandaag zijn beperkt tot werkpakketten. U kunt ervoor kiezen om e-mailmeldingen voor deze gebeurtenissen te blijven ontvangen totdat ze zijn opgenomen in meldingen: - - ' - news_added: Nieuws toegevoegd - news_commented: Reageer op een nieuwsitem - document_added: Documenten toegevoegd - forum_messages: Nieuwe forumberichten - wiki_page_added: Wiki-pagina toegevoegd - wiki_page_updated: Wiki-pagina bijgewerkt - membership_added: Lidmaatschap toegevoegd - membership_updated: Lidmaatschap bijgewerkt - title: E-mail herinneringen - pause: - label: Temporarily pause daily email reminders - first_day: Eerste dag - last_day: Laatste dag text_are_you_sure: Weet u het zeker? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ nl: form_submit: title: Bevestigen om verder te gaan text: Weet u zeker dat u deze actie wilt uitvoeren? - destroy_work_package: - title: Bevestig verwijdering van %{label} - single_text: Weet u zeker dat u het werkpakket wilt verwijderen - bulk_text: Weet u zeker dat u de volgende %{label} wilt verwijderen? - has_children: 'Het werkpakket heeft %{childUnits}:' - confirm_deletion_children: Ik erken dat alle onderliggende werkpakketten van de weergegeven werkpakketten verwijderd worden. - deletes_children: Alle onderliggende werkpakketten en hun nakomelingen ook worden verwijderd. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-no.yml b/config/locales/crowdin/js-no.yml index ac48d95ba53..5595dae373e 100644 --- a/config/locales/crowdin/js-no.yml +++ b/config/locales/crowdin/js-no.yml @@ -109,7 +109,7 @@ button_save: Lagre button_settings: Innstillinger button_uncheck_all: Avmerk alle - button_update: Oppdater + button_update: Oppdatèr button_export-atom: Last ned Atom button_generate_pdf: Generate PDF button_create: Opprett @@ -515,12 +515,6 @@ sidebar_arrow: Bruk returpilen i øvre venstre hjørne for å gå tilbake til prosjektets hovedmeny. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: I wiki kan du dokumentere og dele kunnskap sammen med teamet. - backlogs: - overview: Administrer arbeidet ditt i - backlogs -visningen. - sprints: Til høyre har du produktet etterslep og feilloggen, til venstre har du de respektive stegene. Her kan du lage epics, brukerhistorier og bugs, prioriterer du via dra og slipp og legge dem til et steg. - task_board_arrow: For å se oppgavetavle, åpne steg fra nedtrekksmenyen... - task_board_select: "...og velg oppgavetavlens oppføring." - task_board: Oppgavetavlen visualiserer fremdriften for denne etappen. Klikk på plussikonet (+) ved siden av en brukerhistorie for å legge til nye oppgaver eller hindringer.
Statusen kan oppdateres ved å dra og slipp. boards: overview: Velg tavler for å skift visningen og administrere prosjektet ved hjelp av den dynamiske tavlevisningen. lists_kanban: Her kan du opprette flere lister (kolonner) i tavlen. Denne funksjonen lar deg for eksempel opprette en Kanban tavle. @@ -592,49 +586,6 @@ settings: change_notification_settings: Du kan endre varslingsinnstillinger for å sikre at du aldri går glipp av en viktig oppdatering. title: Varslingsinnstillinger - notify_me: Gi meg beskjed - reminders: - no_notification: Ingen varsling - timeframes: - normal: - PT0S: samme dag - P1D: 1 dag før - P3D: 3 dager før - P7D: en uke før - overdue: - P1D: hver dag - P3D: hver 3. dag - P7D: hver uke - reasons: - mentioned: - title: Nevnt - description: Motta en melding hver gang noen nevner meg hvor som helst - assignee: Deltaker - responsible: Ansvarlig - shared: Delt - watched: Overvåker - work_package_commented: Alle nye kommentarer - work_package_created: Nye arbeidspakker - work_package_processed: Alle statusendringer - work_package_prioritized: Alle endringer i prioritet - work_package_scheduled: Alle dato endringer - global: - immediately: - title: Deltar - description: Varsler for alle aktiviteter i arbeidspakker du er involvert i (oppdragstaker, ansvarlig eller overvåker). - delayed: - title: Ikke-deltakende - description: Melding om ytterligere aktiviteter i alle prosjekter. - date_alerts: - title: Dato varsler - description: Automatiske varsler når viktige datoer nærmer seg for åpne arbeidspakker du er med i (oppdragstaker, ansvarlig eller overvåker). - overdue: Når forfalt - project_specific: - title: Prosjekt-spesifikke varslingsinnstillinger - description: Disse prosjektspesifikke innstillingene overstyrer standardinnstillingene ovenfor. - add: Legg til innstilling for prosjekt - already_selected: Dette prosjektet er allerede valgt - remove: Fjern prosjektinnstillinger pagination: no_other_page: Du er på den eneste siden. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ required_outside_context: 'Velg et prosjekt for å opprette arbeidspakken og se alle egenskaper. Du kan bare velge prosjekter som har typen ovenfor aktivert. ' - reminders: - settings: - daily: - add_time: Legg til tid - enable: Aktiver daglige e-postpåminnelser - explanation: Du får bare disse påminnelsene for uleste varslinger og bare på tider du angir. %{no_time_zone} - no_time_zone: Fram til du konfigurerer en tidssone for kontoen din, vil tidene bli tolket til å være i UTC. - time_label: 'Tid %{counter}:' - title: Send meg daglige e-postpåminnelser for uleste varslinger - workdays: - title: Motta e-postpåminnelser for disse dagene - immediate: - title: Send meg en påminnelse - mentioned: Umiddelbart hvis noen @nevner meg - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Epost varsler for andre produkter (som ikke er arbeidspakker) - explanation: 'Varsler i dag er begrenset til arbeidspakker. Du kan velge å fortsette å motta e-postvarsler for disse hendelsene inntil de er inkludert i varsler: - - ' - news_added: Lagt til nyhet - news_commented: Kommenter en nyhet - document_added: Dokumenter lagt til - forum_messages: Nye forummeldinger - wiki_page_added: Wiki-side lagt til - wiki_page_updated: Wiki-side oppdatert - membership_added: Medlemskap lagt til - membership_updated: Medlemskapet er oppdatert - title: E-postpåminnelser - pause: - label: pause daglige e-postpåminnelser midlertidig - first_day: 'Første dag ' - last_day: Siste dag text_are_you_sure: Er du sikker? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Brødsmule @@ -1063,13 +981,6 @@ form_submit: title: Bekreft for å fortsette text: Er du sikker på at du vil utføre denne handlingen? - destroy_work_package: - title: Bekreft sletting av %{label} - single_text: Er du sikker på at du vil slette arbeidspakken - bulk_text: Er du sikker på at du vil slette følgende %{label}? - has_children: 'Arbeidspakken har %{childUnits}:' - confirm_deletion_children: Jeg erkjenner at ALLE underordnede av listede arbeidspakker vil fjernes samtidig. - deletes_children: Alle underordnede arbeidspakker og tilhørende vil også bli slettet samtidig. destroy_time_entry: title: Bekreft sletting av tidsregistrering text: Er du sikker på at du vil slette følgende tidsregistrering? diff --git a/config/locales/crowdin/js-pl.yml b/config/locales/crowdin/js-pl.yml index a2ddce6bc77..c77d8f5a2f7 100644 --- a/config/locales/crowdin/js-pl.yml +++ b/config/locales/crowdin/js-pl.yml @@ -515,12 +515,6 @@ pl: sidebar_arrow: Użyj strzałki powrotu w lewym górnym rogu, aby wrócić do menu głównego projektu. welcome: "Poświęć trzy minuty na zapoznanie się z najważniejszymi funkcjami. \n
Zalecamy wykonanie tych kroków do końca. Zwiedzanie można w każdej chwili zacząć od nowa." wiki: W wiki możesz dokumentować wiedzę i dzielić się nią z zespołem. - backlogs: - overview: Zarządzaj swoją pracą w widoku backlogs. - sprints: Po prawej stronie znajduje się backlog produktu i backlog usterek, a po lewej stronie znajdują się odpowiednie sprinty. Tutaj można tworzyć wydarzenia, wpisy użytkownika i usterki, nadawać priorytety metodą przeciągania i upuszczania oraz dodawać je do sprintu. - task_board_arrow: Aby zobaczyć swój panel zadań, otwórz menu rozwijane sprint... - task_board_select: "...i wybierz wpis panel zadań." - task_board: Panel zadań przedstawia postęp tego sprintu. Dodaj nowe zadania lub przeszkody, klikając ikonę (+) obok wpisu użytkownika.
Stan można zaktualizować metodą przeciągania i upuszczania. boards: overview: Wybierz tablice, aby zmienić widok i zarządzać projektem za pomocą widoku tablic. lists_kanban: Tutaj możesz utworzyć wiele list (kolumn) w jednym widoku tablicy. Ta funkcja pozwala ci na przykład na utworzenie tablicy Kanban. @@ -594,49 +588,6 @@ pl: settings: change_notification_settings: Możesz zmodyfikować ustawienia powiadomień, aby upewnić się, że nigdy nie przegapisz ważnej aktualizacji. title: Ustawienia powiadomień - notify_me: Powiadom mnie - reminders: - no_notification: Bez powiadomienia - timeframes: - normal: - PT0S: tego samego dnia - P1D: 1 dzień przed - P3D: 3 dni przed - P7D: 1 tydzień przed - overdue: - P1D: codziennie - P3D: co 3 dni - P7D: co tydzień - reasons: - mentioned: - title: Wzmianka - description: Otrzymuj powiadomienie za każdym razem, gdy pojawi się o tobie wzmianka gdziekolwiek - assignee: Przypisany do - responsible: Osoba odpowiedzialna - shared: Udostępniono - watched: Obserwator - work_package_commented: Wszystkie nowe komentarze - work_package_created: Nowe pakiety robocze - work_package_processed: Wszystkie zmiany statusu - work_package_prioritized: Wszystkie zmiany priorytetów - work_package_scheduled: Wszystkie zmiany daty - global: - immediately: - title: Uczestnictwo - description: Powiadomienia dotyczące wszystkich działań w pakietach roboczych, do których Cię zaangażowano (jako osobę przypisaną, odpowiedzialną lub obserwator) - delayed: - title: Brak uczestnictwa - description: Dodatkowe powiadomienia dotyczące działań we wszystkich projektach - date_alerts: - title: Alerty dotyczące dat - description: 'Automatyczne powiadomienia, gdy zbliżają się ważne daty otwartych pakietów roboczych, do których Cię zaangażowano (jako osobę: przypisaną, odpowiedzialną lub obserwatora)' - overdue: Po upływie terminu - project_specific: - title: Ustawienia powiadomień dla danego projektu - description: Te ustawienia dla danego projektu zastępują powyższe ustawienia domyślne. - add: Dodaj ustawienie dla projektu - already_selected: Ten projekt jest już wybrany - remove: Usuń ustawienia projektu pagination: no_other_page: Jesteś na jedynej stronie. pages_skipped: Pominięte strony. @@ -660,39 +611,6 @@ pl: required_outside_context: 'Wybierz projekt, w którym chcesz utworzyć pakiet roboczy, aby zobaczyć wszystkie atrybuty. Możesz wybrać tylko projekty, które mają aktywowany powyższy typ. ' - reminders: - settings: - daily: - add_time: Dodaj czas - enable: Włącz codzienne przypomnienia e-mail - explanation: Otrzymasz te przypomnienia tylko dla nieprzeczytanych powiadomień i tylko w określonych przez Ciebie godzinach. %{no_time_zone} - no_time_zone: Dopóki nie skonfigurujesz strefy czasowej dla swojego konta, czas będzie interpretowany jako UTC. - time_label: 'Czas %{counter}:' - title: Wyślij mi codzienne przypomnienia e-mail dla nieprzeczytanych powiadomień - workdays: - title: Otrzymuj przypomnienia e-mail w tych dniach - immediate: - title: Wyślij mi przypomnienie e-mail - mentioned: Natychmiast, gdy pojawi się @wzmianka o mnie - personal_reminder: Natychmiast po otrzymaniu osobistego przypomnienia - alerts: - title: Powiadomienia e-mail dla innych elementów (które nie są pakietami roboczymi) - explanation: 'Dzisiaj powiadomienia są ograniczone do pakietów roboczych. Możesz nadal otrzymywać powiadomienia e-mail dla tych zdarzeń, dopóki nie zostaną one zawarte w powiadomieniach: - - ' - news_added: Wiadomość dodana - news_commented: Komentarz do nowości - document_added: Dodanie dokumentu - forum_messages: Nowe wiadomości na forum - wiki_page_added: Dodano stronę wiki - wiki_page_updated: Zaktualizowano stronę wiki - membership_added: Dodanie członkostwa - membership_updated: Aktualizacja członkostwa - title: Przypomnienia e-mail - pause: - label: Tymczasowo wstrzymuj codzienne przypomnienia e-mail - first_day: Pierwszy dzień - last_day: Ostatni dzień text_are_you_sure: Jesteś pewny? text_are_you_sure_to_cancel: Na tej stronie znajdują się niezapisane zmiany. Czy na pewno chcesz je odrzucić? breadcrumb: Ścieżka @@ -1065,13 +983,6 @@ pl: form_submit: title: Potwierdź, aby kontynuować text: Czy na pewno chcesz wykonać tę akcję? - destroy_work_package: - title: Potwierdź usunięcie %{label} - single_text: Czy na pewno chcesz usunąć pakiet roboczy - bulk_text: Czy na pewno chcesz usunąć %{label}? - has_children: 'Pakiet roboczy zawiera %{childUnits}:' - confirm_deletion_children: Potwierdzam, że WSZYSTKIE elementy potomne pakietów roboczych z listy będą rekurencyjnie usuwane. - deletes_children: Wszystkie pakiety podrzędne i ich elementy potomne również będą rekursywnie usuwane. destroy_time_entry: title: Potwierdź usunięcie wpisu text: Na pewno chcesz skasować? diff --git a/config/locales/crowdin/js-pt-BR.yml b/config/locales/crowdin/js-pt-BR.yml index b0659471aa9..a2a87370503 100644 --- a/config/locales/crowdin/js-pt-BR.yml +++ b/config/locales/crowdin/js-pt-BR.yml @@ -515,12 +515,6 @@ pt-BR: sidebar_arrow: Use a seta de retorno no canto superior esquerdo para retornar ao menu principal do projeto. welcome: Faça um tour de introdução de três minutos para conhecer os principais recursos.
Recomendamos que você siga todas as etapas até o final. O tour pode ser reiniciado a qualquer momento. wiki: 'Na wiki, você pode documentar e compartilhar conhecimento junto com sua equipe. ' - backlogs: - overview: Gerencie seu trabalho na visão de backlogs. - sprints: À direita, você tem o backlog de produto e o backlog de erro, à esquerda, você tem as respectivas sprints. Aqui você pode criar épicos, user stories e erros, priorizar via arrastar e soltar e adicioná-los a uma sprint. - task_board_arrow: Para ver o seu quadro de tarefas, abra o menu suspenso da sprint... - task_board_select: "... e selecione a entrada do quadro de tarefas. " - task_board: O quadro de tarefas visualiza o progresso desta sprint. Clique no ícone de mais (+) ao lado de uma user story para adicionar novas tarefas ou impedimentos.
O status pode ser atualizado por arrastar e soltar. boards: overview: Selecione quadros para mudar a visão e gerenciar seu projeto usando a exibição de quadros ágeis. lists_kanban: Aqui você pode criar várias listas (colunas) dentro do seu painel. Este recurso permite que você crie um Quadro Kanban, por exemplo. @@ -592,49 +586,6 @@ pt-BR: settings: change_notification_settings: Você pode modificar suas configurações de notificação para se certificar de nunca perder nenhuma atualização importante. title: Configurações de notificação - notify_me: Notifique-me - reminders: - no_notification: Sem notificação - timeframes: - normal: - PT0S: mesmo dia - P1D: 1 dia antes - P3D: 3 dias antes - P7D: uma semana antes - overdue: - P1D: todo dia - P3D: a cada 3 dias - P7D: Toda semana - reasons: - mentioned: - title: Mencionado - description: Receber uma notificação sempre que alguém me mencionar - assignee: Cessionário - responsible: Responsável - shared: Compartilhado - watched: Observador - work_package_commented: Todos os novos comentários - work_package_created: Novos pacotes de trabalho - work_package_processed: Todas as mudanças de situação - work_package_prioritized: Todas as mudanças de prioridade - work_package_scheduled: Todas as alterações de data - global: - immediately: - title: Participando - description: Notificações para todas as atividades nos pacotes de trabalho que você está envolvido (cessionário, responsável ou observador). - delayed: - title: Não participando - description: Notificações adicionais para atividades em todos os projetos - date_alerts: - title: Alertas de data - description: Notificações automáticas quando datas importantes estiverem se aproximando para pacotes de trabalho abertos que você esteja envolvido (cessionário, responsável ou observador). - overdue: Quando vencido - project_specific: - title: Configurações de notificação específicas de projetos - description: Essas configurações específicas de projeto substituem as configurações padrão acima - add: Adicionar configuração para o projeto - already_selected: Este projeto já está selecionado - remove: Remover configurações de projeto pagination: no_other_page: Você está na página única. pages_skipped: Páginas puladas. @@ -658,39 +609,6 @@ pt-BR: required_outside_context: 'Por favor, escolha o projeto onde vai criar o pacote de trabalho para visualizar todos os atributos. Você pode selecionar somente projetos que possuam o tipo acima ativado. ' - reminders: - settings: - daily: - add_time: Adicionar tempo - enable: 'Ativar lembretes diários por e-mail ' - explanation: Você receberá estes lembretes somente para as notificações não lidas e apenas nas horas especificadas por você. %{no_time_zone} - no_time_zone: Até que um fuso horário seja configurado em sua conta, os horários serão interpretados como UTC. - time_label: 'Tempo %{counter}:' - title: Envie-me lembretes diários por e-mail para as notificações não lidas - workdays: - title: Receber lembretes por e-mail nestes dias - immediate: - title: Envie-me um lembrete por e-mail - mentioned: Imediatamente quando alguém me @mencionar - personal_reminder: Imediatamente quando eu receber um lembrete pessoal - alerts: - title: Alertas por e-mail para outros itens (qua não sejam pacotes de trabalho) - explanation: 'As notificações hoje estão limitadas a pacotes de trabalho. Você pode optar por continuar a receber alertas por e-mail para estes eventos até que eles sejam incluídos nas notificações: - - ' - news_added: Notícia adicionada - news_commented: Comentar em um item de notícia - document_added: Documentos adicionados - forum_messages: Novas mensagens de fórum - wiki_page_added: Página wiki adicionada - wiki_page_updated: Página wiki atualizada - membership_added: Associação adicionada - membership_updated: Associação atualizada - title: Lembretes por e-mail - pause: - label: 'Pausar temporariamente lembretes por e-mail diários ' - first_day: Primeiro dia - last_day: Último dia text_are_you_sure: Você tem certeza? text_are_you_sure_to_cancel: Você tem alterações não salvas nesta página. Tem certeza de que deseja descartá-las? breadcrumb: Trilha de navegação @@ -1063,13 +981,6 @@ pt-BR: form_submit: title: Confirme para continuar text: Tem certeza de que deseja executar esta ação? - destroy_work_package: - title: Confirmar exclusão do %{label} - single_text: Tem certeza de que deseja excluir o pacote de trabalho? - bulk_text: Você tem certeza de que deseja excluir o seguinte %{label}? - has_children: 'O pacote de trabalho tem %{childUnits}:' - confirm_deletion_children: Tenho conhecimento que TODOS os descendentes dos pacotes de trabalho listados serão removidos recursivamente. - deletes_children: Todos os pacotes de trabalho filhos e seus descendentes também serão excluídos recursivamente. destroy_time_entry: title: Confirmar exclusão da entrada de tempo text: Você tem certeza que deseja excluir a seguinte entrada de tempo? diff --git a/config/locales/crowdin/js-pt-PT.yml b/config/locales/crowdin/js-pt-PT.yml index 889f67bd505..1703ba94717 100644 --- a/config/locales/crowdin/js-pt-PT.yml +++ b/config/locales/crowdin/js-pt-PT.yml @@ -515,12 +515,6 @@ pt-PT: sidebar_arrow: Use a seta de retorno no canto superior esquerdo para voltar ao menu principal do projeto. welcome: Faça uma visita guiada de 3 minutos para conhecer as funcionalidades mais importantes.
Recomendamos que conclua as etapas até ao final. Pode reiniciar a visita a qualquer momento. wiki: Na wiki pode documentar e partilhar conhecimento com a sua equipa. - backlogs: - overview: Faça a gestão do seu trabalho na vista de backlogs. - sprints: À direita tem o backlog do produto e o backlog do bug, à esquerda tem os sprints respectivos. Aqui pode criar épicos, histórias de utilizadores e bugs, definir a prioridade arrastando e soltando, e adicioná-los a um sprint. - task_board_arrow: Para ver o seu quadro de tarefas, abra o menu suspenso de sprint... - task_board_select: "...e selecione a entrada quadro de tarefas." - task_board: O painel de tarefas visualiza o progresso deste sprint. Clique no ícone mais (+) ao lado de um histórico de utilizador para adicionar novas tarefas ou impedimentos.
O estado pode ser atualizado ao arrastar e soltar. boards: overview: Selecione quadros para mudar a vista e gerir o seu projeto através da vista de painéis ágeis. lists_kanban: Aqui pode criar múltiplas listas (colunas) dentro do seu quadro. Este recurso permite que crie um Painel Kanban, por exemplo. @@ -592,49 +586,6 @@ pt-PT: settings: change_notification_settings: Pode modificar as suas definições de notificações para garantir que nunca perde uma atualização importante. title: Definições das notificações - notify_me: Notificar-me - reminders: - no_notification: Sem notificações - timeframes: - normal: - PT0S: no mesmo dia - P1D: 1 dia antes - P3D: 3 dias antes - P7D: uma semana antes - overdue: - P1D: todos os dias - P3D: a cada 3 dias - P7D: todas as semanas - reasons: - mentioned: - title: Mencionou - description: Receber uma notificação sempre que alguém me mencionar em qualquer lugar - assignee: Responsável - responsible: Responsável - shared: Partilhado - watched: Observador - work_package_commented: Todos os novos comentários - work_package_created: Novos pacotes de trabalho - work_package_processed: Todas as mudanças de estado - work_package_prioritized: Todas as mudanças de prioridade - work_package_scheduled: Todas as alterações de data - global: - immediately: - title: Participante - description: Notificações para todas as atividades nos pacotes de trabalho em que está envolvido (responsável ou observador). - delayed: - title: Não participante - description: Notificações adicionais para atividades em todos os projetos. - date_alerts: - title: Alertas de data - description: Notificações automáticas ao aproximarem-se datas importantes para pacotes de trabalho abertos em que esteja envolvido (responsável ou observador). - overdue: Quando vencido - project_specific: - title: Configurações de notificação específicas do projeto - description: Estas configurações específicas do projeto substituem as configurações padrão acima. - add: Adicionar configuração ao projeto - already_selected: Este projeto já está selecionado - remove: Remover definições do projeto pagination: no_other_page: Mais nenhuma página a apresentar. pages_skipped: Páginas saltadas. @@ -658,39 +609,6 @@ pt-PT: required_outside_context: 'Por favor escolha um projeto onde criar o pacote de trabalho de forma a ver todos os atributos. Só pode selecionar projetos com o tipo acima ativado. ' - reminders: - settings: - daily: - add_time: Adicionar hora - enable: Ativar lembretes diários por e-mail - explanation: Vai receber estes lembretes apenas para notificações não lidas e apenas às horas que especificar. %{no_time_zone} - no_time_zone: Até que configure um fuso horário para a sua conta, os horários serão interpretados como estando em UTC. - time_label: 'Hora %{counter}:' - title: Envie-me lembretes diários de e-mail para notificações não lidas - workdays: - title: Receba lembretes por e-mail nestes dias - immediate: - title: Enviar-me um lembrete por e-mail - mentioned: Imediatamente quando alguém me @mencionar - personal_reminder: Imediatamente quando recebo um lembrete pessoal - alerts: - title: Alertas de e-mail para outros itens (que não são pacotes de trabalho) - explanation: 'As notificações hoje estão limitadas a pacotes de trabalho. Pode optar por continuar a receber alertas de e-mail para estes eventos até que eles sejam incluídos nas notificações: - - ' - news_added: Notícia adicionada - news_commented: Comentar numa notícia - document_added: Documentos adicionados - forum_messages: Novas mensagens do fórum - wiki_page_added: Página wiki adicionada - wiki_page_updated: Página wiki atualizada - membership_added: Adesão adicionada - membership_updated: Adesão atualizada - title: Lembretes por e-mail - pause: - label: Pausar temporariamente lembretes de e-mail diários - first_day: Primeiro dia - last_day: Último dia text_are_you_sure: Tem a certeza? text_are_you_sure_to_cancel: Tem alterações não guardadas nesta página. Tem a certeza de que as quer eliminar? breadcrumb: Estrutural @@ -1063,13 +981,6 @@ pt-PT: form_submit: title: Confirmar para continuar text: Tem a certeza que pretende realizar esta ação? - destroy_work_package: - title: Confirmar exclusão do %{label} - single_text: Tem a certeza de que deseja eliminar o pacote de trabalho? - bulk_text: Tem a certeza de que deseja apagar a seguinte %{label}? - has_children: 'O pacote de trabalho tem %{childUnits}:' - confirm_deletion_children: Reconheço que todos os descendentes dos pacotes de trabalho listados serão recursivamente removidos. - deletes_children: Todos os pacotes de trabalho filhos e seus descendentes serão também recursivamente apagados. destroy_time_entry: title: Confirmar a eliminação da entrada de tempo text: Pretende remover a seguinte entrada de tempo? diff --git a/config/locales/crowdin/js-ro.yml b/config/locales/crowdin/js-ro.yml index 13eb514d9b7..05b6cff6b0c 100644 --- a/config/locales/crowdin/js-ro.yml +++ b/config/locales/crowdin/js-ro.yml @@ -109,7 +109,7 @@ ro: button_save: Salvează button_settings: Setări button_uncheck_all: Deselectează tot - button_update: Actualizează + button_update: Actualizare button_export-atom: Descarcă Atom button_generate_pdf: Generează PDF button_create: Creează @@ -515,12 +515,6 @@ ro: sidebar_arrow: Folosește săgeata de întoarcere din colțul din stânga sus pentru a te întoarce în meniul al proiectului. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: În cadrul Wiki puteți documenta și împărtăși cunoștințe împreună cu echipa dumneavoastră. - backlogs: - overview: Gestionează-ți munca în vizualizarea backlogs. - sprints: În dreapta aveți lista de rezultate și erorile restante, în stânga aveți sprintele respective. Aici poți crea epicuri, povestiri pentru utilizatori și erori, prioritizează prin drag & drop și adaugă-le la un sprint. - task_board_arrow: Pentru a vedea panoul de sarcini, deschideți meniul Sprint... - task_board_select: "... și selectați intrarea Task board." - task_board: Grupul de lucru vizualizează progresul pentru această iterație. Dă click pe pictograma plus (+) de lângă o cerință de utilizator pentru a adăuga sarcini noi sau impedimente.
Starea poate fi actualizată cu glisare și plasare. boards: overview: Selectează secțiunile pentru a schimba vizualizarea și administrarea proiectului tău folosind vizualizarea secțiunilor agile. lists_kanban: Aici puteţi crea mai multe liste (coloane) în cadrul secţiunii. Această funcţie vă permite să creaţi o tabelă Kanban, de exemplu. @@ -593,49 +587,6 @@ ro: settings: change_notification_settings: Poți modifica setările de notificare pentru a te asigura că nu pierzi niciodată o actualizare importantă. title: Setări notificare - notify_me: Notifică-mă - reminders: - no_notification: Nicio notificare - timeframes: - normal: - PT0S: în aceeași zi - P1D: cu 1 zi înainte - P3D: cu 3 zile înainte - P7D: " cu o săptămână înainte " - overdue: - P1D: zilnic - P3D: la fiecare 3 zile - P7D: săptămânal - reasons: - mentioned: - title: Menţionat - description: Primesc o notificare de fiecare dată când cineva mă menționează oriunde - assignee: Executant - responsible: Responsabil - shared: Partajat - watched: Observator - work_package_commented: Toate comentariile noi - work_package_created: Pachete de lucru noi - work_package_processed: Toate modificările de stare - work_package_prioritized: Toate modificările de prioritate - work_package_scheduled: Toate modificările dății - global: - immediately: - title: Participant - description: Notificări pentru toate activitățile din pachetele de lucru în care ești implicat (desemnat, responsabil sau observator). - delayed: - title: Unde nu participi - description: Notificări suplimentare pentru activitățile din toate proiectele. - date_alerts: - title: Alerte dăți - description: Notificări automate atunci când se apropie dăți importante pentru pachetele de lucru deschise în care ești implicat (desemnat, responsabil sau observator). - overdue: În caz de întârziere - project_specific: - title: Setări notificare specifice proiectului - description: Aceste setări specifice proiectului prevalează asupra setărilor implicite de mai sus. - add: Adaugă o setare pentru proiect - already_selected: Acest proiect este deja selectat - remove: Elimină setările proiectului pagination: no_other_page: Ești pe singura pagină. pages_skipped: Pagini sărite. @@ -659,39 +610,6 @@ ro: required_outside_context: 'Te rog să alegi un proiect în care să creezi pachetul de lucru pentru a vedea toate atributele. Poți selecta numai proiectele care au tipul de mai sus activat. ' - reminders: - settings: - daily: - add_time: Adaugă timp - enable: Activează reamintirile zilnice prin e-mail - explanation: Vei primi aceste reamintiri numai pentru notificările necitite și numai la câteva ore specificate. %{no_time_zone} - no_time_zone: Până când configurezi un fus orar pentru contul tău, orele vor fi interpretate ca fiind în UTC. - time_label: 'Timp %{counter}:' - title: Trimite-mi reamintiri zilnice prin e-mail pentru notificările necitite - workdays: - title: Primește reamintiri prin e-mail în aceste zile - immediate: - title: Trimite-mi un e-mail de reamintire - mentioned: Imediat când cineva mă @menționează - personal_reminder: Imediat când primesc reamintiri personale - alerts: - title: Alerte prin e-mail pentru alte elemente (care nu sunt pachete de lucru) - explanation: 'Astăzi, notificările se limitează la pachetele de lucru. Puteți alege să continuați să primiți alerte prin e-mail pentru aceste evenimente până când acestea vor fi incluse în notificări: - - ' - news_added: Noutăți adăugate - news_commented: Comentariu la o știre - document_added: Documente adăugate - forum_messages: Mesaje noi pe forum - wiki_page_added: Pagină wiki adăugată - wiki_page_updated: Pagină wiki actualizată - membership_added: Adaugă membri - membership_updated: Membrii actualizați - title: Reamintiri e-mail - pause: - label: Întrerupe temporar reamintirile zilnice prin e-mail - first_day: Prima zi - last_day: Ultima zi text_are_you_sure: Ești sigur? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1064,13 +982,6 @@ ro: form_submit: title: Confirmă pentru a continua text: Ești sigur că vrei să realizezi această acțiune? - destroy_work_package: - title: Confirmă ștergerea %{label} - single_text: Ești sigur că vrei să ștergi pachetul de lucru - bulk_text: Ești sigur că vrei să ștergi următoarele %{label}? - has_children: 'Pachetul de lucru conține %{childUnits}:' - confirm_deletion_children: Recunosc că TOȚI descendenții pachetelor de lucru enumerate vor fi eliminați în mod recursiv. - deletes_children: De asemenea, toate pachetele de lucru minore și descendenții acestora vor fi șterse în mod recursiv. destroy_time_entry: title: Confirmarea ștergerii înregistrării timpului text: Ești sigur că vrei să ștergi următoarea înregistrare a timpului? diff --git a/config/locales/crowdin/js-ru.yml b/config/locales/crowdin/js-ru.yml index e0c88afba90..2420a7ae5af 100644 --- a/config/locales/crowdin/js-ru.yml +++ b/config/locales/crowdin/js-ru.yml @@ -109,7 +109,7 @@ ru: button_save: Сохранить button_settings: Настройки button_uncheck_all: Снять все отметки - button_update: Обновить + button_update: Обновление button_export-atom: Скачать Atom button_generate_pdf: Создать PDF button_create: Создать @@ -517,12 +517,6 @@ ru: sidebar_arrow: Используйте стрелку возврата в левом верхнем углу, чтобы вернуться в главное меню проекта. welcome: Совершите вводный трехминутный тур для знакомства с наиболее важными особенностями.
Рекомендуем пройти все шаги до конца. Пройти вводный тур повторно можно в любое время. wiki: В пределах wiki возможно документирование и обмен знаниями в вашей команде. - backlogs: - overview: Управляйте своей работой в представлении backlogs. - sprints: Справа у вас есть бэклог продукта и бэклог ошибок, слева у вас есть соответствующие спринты. Здесь вы можете создать эпики, пользовательские истории и ошибки, приоритизировать через перетаскивание и добавить их в спринт. - task_board_arrow: Чтобы увидеть вашу панель задач, откройте выпадающий спринт... - task_board_select: "... и выберите строку Панель задач." - task_board: Панель задач визуализирует прогресс для этого спринта. Нажмите на значок плюс (+) рядом с пользовательской историей, чтобы добавить новые задачи или препятствия.
Статус можно обновить перетаскиванием. boards: overview: Выберите доски , чтобы сдвинуть вид и управлять вашим проектом с помощью вида agile-доски. lists_kanban: Здесь вы можете создать несколько списков (столбцов) на доске. Эта функция позволяет создать доску Канбана, например. @@ -596,49 +590,6 @@ ru: settings: change_notification_settings: Измените настройки уведомлений , чтобы не пропустить важное обновление. title: Настройки уведомлений - notify_me: Уведомлять меня - reminders: - no_notification: Нет уведомления - timeframes: - normal: - PT0S: тот же день - P1D: за 1 день до - P3D: 3 дня до - P7D: за неделю до - overdue: - P1D: каждый день - P3D: каждые 3 дня - P7D: каждую неделю - reasons: - mentioned: - title: Упомянутый - description: Получать уведомления каждый раз, когда кто-то упоминает меня в любом месте - assignee: Назначенный - responsible: Ответственный - shared: Общий доступ - watched: Наблюдатель - work_package_commented: Все новые комментарии - work_package_created: Новые пакеты работ - work_package_processed: Все изменения статуса - work_package_prioritized: Все приоритетные изменения - work_package_scheduled: Все изменения даты - global: - immediately: - title: Участие - description: Уведомления обо всех действиях в пакетах работ, в которых вы участвуете (назначение, подотчетность или наблюдение). - delayed: - title: Неучастие - description: Дополнительные уведомления для деятельности во всех проектах. - date_alerts: - title: Дата оповещения - description: Автоматическое уведомление, когда приближаются важные даты открытия пакетов работ, в которых вы участвуете (исполнитель, ответственный или наблюдатель). - overdue: Когда просрочено - project_specific: - title: Конкретные настройки уведомлений проекта - description: Эти настройки специфического проекта переопределяют параметры по умолчанию выше. - add: Добавить настройку для проекта - already_selected: Этот проект уже выбран - remove: Удалить настройки проекта pagination: no_other_page: Вы находитесь на единственной странице. pages_skipped: Страницы пропущены. @@ -662,39 +613,6 @@ ru: required_outside_context: 'Пожалуйста, выберите проект для создания пакета работ для просмотра всех атрибутов. Вы можете выбрать только проекты, которые имеют вышеуказанный тип. ' - reminders: - settings: - daily: - add_time: Добавить время - enable: Включить ежедневные напоминания по электронной почте - explanation: Вы будете получать эти напоминания только для непрочитанных уведомлений и только в указанные вами часы. %{no_time_zone} - no_time_zone: До уточнения часового пояса для вашего аккаунта время будет в UTC. - time_label: 'Время %{counter}:' - title: Посылать мне ежедневные напоминания по электронной почте для непрочитанных уведомлений - workdays: - title: Получать напоминания по электронной почте в эти дни - immediate: - title: Отправить мне напоминание по электронной почте - mentioned: Сразу же когда кто-то @упоминает меня - personal_reminder: Немедленно, когда я получаю персональное напоминание - alerts: - title: Уведомления по электронной почте для других элементов (которые не являются пакетами работ) - explanation: 'Уведомления сегодня ограничены пакетами работ. Вы можете продолжать получать уведомления по электронной почте об этих событиях до тех пор, пока они не будут включены в уведомления: - - ' - news_added: Новость добавлена - news_commented: Комментарий к новости - document_added: Добавлены документы - forum_messages: Новые сообщения форума - wiki_page_added: Wiki страница добавлена - wiki_page_updated: Wiki страница обновлена - membership_added: Членство добавлено - membership_updated: Членство обновлено - title: Почтовые напоминания - pause: - label: Временно приостановить ежедневные напоминания по электронной почте - first_day: Первый день - last_day: Последний день text_are_you_sure: Уверены? text_are_you_sure_to_cancel: У Вас есть несохраненные изменения на этой странице. Вы уверены, что хотите их отменить? breadcrumb: Навигационная цепочка @@ -1067,13 +985,6 @@ ru: form_submit: title: Подтвердите продолжение text: Вы действительно хотите выполнить это действие? - destroy_work_package: - title: Подтвердите удаление %{label} - single_text: Вы уверены, что хотите удалить пакет работ - bulk_text: Вы уверены, что хотите удалить следующие %{label}? - has_children: 'Пакет работ имеет %{childUnits}:' - confirm_deletion_children: Я понимаю, что ВСЕ производные элементы указанных пакетов работ будут рекурсивно удалены. - deletes_children: Все дочерние пакеты работ и их потомки также будут рекурсивно удалены. destroy_time_entry: title: Подтвердите удаление записи времени text: Вы уверены, что хотите удалить следующую запись? diff --git a/config/locales/crowdin/js-rw.yml b/config/locales/crowdin/js-rw.yml index fbb287eec30..f744ef2fc9f 100644 --- a/config/locales/crowdin/js-rw.yml +++ b/config/locales/crowdin/js-rw.yml @@ -515,12 +515,6 @@ rw: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ rw: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ rw: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ rw: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-si.yml b/config/locales/crowdin/js-si.yml index cd0caa7c2b1..36836526eb9 100644 --- a/config/locales/crowdin/js-si.yml +++ b/config/locales/crowdin/js-si.yml @@ -515,12 +515,6 @@ si: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ si: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: අස්ගිනී - responsible: වගවීම - shared: Shared - watched: මුරකරු - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: ඔබ සිටින්නේ එකම පිටුවේ ය. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ si: required_outside_context: 'කරුණාකර සියලු ගුණාංග බැලීමට වැඩ පැකේජය නිර්මාණය කිරීම සඳහා ව්යාපෘතියක් තෝරන්න. ඔබට තෝරා ගත හැක්කේ ඉහත ආකාරයේ සක්රිය කර ඇති ව්යාපෘති පමණි. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: පුවත් එකතු - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: විකි පිටුව එකතු කරන ලදි - wiki_page_updated: විකි පිටුව යාවත්කාලීන කරන ලදි - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: ඔබට විශ්වාසද? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ si: form_submit: title: දිගටම කරගෙන යාමට තහවුරු කරන්න text: ඔබට මෙම ක්රියාව කිරීමට අවශ්ය බව ඔබට විශ්වාසද? - destroy_work_package: - title: "%{label}මකාදැමීම තහවුරු කරන්න" - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'වැඩ පැකේජයට %{childUnits}ඇත:' - confirm_deletion_children: ලැයිස්තුගත වැඩ පැකේජ වලින් පැවත එන සියලුම දෙනා නැවත නැවතත් ඉවත් කරන බව මම පිළිගනිමි. - deletes_children: සියලුම ළමා වැඩ පැකේජ සහ ඔවුන්ගෙන් පැවත එන්නන් ද නැවත නැවතත් මකා දැමෙනු ඇත. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-sk.yml b/config/locales/crowdin/js-sk.yml index af58c4b132c..e2890e09f9b 100644 --- a/config/locales/crowdin/js-sk.yml +++ b/config/locales/crowdin/js-sk.yml @@ -515,12 +515,6 @@ sk: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -594,49 +588,6 @@ sk: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Priradené - responsible: Zodpovedný - shared: Shared - watched: Pozorovateľ - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: Ste na jedinej stránke. pages_skipped: Pages skipped. @@ -660,39 +611,6 @@ sk: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Novinky pridané - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wikistránka bola pridaná - wiki_page_updated: Wikistránka bola aktualizovaná - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Ste si istí? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1065,13 +983,6 @@ sk: form_submit: title: Potvrdiť a pokračovať text: Ste si istý, že chcete vykonať túto akciu? - destroy_work_package: - title: Potvrďte zmazanie %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'Pracovný balíček má %{childUnits}:' - confirm_deletion_children: Potvrdzujem, že všetky podriadené prvky pracovných balíčkov, ktoré sú tu uvedené, budú odstránené rekurzívne. - deletes_children: Všetky podradené pracovné balíčky a s nimi súvisiace budú rekurzívne odstránené. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-sl.yml b/config/locales/crowdin/js-sl.yml index 6c243c3fdd7..0e95dc8ada6 100644 --- a/config/locales/crowdin/js-sl.yml +++ b/config/locales/crowdin/js-sl.yml @@ -517,12 +517,6 @@ sl: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -596,49 +590,6 @@ sl: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Nastavitve obvestil - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Omenjen - description: Receive a notification every time someone mentions me anywhere - assignee: Prevzemnik - responsible: Odgovorni - shared: Shared - watched: Opazovalec - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: 'Ste na edini strani. ' pages_skipped: Pages skipped. @@ -662,39 +613,6 @@ sl: required_outside_context: 'Prosimo, izberite projekt, v katerem boste ustvarili delovni paket, če si želite ogledati vse atribute. Izberete lahko samo projekte, ki so aktivirani zgoraj. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: Dodane novice - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki stran dodana - wiki_page_updated: Wiki stran posodobljena - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Ste prepričani? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1083,13 +1001,6 @@ sl: form_submit: title: Potrdite za nadaljevanje text: Ali ste prepričani, da želite to izvesti? - destroy_work_package: - title: Potrdite izbris %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'Ta delavni paket ima %{childUnits}:' - confirm_deletion_children: Zavedam se, da bodo VSI potomci naštetih delovnih paketov rekurzivno odstranjeni. - deletes_children: Vsi delovni paketi in njihovi potomci bodo tudi rekurzivno izbrisani. destroy_time_entry: title: Potrdite brisanje časovnega vnosa text: Ali ste prepričani, da želite izbrisati naslednji časovni vnos? diff --git a/config/locales/crowdin/js-sr.yml b/config/locales/crowdin/js-sr.yml index 6c3197f8805..eacac083a77 100644 --- a/config/locales/crowdin/js-sr.yml +++ b/config/locales/crowdin/js-sr.yml @@ -515,12 +515,6 @@ sr: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -593,49 +587,6 @@ sr: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Zadužen - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -659,39 +610,6 @@ sr: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1064,13 +982,6 @@ sr: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-sv.yml b/config/locales/crowdin/js-sv.yml index 8c256a434c4..f1cc8a5607e 100644 --- a/config/locales/crowdin/js-sv.yml +++ b/config/locales/crowdin/js-sv.yml @@ -515,12 +515,6 @@ sv: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ sv: settings: change_notification_settings: Du kan ändra dina aviseringsinställningar för att säkerställa att du aldrig missar en viktig uppdatering. title: Inställningar för aviseringar - notify_me: Meddela mig - reminders: - no_notification: Ingen avisering - timeframes: - normal: - PT0S: samma dag - P1D: 1 dag innan - P3D: 3 dagar innan - P7D: en vecka innan - overdue: - P1D: varje dag - P3D: var 3:e dag - P7D: varje vecka - reasons: - mentioned: - title: Omnämnd - description: Få ett meddelande varje gång någon nämner mig var som helst - assignee: Tilldelad till - responsible: Huvudansvarig - shared: Delad - watched: Bevakare - work_package_commented: Alla nya kommentarer - work_package_created: Nya arbetspaket - work_package_processed: Alla statusändringar - work_package_prioritized: Alla prioritetsändringar - work_package_scheduled: Alla datumändringar - global: - immediately: - title: Deltar - description: Aviseringar för alla aktiviteter i arbetspaket som du är involverad i (tilldelad ansvarig, ansvarig eller bevakare). - delayed: - title: Icke-deltagande - description: Ytterligare aviseringar för aktiviteter i alla projekt. - date_alerts: - title: Datumnotiser - description: Automatiska aviseringar när viktiga datum närmar sig för öppna arbetspaket som du är involverad i (tilldelad ansvarig, ansvarig eller bevakare). - overdue: När försenad - project_specific: - title: Projekt-specifika aviseringsinställningar - description: Dessa projektspecifika inställningar åsidosätter standardinställningarna ovan. - add: Lägg till inställning för projekt - already_selected: Detta projekt är redan valt - remove: Ta bort projektinställningar pagination: no_other_page: Du befinner dig på den enda sidan. pages_skipped: Sidor överhoppade. @@ -658,39 +609,6 @@ sv: required_outside_context: 'Välj ett projekt för att skapa arbetspaketet för att se alla attribut. Du kan bara välja projekt som har typen ovan aktiverad. ' - reminders: - settings: - daily: - add_time: Lägg till tid - enable: Aktivera dagliga e-postpåminnelser - explanation: Du kommer att få dessa påminnelser endast för olästa aviseringar och endast vid timmar du anger. %{no_time_zone} - no_time_zone: Tills du konfigurerar en tidszon för ditt konto kommer tiderna att tolkas som att vara i UTC. - time_label: 'Tid %{counter}:' - title: Skicka dagligen e-postpåminnelser för olästa aviseringar - workdays: - title: Få påminnelser via e-post dessa dagar - immediate: - title: Skicka mig en påminnelse via e-post - mentioned: Omedelbart när någon @nämner mig - personal_reminder: Omedelbart när jag får en personlig påminnelse - alerts: - title: E-postmeddelanden för andra artiklar (som inte är arbetspaket) - explanation: 'Meddelanden i dag är begränsade till arbetspaket. Du kan välja att fortsätta ta emot e-postmeddelanden för dessa händelser tills de ingår i meddelanden: - - ' - news_added: Nyheter tillagda - news_commented: Kommentar till en nyhet - document_added: Dokument tillagt - forum_messages: Nya forummeddelanden - wiki_page_added: Wiki-sidan tillagd - wiki_page_updated: Wiki-sidan uppdaterad - membership_added: Medlemskap tillagt - membership_updated: Medlemskap uppdaterat - title: E-postpåminnelser - pause: - label: Pausa tillfälligt dagliga e-postpåminnelser - first_day: Första dagen - last_day: Senaste dagen text_are_you_sure: Är du säker? text_are_you_sure_to_cancel: Du har osparade ändringar på denna sida. Är du säker på att du vill kasta dem? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ sv: form_submit: title: Bekräfta för att fortsätta text: Är du säker på att du vill utföra den här åtgärden? - destroy_work_package: - title: Bekräfta radering av %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'Arbetspaketet har %{childUnits}:' - confirm_deletion_children: Jag bekräftar att allt underliggande till de listade arbetspaketen kommer att tas bort rekursivt. - deletes_children: Alla barnarbetspaket och deras underliggande kommer också att raderas rekursivt. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-th.yml b/config/locales/crowdin/js-th.yml index 8209937a2d5..e2492cae3d4 100644 --- a/config/locales/crowdin/js-th.yml +++ b/config/locales/crowdin/js-th.yml @@ -515,12 +515,6 @@ th: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -591,49 +585,6 @@ th: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: ผู้ได้รับมอบหมาย - responsible: Accountable - shared: Shared - watched: ผู้ดูข้อมูล - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -657,39 +608,6 @@ th: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: เพิ่มข่าวแล้ว - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: เพิ่มหน้าวิกิแล้ว - wiki_page_updated: ปรับปรุงหน้าวิกิแล้ว - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: คุณแน่ใจหรือไม่ ? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1062,13 +980,6 @@ th: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-tr.yml b/config/locales/crowdin/js-tr.yml index 8b417636476..d368508ddd5 100644 --- a/config/locales/crowdin/js-tr.yml +++ b/config/locales/crowdin/js-tr.yml @@ -515,12 +515,6 @@ tr: sidebar_arrow: Projenin ana menüsüne dönmek için sol üst köşedeki dönüş okunu kullanın. welcome: En önemli özellikleri öğrenmek için üç dakikalık bir tanıtım gezintisine çıkın.
Tüm adımları tamamlamanızı öneririz. Turu istediğiniz zaman yeniden başlatabilirsiniz. wiki: "wiki içinde belgeleyebilir ve ekibinizle birlikte bilgi paylaşabilirsiniz." - backlogs: - overview: İşinizi birikimler görünümünde yönetin. - sprints: Sağ tarafta ürün biriktirme listesi ve hata biriktirme listesi var, solda da ilgili sprintler var. Burada epikler, kullanıcı hikayeleri ve buglar oluşturabilir, sürükleyip bırakarak öncelik sırasına koyabilir ve bunları bir sprint'e ekleyebilirsiniz. - task_board_arrow: "Görev panonuzu görmek için Sprint açılır menüsünde açın..." - task_board_select: "... ve görev panosu kaydını seçin." - task_board: Görev panosu, bu sprint için ilerlemeyi görselleştirir. Yeni görevler veya engeller eklemek için bir kullanıcı hikayesinin yanındaki artı (+) simgesine tıklayın.
Sürükle bırak ile durum güncellenebilir. boards: overview: Görünümü değiştirmek ve çevik panolar görünümünü kullanarak projenizi yönetmek için panolar'ı seçin. lists_kanban: Burada panonuzda birden çok liste (sütun) oluşturabilirsiniz. Bu özellik, örneğin bir Kanban panosu oluşturmanıza olanak tanır. @@ -592,49 +586,6 @@ tr: settings: change_notification_settings: Önemli bir güncellemeyi asla kaçırmamak için bildirim ayarlarınızı değiştirebilirsiniz. title: Bildirim ayarları - notify_me: Beni dürt - reminders: - no_notification: Bildirim yok - timeframes: - normal: - PT0S: aynı gün - P1D: 1 gün önce - P3D: 3 gün önce - P7D: 1 hafta önce - overdue: - P1D: her gün - P3D: Her 3 günde bir - P7D: her hafta - reasons: - mentioned: - title: Bahsedilen - description: Birisi herhangi bir yerde benden her bahsettiğinde bir bildirim alın - assignee: Atanan - responsible: Sorumlu - shared: Paylaşıldı - watched: Takip edilen - work_package_commented: Tüm yeni yorumlar - work_package_created: Yeni iş paketleri - work_package_processed: Tüm durum değişiklikleri - work_package_prioritized: Tüm öncelik değişiklikleri - work_package_scheduled: Tüm tarih değişiklikleri - global: - immediately: - title: Katılım - description: Dahil olduğunuz (vekil, sorumlu veya gözlemci) iş paketlerindeki tüm faaliyetler için bildirimler. - delayed: - title: Katılmayan - description: Tüm projelerdeki faaliyetler için ek bildirimler. - date_alerts: - title: Tarih uyarısı - description: Dahil olduğunuz (vekil, sorumlu veya gözlemci) açık iş paketleri için önemli tarihler yaklaştığında otomatik bildirimler. - overdue: Süresi geçtiğinde - project_specific: - title: Projeye özel bildirim ayarları - description: Bu projeye özgü ayarlar, yukarıdaki varsayılan ayarları geçersiz kılar. - add: Proje için ayar ekle - already_selected: Bu proje zaten seçildi - remove: Proje ayarlarını kaldır pagination: no_other_page: Geçerli tek sayfadasınız. pages_skipped: Sayfalar atlandı. @@ -658,39 +609,6 @@ tr: required_outside_context: 'Lütfen tüm özellikleri görmek için çalışma paketini oluşturacak bir proje seçin. Yalnızca yukarıdaki türden etkin olan projeleri seçebilirsiniz. ' - reminders: - settings: - daily: - add_time: Zaman ekle - enable: Günlük email hatırlatıcılarını etkinleştir - explanation: Bu hatırlatıcıları yalnızca okunmamış bildirimler için ve yalnızca sizin belirlediğiniz saatlerde alacaksınız. %{no_time_zone} - no_time_zone: Hesabınız için bir saat dilimi yapılandırana kadar, saatler UTC olarak yorumlanacaktır. - time_label: 'Zaman %{counter}:' - title: Okunmamış bildirimler için bana günlük e-posta hatırlatıcıları gönder - workdays: - title: Bu günlerde e-posta hatırlatıcıları alın - immediate: - title: Bana email hatırlatıcısı gönder - mentioned: Birisi benden @bahsettiğinde hemen - personal_reminder: Kişisel bir hatırlatma aldığımda hemen - alerts: - title: Diğer öğeler için e-posta uyarıları (iş paketleri olmayanlar) - explanation: 'Bildirimler bugün iş paketleri ile sınırlıdır. Bildirimlere eklenene kadar bu etkinlikler için e-posta uyarıları almaya devam etmeyi seçebilirsiniz: - - ' - news_added: Haber eklendiğinde - news_commented: Bir habere yorum yapıldığında - document_added: Belgeler eklendi - forum_messages: Yeni forum iletileri yazıldığında - wiki_page_added: Wiki sayfası eklendiğinde - wiki_page_updated: Wiki sayfası güncellendiğinde - membership_added: Üyelik eklendiğinde - membership_updated: Üyelik güncellendiğinde - title: Email hatırlatıcıları - pause: - label: Email hatırlatıcılarını geçici olarak durdur - first_day: İlk gün - last_day: Son gün text_are_you_sure: Emin misiniz? text_are_you_sure_to_cancel: Bu sayfada kaydedilmemiş değişiklikleriniz var. Bunları atmak istediğinizden emin misiniz? breadcrumb: Gezinti Menüsü @@ -1063,13 +981,6 @@ tr: form_submit: title: Devam etmek için onaylayın text: Bu işlemi gerçekleştirmek istediğinizden emin misiniz? - destroy_work_package: - title: "%{label} için silme işlimini onaylayın" - single_text: Bu iş paketini silmek istediğinize emin misiniz? - bulk_text: Aşağıdaki %{label}'i silmek istediğinizden emin misiniz? - has_children: 'İş paketi %{childUnits} alt paketlerine sahip:' - confirm_deletion_children: Listelenen iş paketlerinin TÜM soylarının tekrar tekrar kaldırılacağını kabul ediyorum. - deletes_children: Tüm çocuk iş paketleri ve torunları da tekrar tekrar silinir. destroy_time_entry: title: Zaman girişinin silinmesini onaylayın text: Aşağıdaki zaman girişini silmek istediğinizden emin misiniz? diff --git a/config/locales/crowdin/js-uk.yml b/config/locales/crowdin/js-uk.yml index 704242f5274..2277c30f7f8 100644 --- a/config/locales/crowdin/js-uk.yml +++ b/config/locales/crowdin/js-uk.yml @@ -515,12 +515,6 @@ uk: sidebar_arrow: Використовуйте стрілку повернення у верхньому лівому куті, щоб повернутися в головне меню проєкту. welcome: Пройдіть трихвилинний вступний тур, щоб дізнатися про найважливіші функції.
Ми рекомендуємо завершити всі етапи до кінця. Ви можете перезапустити тур у будь-який час. wiki: У межах Wiki ви можете документувати знання та обмінюватися ними зі своєю командою. - backlogs: - overview: Керуйте своєю роботою в поданні Backlogs. - sprints: Праворуч у вікні подання можна переглянути невиконані завдання щодо продукту та помилки. Ліворуч можна створювати епіки, історії користувачів і помилки, визначати їх пріоритет перетягуванням і додавати їх до спринтів. - task_board_arrow: Щоб переглянути свою дошку завдань, відкрийте спадне меню спринту… - task_board_select: "... і виберіть запис на дошці завдань." - task_board: Панель завдань візуалізує перебіг цього спринту. Натисніть на знак плюса (+) біля історії користувача, щоб додати нові завдання або перешкоди.
Стан можна оновити перетягуванням. boards: overview: Виберіть дошки, щоб зсунути подання та керувати своїм проєктом за допомогою подання дощок Agile. lists_kanban: Тут можна створити кілька списків (стовпців) у межах дошки. Ця функція дає змогу створити, наприклад, дошку Kanban. @@ -594,49 +588,6 @@ uk: settings: change_notification_settings: Ви можете змінити налаштування сповіщень, щоб ніколи не пропускати важливі оновлення. title: Налаштування сповіщень - notify_me: Сповіщати мене - reminders: - no_notification: Немає сповіщень - timeframes: - normal: - PT0S: того ж дня - P1D: за 1 день - P3D: за 3 дні - P7D: за тиждень - overdue: - P1D: щодня - P3D: кожні 3 дні - P7D: щотижня - reasons: - mentioned: - title: Згадано - description: Отримувати сповіщення щоразу, коли хтось згадує мене де завгодно - assignee: Виконавець - responsible: Відповідальний - shared: Спільні - watched: Спостерігач - work_package_commented: Усі нові коментарі - work_package_created: Нові пакети робіт - work_package_processed: Усі зміни статусу - work_package_prioritized: Усі зміни пріоритету - work_package_scheduled: Усі зміни дат - global: - immediately: - title: Бере участь - description: Сповіщення про всі дії з пакетами робіт, з якими ви пов’язані (виконавець, відповідальний або спостерігач). - delayed: - title: Не бере участі - description: Додаткові сповіщення про дії в усіх проєктах. - date_alerts: - title: Оповіщення про дати - description: Автоматичні сповіщення в разі наближення важливих дат для відкритих пакетів робіт, з якими ви пов’язані (виконавець, відповідальний або спостерігач). - overdue: Якщо прострочено - project_specific: - title: Параметри сповіщень проєкту - description: Ці налаштування сповіщень проєкту перевизначають типові налаштування вище. - add: Додати налаштування для проєкту - already_selected: Цей проєкт уже вибрано - remove: Вилучити налаштування проєкту pagination: no_other_page: Ви знаходитесь на єдиній сторінці. pages_skipped: Сторінки пропущено. @@ -660,39 +611,6 @@ uk: required_outside_context: 'Виберіть проект, щоб створити робочий пакет, щоб побачити всі атрибути. Ви можете вибирати лише проекти, які мають вказаний вище тип активації. ' - reminders: - settings: - daily: - add_time: Додати час - enable: Увімкнути щоденні нагадування електронною поштою - explanation: Ви отримуватимете ці нагадування лише для непрочитаних сповіщень і тільки в указані години. %{no_time_zone} - no_time_zone: Доки ви не налаштуєте часовий пояс для свого облікового запису, вважатиметься, що ваш час указано за стандартом UTC. - time_label: 'Час %{counter}:' - title: Надсилати мені щоденні нагадування електронною поштою про непрочитані сповіщення - workdays: - title: Отримувати нагадування електронною поштою в ці дні - immediate: - title: Надсилати мені нагадування електронною поштою - mentioned: Негайно, коли хтось згадує @мене - personal_reminder: Одразу після отримання особистого нагадування - alerts: - title: Сповіщення електронною поштою для інших елементів (які не є пакетами робіт) - explanation: 'Сьогодні надходитимуть лише сповіщення про пакети робіт. Ви можете й надалі отримувати сповіщення електронною поштою для цих подій, якщо їх не буде включено в сповіщення: - - ' - news_added: Новину додано - news_commented: Коментар щодо новини - document_added: Документи додано - forum_messages: Нові повідомлення форуму - wiki_page_added: Додано сторінку Wiki - wiki_page_updated: Сторінка Wiki оновлена - membership_added: Членство додано - membership_updated: Членство додано - title: Нагадування електронною поштою - pause: - label: Тимчасово призупинити щоденні нагадування електронною поштою - first_day: Перший день - last_day: Останній день text_are_you_sure: Ви впевненені? text_are_you_sure_to_cancel: На цій сторінці є незбережені зміни. Ви дійсно хочете їх скасувати? breadcrumb: Ланцюжок навігації @@ -1065,13 +983,6 @@ uk: form_submit: title: Підтвердьте продовження text: Дійсно виконати цю дію? - destroy_work_package: - title: Підтвердьте видалення %{label} - single_text: Справді видалити цей пакет робіт? - bulk_text: Справді видалити %{label} нижче? - has_children: Робочий пакет має %{childUnits} - confirm_deletion_children: Я визнаю, що ВСІ нащадки перерахованих робочих пакетів будуть рекурсивно видалені. - deletes_children: Всі дочірні робочі пакети та їх нащадки також будуть рекурсивно видалені. destroy_time_entry: title: Підтвердьте видалення запису часу text: Справді видалити наведений нижче запис часу? diff --git a/config/locales/crowdin/js-uz.yml b/config/locales/crowdin/js-uz.yml index ab77db4e91e..6fa739ffc6c 100644 --- a/config/locales/crowdin/js-uz.yml +++ b/config/locales/crowdin/js-uz.yml @@ -515,12 +515,6 @@ uz: sidebar_arrow: Use the return arrow in the top left corner to return to the project’s main menu. welcome: Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time. wiki: Within the wiki you can document and share knowledge together with your team. - backlogs: - overview: Manage your work in the backlogs view. - sprints: On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint. - task_board_arrow: To see your task board, open the sprint drop-down... - task_board_select: "...and select the task board entry." - task_board: The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop. boards: overview: Select boards to shift the view and manage your project using the agile boards view. lists_kanban: Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example. @@ -592,49 +586,6 @@ uz: settings: change_notification_settings: You can modify your notification settings to ensure you never miss an important update. title: Notification settings - notify_me: Notify me - reminders: - no_notification: No notification - timeframes: - normal: - PT0S: same day - P1D: 1 day before - P3D: 3 days before - P7D: a week before - overdue: - P1D: every day - P3D: every 3 days - P7D: every week - reasons: - mentioned: - title: Mentioned - description: Receive a notification every time someone mentions me anywhere - assignee: Assignee - responsible: Accountable - shared: Shared - watched: Watcher - work_package_commented: All new comments - work_package_created: New work packages - work_package_processed: All status changes - work_package_prioritized: All priority changes - work_package_scheduled: All date changes - global: - immediately: - title: Participating - description: Notifications for all activities in work packages you are involved in (assignee, accountable or watcher). - delayed: - title: Non-participating - description: Additional notifications for activities in all projects. - date_alerts: - title: Date alerts - description: Automatic notifications when important dates are approaching for open work packages you are involved in (assignee, accountable or watcher). - overdue: When overdue - project_specific: - title: Project-specific notification settings - description: These project-specific settings override default settings above. - add: Add setting for project - already_selected: This project is already selected - remove: Remove project settings pagination: no_other_page: You are on the only page. pages_skipped: Pages skipped. @@ -658,39 +609,6 @@ uz: required_outside_context: 'Please choose a project to create the work package in to see all attributes. You can only select projects which have the type above activated. ' - reminders: - settings: - daily: - add_time: Add time - enable: Enable daily email reminders - explanation: You will receive these reminders only for unread notifications and only at hours you specify. %{no_time_zone} - no_time_zone: Until you configure a time zone for your account, the times will be interpreted to be in UTC. - time_label: 'Time %{counter}:' - title: Send me daily email reminders for unread notifications - workdays: - title: Receive email reminders on these days - immediate: - title: Send me an email reminder - mentioned: Immediately when someone @mentions me - personal_reminder: Immediately when I receive a personal reminder - alerts: - title: Email alerts for other items (that are not work packages) - explanation: 'Notifications today are limited to work packages. You can choose to continue receiving email alerts for these events until they are included in notifications: - - ' - news_added: News added - news_commented: Comment on a news item - document_added: Documents added - forum_messages: New forum messages - wiki_page_added: Wiki page added - wiki_page_updated: Wiki page updated - membership_added: Membership added - membership_updated: Membership updated - title: Email reminders - pause: - label: Temporarily pause daily email reminders - first_day: First day - last_day: Last day text_are_you_sure: Are you sure? text_are_you_sure_to_cancel: You have unsaved changes on this page. Are you sure you want to discard them? breadcrumb: Breadcrumb @@ -1063,13 +981,6 @@ uz: form_submit: title: Confirm to continue text: Are you sure you want to perform this action? - destroy_work_package: - title: Confirm deletion of %{label} - single_text: Are you sure you want to delete the work package - bulk_text: Are you sure you want to delete the following %{label}? - has_children: 'The work package has %{childUnits}:' - confirm_deletion_children: I acknowledge that ALL descendants of the listed work packages will be recursively removed. - deletes_children: All child work packages and their descendants will also be recursively deleted. destroy_time_entry: title: Confirm deletion of time entry text: Are you sure you want to delete the following time entry? diff --git a/config/locales/crowdin/js-vi.yml b/config/locales/crowdin/js-vi.yml index 1e8f3b6ea59..4e34fc537bc 100644 --- a/config/locales/crowdin/js-vi.yml +++ b/config/locales/crowdin/js-vi.yml @@ -109,7 +109,7 @@ vi: button_save: lưu lại button_settings: cài đặt button_uncheck_all: Bỏ chọn tất cả - button_update: cập nhật + button_update: Cập Nhật button_export-atom: Tải xuống nguyên tử button_generate_pdf: Tạo PDF button_create: Tạo mới @@ -515,12 +515,6 @@ vi: sidebar_arrow: Sử dụng mũi tên quay lại ở góc trên cùng bên trái để quay lại menu chính của dự án. welcome: Hãy tham gia chuyến tham quan giới thiệu dài ba phút để tìm hiểu tính năng quan trọng nhất.
Chúng tôi khuyên bạn nên hoàn thành các bước cho đến hết. Bạn có thể bắt đầu lại chuyến tham quan bất cứ lúc nào. wiki: Trong wiki bạn có thể ghi lại và chia sẻ kiến ​​thức cùng với nhóm của mình. - backlogs: - overview: Quản lý công việc của bạn trong chế độ xem backlogs. - sprints: Ở bên phải, bạn có tồn đọng sản phẩm và tồn đọng lỗi, ở bên trái, bạn có các lần chạy nước rút tương ứng. Tại đây, bạn có thể tạo sử thi, cốt truyện của người dùng và lỗi, ưu tiên bằng cách kéo và thả và thêm chúng vào chạy nước rút. - task_board_arrow: Để xem bảng nhiệm vụ của bạn, hãy mở trình đơn thả xuống chạy nước rút... - task_board_select: "...và chọn mục nhập bảng nhiệm vụ." - task_board: Bảng nhiệm vụ hiển thị tiến trình cho lần chạy nước rút này. Nhấp vào biểu tượng dấu cộng (+) bên cạnh cốt truyện của người dùng để thêm nhiệm vụ hoặc trở ngại mới.
Trạng thái có thể được cập nhật bằng cách kéo và thả. boards: overview: Chọn boards để thay đổi chế độ xem và quản lý dự án của bạn bằng chế độ xem bảng linh hoạt. lists_kanban: 'Tại đây bạn có thể tạo nhiều danh sách (cột) trong bảng của mình. Ví dụ: tính năng này cho phép bạn tạo Kanban board.' @@ -591,49 +585,6 @@ vi: settings: change_notification_settings: Bạn có thể thay đổi cài đặt thông báo của mình để đảm bảo không bỏ lỡ cập nhật quan trọng nào. title: Cài đặt thông báo - notify_me: Thông báo cho tôi - reminders: - no_notification: Không có thông báo - timeframes: - normal: - PT0S: cùng ngày - P1D: 1 ngày trước - P3D: 3 ngày trước - P7D: một tuần trước - overdue: - P1D: mỗi ngày - P3D: cứ sau 3 ngày - P7D: mỗi tuần - reasons: - mentioned: - title: Được đề cập - description: Nhận thông báo mỗi khi ai đó nhắc đến tôi ở bất cứ đâu - assignee: Người được chuyển nhượng - responsible: chịu trách nhiệm - shared: đã chia sẻ - watched: người theo dõi - work_package_commented: Tất cả bình luận mới - work_package_created: Gói công việc mới - work_package_processed: Mọi thay đổi trạng thái - work_package_prioritized: Tất cả các thay đổi ưu tiên - work_package_scheduled: Tất cả các thay đổi ngày - global: - immediately: - title: Tham gia - description: Thông báo cho tất cả các hoạt động trong gói công việc mà bạn tham gia (người được giao, người chịu trách nhiệm hoặc người theo dõi). - delayed: - title: Không tham gia - description: Thông báo bổ sung cho các hoạt động trong tất cả các dự án. - date_alerts: - title: Thông báo ngày - description: Thông báo tự động khi các ngày quan trọng đang đến gần đối với các gói công việc đang mở mà bạn tham gia (người được giao, người chịu trách nhiệm hoặc người theo dõi). - overdue: Khi quá hạn - project_specific: - title: Cài đặt thông báo dành riêng cho dự án - description: Các cài đặt dành riêng cho dự án này sẽ ghi đè các cài đặt mặc định ở trên. - add: Thêm cài đặt cho dự án - already_selected: Dự án này đã được chọn - remove: Xóa cài đặt dự án pagination: no_other_page: Bạn đang trên trang duy nhất. pages_skipped: Các trang bị bỏ qua. @@ -657,39 +608,6 @@ vi: required_outside_context: 'Vui lòng chọn một dự án để tạo gói công việc để xem tất cả các thuộc tính. Bạn chỉ có thể chọn các dự án đã kích hoạt loại trên. ' - reminders: - settings: - daily: - add_time: Thêm thời gian - enable: Bật lời nhắc email hàng ngày - explanation: Bạn sẽ chỉ nhận được những lời nhắc này đối với những thông báo chưa đọc và chỉ vào những giờ bạn chỉ định. %{no_time_zone} - no_time_zone: Cho đến khi bạn định cấu hình múi giờ cho tài khoản của mình, thời gian sẽ được hiểu là theo UTC. - time_label: 'Thời gian %{counter}:' - title: Gửi cho tôi lời nhắc email hàng ngày về các thông báo chưa đọc - workdays: - title: Nhận lời nhắc qua email vào những ngày này - immediate: - title: Gửi cho tôi lời nhắc qua email - mentioned: Ngay lập tức khi ai đó @đề cập đến tôi - personal_reminder: Ngay lập tức khi tôi nhận được lời nhắc cá nhân - alerts: - title: Thông báo qua email cho các mục khác (không phải là gói công việc) - explanation: 'Thông báo ngày hôm nay được giới hạn trong các gói công việc. Bạn có thể chọn tiếp tục nhận thông báo qua email cho những sự kiện này cho đến khi chúng được đưa vào thông báo: - - ' - news_added: Đã thêm tin tức - news_commented: Bình luận về một tin tức - document_added: Đã thêm tài liệu - forum_messages: Thông báo diễn đàn mới - wiki_page_added: Đã thêm trang Wiki - wiki_page_updated: Trang Wiki được cập nhật - membership_added: Đã thêm thành viên - membership_updated: Đã cập nhật tư cách thành viên - title: Lời nhắc qua email - pause: - label: Tạm dừng nhắc nhở email hàng ngày - first_day: Ngày đầu tiên - last_day: Ngày cuối cùng text_are_you_sure: Bạn có chắc không? text_are_you_sure_to_cancel: Bạn có những thay đổi chưa được lưu trên trang này. Bạn có chắc chắn muốn loại bỏ chúng không? breadcrumb: vụn bánh mì @@ -1062,13 +980,6 @@ vi: form_submit: title: Xác nhận để tiếp tục text: Bạn có thực sự muốn thực hiện thao tác này? - destroy_work_package: - title: Xác nhận xóa %{label} - single_text: Bạn có chắc chắn muốn xóa gói công việc - bulk_text: Bạn có chắc chắn muốn xóa %{label} sau đây không? - has_children: 'Gói công việc có %{childUnits}:' - confirm_deletion_children: Tôi xác nhận rằng TẤT CẢ các gói công việc kế thừa được liệt kê sẽ bị xóa đệ quy. - deletes_children: Tất cả các gói công việc con và con cháu của chúng cũng sẽ bị xóa đệ quy. destroy_time_entry: title: Xác nhận xóa mục nhập thời gian text: Bạn có chắc chắn muốn xóa mục nhập thời gian sau đây không? diff --git a/config/locales/crowdin/js-zh-CN.yml b/config/locales/crowdin/js-zh-CN.yml index 39814fd5b41..32f29d7f603 100644 --- a/config/locales/crowdin/js-zh-CN.yml +++ b/config/locales/crowdin/js-zh-CN.yml @@ -515,12 +515,6 @@ zh-CN: sidebar_arrow: 使用左上角的返回箭头可返回到项目的主菜单。 welcome: 通过三分钟的导览了解最重要的功能
我们建议您完成所有步骤。您可以随时重新浏览介绍。 wiki: 在 维基 中,您可以记录并与您的团队共享知识。 - backlogs: - overview: 在待办清单视图中管理您的工作。 - sprints: 右侧为产品待办清单和缺陷待办清单,左侧为各自的冲刺 (Sprint)。在这里,您可以创建长篇故事、用户故事和错误,通过拖放来确定优先级,以及将它们添加到冲刺 (sprint) 中。 - task_board_arrow: 要查看您的任务面板,请打开冲刺 (sprint) 下拉菜单… - task_board_select: "…并选择任务面板条目。" - task_board: 任务面板可以将此冲刺 (sprint) 的进度可视化。点击用户故事旁边的加号 (+) 图标可添加新任务或障碍。
状态可以通过拖放更新。 boards: overview: 选择面板以切换视图并使用敏捷面板视图管理您的项目。 lists_kanban: 在此,您可以在面板中创建多个列表(列)。例如,此功使让您可以创建看板面板。 @@ -591,51 +585,6 @@ zh-CN: settings: change_notification_settings: 您可以修改您的通知设置,以确保您不会错过重要的更新。 title: 通知设置 - notify_me: 通知我 - reminders: - no_notification: 无通知 - timeframes: - normal: - PT0S: 当天 - P1D: 提前 1 天 - P3D: 提前 3 天 - P7D: 提前一周 - overdue: - P1D: 每天 - P3D: 每 3 天 - P7D: 每周 - reasons: - mentioned: - title: 被提及 - description: 每当有人在任何位置提及我时都接收通知 - assignee: 受理人 - responsible: 负责人 - shared: 共享 - watched: 关注者 - work_package_commented: 所有新评论 - work_package_created: 新工作包 - work_package_processed: 所有状态更改 - work_package_prioritized: 所有优先级更改 - work_package_scheduled: 所有日期更改 - global: - immediately: - title: 参与 - description: 对于你参与的所有工作包(受理人、负责人或关注者),可以设置通知以接收所有活动的提醒。 - delayed: - title: 不参与 - description: 在所有项目中通知您所有活动。 - date_alerts: - title: 日期提醒 - description: 当你参与的开放工作包(作为受理人、负责人或关注者)的重要日期临近时,自动发送通知。 - overdue: 逾期时 - project_specific: - title: 项目特定通知设置 - description: '这些特定于项目的设置会覆盖上面的默认设置。 - - ' - add: 为项目添加设置 - already_selected: 此项目已经选定 - remove: 移除项目设置 pagination: no_other_page: 您位于唯一页面上。 pages_skipped: 跳过的页面。 @@ -659,39 +608,6 @@ zh-CN: required_outside_context: '请选择要创建工作包的项目以查看所有属性。 您只能选择已激活上述类型的项目。 ' - reminders: - settings: - daily: - add_time: 添加时间 - enable: 启用每日电子邮件提醒 - explanation: 只有存在未读通知时,您才会在指定的时间收到这些提醒。%{no_time_zone} - no_time_zone: 除非您为帐户配置时区,否则将以 UTC 显示时间。 - time_label: 时间 %{counter}: - title: 向我发送针对未读通知的每日电子邮件提醒 - workdays: - title: 在如下日期接收电子邮件提醒 - immediate: - title: 向我发送电子邮件提醒 - mentioned: 当有人@提及我时立即提醒 - personal_reminder: 收到个人提醒后立即发送 - alerts: - title: 其他项目(非工作包)的电子邮件提醒 - explanation: '今日提醒仅限于工作包。您可以选择继续接收这些事件的电子邮件提醒,直到它们被包含在通知中。 - - ' - news_added: 添加新闻 - news_commented: 评论新闻 - document_added: 添加文档 - forum_messages: 新的论坛消息 - wiki_page_added: 添加维基页面 - wiki_page_updated: 更新维基页面 - membership_added: 添加成员 - membership_updated: 更新成员 - title: 电子邮件提醒 - pause: - label: 临时暂停每日电子邮件提醒 - first_day: 第一天 - last_day: 最后一天 text_are_you_sure: 是否确定? text_are_you_sure_to_cancel: 您在此页面上有未保存的更改。您确定要放弃这些更改吗? breadcrumb: 面包屑导航 @@ -1064,13 +980,6 @@ zh-CN: form_submit: title: 确认已继续 text: 是否确定想要执行此操作? - destroy_work_package: - title: 确认删除%{label} - single_text: 确定要删除此工作包吗? - bulk_text: 确定要删除以下%{label}吗? - has_children: '工作包有 %{childUnits}:' - confirm_deletion_children: 我确认所列工作包的全部子工作包都将递归移除。 - deletes_children: 所有子工作包及其子级也将递归删除。 destroy_time_entry: title: 确认删除时间条目 text: 确定要删除以下时间条目? diff --git a/config/locales/crowdin/js-zh-TW.yml b/config/locales/crowdin/js-zh-TW.yml index 20f77ec0357..58d066fc883 100644 --- a/config/locales/crowdin/js-zh-TW.yml +++ b/config/locales/crowdin/js-zh-TW.yml @@ -515,12 +515,6 @@ zh-TW: sidebar_arrow: 用左上方的返回鍵,返回至專案的 主選單. welcome: 觀看三分鐘的介紹導覽, 以瞭解最多 重要功能
我們建議完成所有步驟直到結束。您可以隨時重新開始導覽。 wiki: 在 wiki 中, 您可以與您的團隊一起記錄和分享知識。 - backlogs: - overview: " 在backlogs 檢視中,管理您的工作。" - sprints: 在待辦工作版面中管理您的工作。 在右邊, 你有專案待辦或錯誤紀錄, 在左邊, 你將有各自的衝刺。在這裡, 您可以創建 史詩、使用者故事和錯誤, 通過拖曳進行優先排序, 並將它們添加到衝刺中。 - task_board_arrow: 要查看您的任務面板,請打開衝刺(sprint)下拉選單… - task_board_select: "…並選擇任務面板條目。" - task_board: 任務面板可以將此衝刺 (sprint) 的進度可視化。點擊用戶故事旁邊的加號 (+) 圖標可添加新任務或障礙。
狀態可以通過拖放更新。 boards: overview: 選擇面板以切換視圖並使用敏捷面板視圖管理您的項目。 lists_kanban: 在此,您可以在面板中創建多個列表(列)。例如,此功使讓您可以創建看板面板。 @@ -591,51 +585,6 @@ zh-TW: settings: change_notification_settings: 您可以修改 通知設定,以確保您不會錯過重要更新。 title: 通知設定 - notify_me: 通知我 - reminders: - no_notification: 无通知 - timeframes: - normal: - PT0S: 當日 - P1D: 一天前 - P3D: 3 天前 - P7D: 1 週前 - overdue: - P1D: 每天 - P3D: 每 3 天 - P7D: 每週 - reasons: - mentioned: - title: 被提及 - description: 每當有人提及我時都收到通知 - assignee: 執行者 - responsible: 負責人 - shared: 參與 - watched: '監看者 - - ' - work_package_commented: 有新留言 - work_package_created: 新增工作套件 - work_package_processed: 所有狀態改變 - work_package_prioritized: 優先順序已變更 - work_package_scheduled: 所有日期變更 - global: - immediately: - title: 參與中 - description: 你參與的工作套件中所有活動的通知(執行者、負責人或監看者)。 - delayed: - title: 未參與之工作套件 - description: 額外通知 - date_alerts: - title: 日期提醒 - description: 當「執行者、負責人或監看者」之開啟的工作套件,其重要日期臨近時,自動發出通知。 - overdue: 逾期 - project_specific: - title: 特定專案通知設定 - description: 特別專案通知設定將覆蓋上列標準設定。 - add: 新增設定 - already_selected: 已選取此專案 - remove: 移除專案設定 pagination: no_other_page: 您位於唯一頁面上。 pages_skipped: 跳過的頁面。 @@ -659,39 +608,6 @@ zh-TW: required_outside_context: '請選擇專案以便新增工作套件以及檢視屬性。您只能選擇啟用中的專案。 ' - reminders: - settings: - daily: - add_time: 加入時間 - enable: 啟用每日電子郵件提醒 - explanation: 只有當未讀取通知時,您才會在指定的時間收到這些提醒。%{no_time_zone} - no_time_zone: 除非您為帳戶配置時區,否則將以 UTC 顯示時間。 - time_label: '時間 %{counter}:' - title: 每日將尚未讀取之通知,寄提醒郵件給我 - workdays: - title: 在以下日期收到電子郵件提醒 - immediate: - title: 寄發提醒郵件給我 - mentioned: 當有人使用「@帳號」提到我,系統立即發信通知我 - personal_reminder: 當我收到個人提醒時立即通知 - alerts: - title: 其他非工作套件之提醒 - explanation: '目前的通知僅限於工作套件。您可以選擇在這些事件納入通知系統之前,繼續透過電子郵件接收提醒: - - ' - news_added: 有最新消息 - news_commented: 最新消息有留言 - document_added: 文件已新增 - forum_messages: 有新的討論區訊息 - wiki_page_added: 新增維基頁面 - wiki_page_updated: 維基頁面有更新 - membership_added: 成員加入 - membership_updated: 成員更新 - title: 電子郵件提醒 - pause: - label: 暫時暫停每日電子郵件提醒 - first_day: 第一天 - last_day: 最後一天 text_are_you_sure: 是否確定? text_are_you_sure_to_cancel: 您在此頁面上有未儲存的變更。您確定要捨棄它們嗎? breadcrumb: 導覽軌跡 @@ -1064,13 +980,6 @@ zh-TW: form_submit: title: 確認以繼續 text: 確定要執行此操作嗎? - destroy_work_package: - title: 確認刪除 %{label} - single_text: 確定要刪除此工作套件嗎 - bulk_text: 確定要刪除以下%{label} 嗎? - has_children: 此工作套件包含 %{childUnits}: - confirm_deletion_children: 我確認列出的工作套件的所有細項全部都將被刪除。 - deletes_children: 所有子工作套件及相關套件也全部將被刪除。 destroy_time_entry: title: 確認刪除時間條目 text: 確定要刪除以下時間條目? diff --git a/config/locales/crowdin/ka.yml b/config/locales/crowdin/ka.yml index 67cbfcc0c23..05cf54d7ce5 100644 --- a/config/locales/crowdin/ka.yml +++ b/config/locales/crowdin/ka.yml @@ -114,7 +114,7 @@ ka: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ka: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ka: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ka: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ ka: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ ka: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ ka: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ ka: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ ka: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ ka: identity_url: იდენტიფიკაციის URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ ka: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ ka: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ ka: avatar: ავატარი base: 'ზოგადი შეცდომა:' body: Body - blocks_ids: IDs of blocked work packages category: კატეგორია comment: კომენტარი comments: კომენტარი @@ -2922,6 +2977,7 @@ ka: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: შეუზღუდავი already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ ka: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ ka: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: დამსაქმებელი @@ -3484,6 +3611,7 @@ ka: invalid_filter: Invalid notification filter label_accessibility: წვდომადობა label_account: ანგარიში + label_actions: ქმედებები label_active: აქტიური label_activate_user: მომხმარებლის გააქტიურება label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ ka: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: უახლესი label_ical_access_key_revoke: გაუქმება + label_integrations: Integrations label_add_column: Add column label_applied_status: გადატარებულია სტატუსი label_archive_project: Archive project @@ -3774,7 +3903,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: ბოლო აქტივობა @@ -3812,6 +3941,10 @@ ka: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: მომხმარებლის favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: გასვლა label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: გვერდითი მენიუ @@ -4884,6 +5017,7 @@ ka: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ ka: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/kk.yml b/config/locales/crowdin/kk.yml index c7aef34428b..b0a96f521e5 100644 --- a/config/locales/crowdin/kk.yml +++ b/config/locales/crowdin/kk.yml @@ -114,7 +114,7 @@ kk: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ kk: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ kk: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ kk: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ kk: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ kk: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ kk: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ kk: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ kk: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ kk: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ kk: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ kk: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ kk: avatar: Аватар base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ kk: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ kk: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ kk: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ kk: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ kk: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ kk: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ kk: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ kk: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ko.yml b/config/locales/crowdin/ko.yml index dc14bb7c0ef..4009e1e8f30 100644 --- a/config/locales/crowdin/ko.yml +++ b/config/locales/crowdin/ko.yml @@ -114,7 +114,7 @@ ko: import: title: 가져오기 jira: - title: Jira 가져오기 + title: Jira Migrator description: 이 도구를 사용하여 Jira 인스턴스에서 데이터를 가져옵니다. 여러 Jira 호스트를 구성하고 가져오기 실행 각각에서 가져올 항목을 선택할 수 있습니다. errors: cannot_delete_with_imports: 기존 가져오기가 있는 Jira 호스트는 삭제할 수 없습니다 @@ -125,8 +125,8 @@ ko: title: Jira 구성 new: 새 구성 banner: - title: 제한된 가져오기 - description: 이 가져오기 도구는 현재 베타 버전이며 프로젝트, 이슈(이름, 제목, 설명, 첨부 파일), 사용자(이름, 이메일, 프로젝트 멤버십), 상태 및 유형과 같은 기본 데이터만 가져올 수 있습니다. 워크플로, 사용자 지정 필드, 이슈 관계 또는 권한은 가져올 수 없습니다. Jira Server/Data Center 버전 10.x 및 11.x만 현재 지원합니다. 클라우드 인스턴스는 현재 지원되지 않습니다. + title: 제한된 가져오기 기능 + description: 이 Jira Migrator는 현재 베타 버전이며 프로젝트, 이슈(이름, 제목, 설명, 첨부 파일), 사용자(이름, 이메일, 프로젝트 멤버십), 상태 및 유형과 같은 기본 데이터만 가져올 수 있습니다. 워크플로, 사용자 지정 필드, 이슈 관계 또는 권한은 가져올 수 없습니다. Jira Server/Data Center 버전 10.x 및 11.x만 현재 지원됩니다. 클라우드 인스턴스는 현재 지원되지 않습니다. form: fields: name: 이름 @@ -154,6 +154,7 @@ ko: connection_timeout: 'Jira 서버에 연결이 시간 초과되었습니다: %{message}' parse_error: 'Jira API 응답을 구문 분석하지 못했습니다: %{message}' api_error: Jira API가 오류 상태 %{status}을(를) 반환했습니다 + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: 프로젝트 last_change: 마지막 변경 @@ -162,7 +163,7 @@ ko: run: title: 가져오기 실행 history: 기록 - remove_error: Jira 가져오기는 실행되는 동안 제거할 수 없습니다 + remove_error: 실행되는 동안에는 Jira 가져오기 실행을 제거할 수 없습니다 import_blocked_error: 다른 Jira 가져오기 실행이 현재 진행 중이거나 검토를 기다리는 중입니다. 새 가져오기를 시작하기 전에 완료하거나 되돌리세요. project_identifier_taken: '이미 사용된 식별자가 있는 프로젝트를 가져오려고 합니다: %{taken_identifier}. Jira에서 프로젝트 식별자를 업데이트한 후에 ''다시 시도''를 클릭하세요.' blank: @@ -381,19 +382,23 @@ ko: notification_text_default: "

안녕하세요,

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

감사합니다.

\n" work_packages_identifier: page_header: - description: 기본 숫자 작업 패키지 ID 또는 작업 패키지 ID 앞에 프로젝트 식별자를 붙이는 프로젝트별 ID 중에서 선택합니다. + description: 클래식 숫자 작업 패키지 ID 또는 작업 패키지 ID 앞에 프로젝트 식별자를 붙이는 프로젝트별 시맨틱 ID 중에서 선택합니다. banner: - existing_identifiers_notice: "%{project_count}개 프로젝트의 기존 식별자가 프로젝트 기반 영숫자 식별자에 대한 요구 사항을 충족하지 않습니다. OpenProject는 아래 예와 같이 해당 식별자가 유효하도록 자동으로 업데이트할 수 있습니다. '자동 수정 및 저장'을 클릭하여 이러한 방식으로 모든 프로젝트의 식별자를 업데이트하고 프로젝트 기반 영숫자 식별자를 활성화하세요.\n" + existing_identifiers_notice: "%{project_count}개 프로젝트의 기존 식별자가 프로젝트 기반 시맨틱 식별자에 대한 요구 사항을 충족하지 않습니다. OpenProject는 아래 예와 같이 해당 식별자가 유효하도록 자동으로 업데이트할 수 있습니다. '자동 수정 및 저장'을 클릭하여 이러한 방식으로 모든 프로젝트의 식별자를 업데이트하고 프로젝트 기반 시맨틱 식별자를 활성화하세요.\n" box_header: label_project: 프로젝트 label_previous_identifier: 이전 식별자 label_autofixed_suggestion: 향후 식별자 label_example_work_package_id: 작업 패키지 ID 예 autofix_preview: - error_too_long: 5자 미만이어야 합니다 + error_too_long: 10자 이하여야 함 + error_numerical: 완전히 숫자로만 지정할 수 없음 + error_starts_with_number: 숫자로 시작할 수 없음 error_special_characters: 특수 문자는 허용되지 않음 + error_not_fully_uppercased: 대문자여야 함 error_in_use: 이미 다른 프로젝트의 활성 핸들로 사용 중입니다 error_reserved: 다른 프로젝트의 핸들 기록에 의해 예약되었습니다 + error_unknown: 수동 검토가 필요합니다 remaining_projects: other: "... %{count}개의 추가 프로젝트" button_autofix: 자동 수정 및 저장 @@ -407,7 +412,7 @@ ko: checkbox_label: 이렇게 하면 모든 작업 패키지 ID가 영구적으로 변경됨을 이해합니다 success_banner: 작업 패키지 식별자 형식을 업데이트했습니다. in_progress: - banner_message: 프로젝트 기반 영숫자 식별자로 프로젝트 식별자를 현재 업데이트하는 중입니다. 시간이 다소 걸릴 수 있습니다. + banner_message: 프로젝트 기반 시맨틱 식별자로 프로젝트 식별자를 현재 업데이트하는 중입니다. 시간이 다소 걸릴 수 있습니다. workflows: tabs: default_transitions: 기본 전환 @@ -672,6 +677,37 @@ ko: danger_dialog: confirmation_live_message_checked: 진행 버튼이 이제 활성화되었습니다. confirmation_live_message_unchecked: 진행 버튼이 이제 비활성화되었습니다. 계속하려면 확인란을 선택해야 합니다. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: 페이지 매김 prev: 이전 @@ -1322,6 +1358,20 @@ ko: index: no_results_title_text: 작업 흐름이 없습니다. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1516,6 +1566,9 @@ ko: dependencies: 종속성 activerecord: attributes: + work_package_semantic_alias: + identifier: 식별자 + work_package: 작업 패키지 jira_import: projects: 프로젝트 import/jira: @@ -1524,7 +1577,7 @@ ko: personal_access_token: 개인 액세스 토큰 import/jira_open_project_reference: jira: Jira - jira_import: Jira 가져오기 + jira_import: Jira Migrator announcements: show_until: 표시 기한 attachment: @@ -1780,6 +1833,7 @@ ko: identity_url: ID URL parent: 부모 그룹 organizational_unit: 조직 단위 + group_users: Group users group_detail: parent: 부모 그룹 organizational_unit: 조직 단위 @@ -1897,6 +1951,7 @@ ko: confirmation: "%{attribute} 속성에 부합하지 않습니다." could_not_be_copied: "%{dependency}을(를) (완전히) 복사할 수 없습니다." does_not_exist: 존재하지 않음 + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action}은(는) OpenProject Enterprise Edition에서만 사용할 수 있습니다." error_unauthorized: 액세스하지 못할 수 있습니다. error_readonly: "- 쓰려고 했지만 쓸 수 없습니다." @@ -1963,6 +2018,7 @@ ko: attributes: parent_id: circular_dependency: "- 순환 그룹 계층을 생성합니다." + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2423,7 +2479,6 @@ ko: avatar: 아바타 base: '일반 오류:' body: 본문 - blocks_ids: 차단 된 작업 패키지의 ID category: 카테고리 comment: 코멘트 comments: 코멘트 @@ -2880,6 +2935,7 @@ ko: title: Enterprise 추가 기능 plan_title: Enterprise %{plan} 추가 기능 plan_name: "%{plan} Enterprise 플랜" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: "%{plan_name}부터 사용 가능합니다." unlimited: 무제한 already_have_token: '이미 토큰이 있으신가요? 아래 버튼을 사용하여 토큰을 추가하고 예약한 Enterprise 플랜으로 업그레이드하세요. @@ -3348,6 +3404,11 @@ ko: quick_add: label: 추가… my_account: + notifications_and_email: + title: 알림 및 이메일 + tabs: + notifications: 알림 설정 + email_reminders: 이메일 미리 알림 access_tokens: description: 공급자 토큰은 OpenProject에서 발급하며, 다른 애플리케이션이 액세스하도록 허용합니다. 클라이언트 토큰은 다른 애플리케이션에서 발급하며, OpenProject가 액세스하도록 허용합니다. no_results: @@ -3410,6 +3471,72 @@ ko: disabled_text: 관리자가 RSS 토큰을 활성화하지 않았습니다. 이 기능을 사용하려면 관리자에게 문의하세요. storages: unknown_storage: 알 수 없는 저장소 + email_reminders: + immediate_reminders: + title: 이메일 미리 알림 보내기 + mentioned: 내가 멘션되면 알림 보내기 + personal_reminder: 개인 미리 알림 보내기 + daily_reminders: + title: 읽지 않은 알림에 대한 일일 이메일 미리 알림 보내기 + caption: 읽지 않은 알림에 대해서만 그리고 사용자가 지정한 시간에만 이러한 미리 알림이 전송됩니다. 계정의 표준 시간대를 구성할 때까지, 시간은 UTC로 적용됩니다. + enabled: 일일 이메일 미리 알림 활성화 + add_time: 시간 추가 + remove_time: 시간 제거 + time_slot_label: 미리 알림 시간(UTC) + workdays: + title: 해당 요일에 이메일 미리 알림 받기 + submit_button: 미리 알림 요일 업데이트 + pause_reminders: + title: 이메일 알림 일시 중지 + enabled: 임시로 일일 이메일 미리 알림 일시 중지 + date_range: 일시 중지 기간 + email_alerts: + title: 작업 패키지가 아닌 기타 항목에 대한 이메일 알림 + news_added: 뉴스 추가됨 + news_commented: 뉴스 항목의 코멘트 + document_added: 문서 추가됨 + forum_messages: 포럼 메시지 게시됨 + wiki_page_added: 위키 페이지 추가됨 + wiki_page_updated: 위키 페이지 업데이트됨 + membership_added: 멤버십 추가됨 + membership_updated: 멤버십 업데이트됨 + submit_button: 알림 업데이트 + notifications: + participating: + title: 참여 + submit_button: 기본 설정 업데이트 + mentioned: 멘션됨 + watched: 지켜보는 중 + assignee: 담당자 + responsible: 담당 + shared: 나와 공유됨 + date_alerts: + title: 날짜 알림 + submit_button: 날짜 알림 업데이트 + start_date: 시작 날짜 + due_date: 완료 날짜 + overdue: 기한 지남 + times: + same_day: 같은 날 + one_day_before: 1일 전 + three_days_before: 3일 전 + seven_days_before: 7일 전 + one_day_after: 1일 후 + three_days_after: 3일 후 + seven_days_after: 7일 후 + non_participating: + title: 참여하지 않음 + submit_button: 기본 설정 업데이트 + work_package_created: 새 작업 패키지 + work_package_commented: 모든 새로운 코멘트 + work_package_processed: 모든 상태 변경 사항 + work_package_prioritized: 모든 우선 순위 변경 사항 + work_package_scheduled: 모든 날짜 변경 사항 + project_specific_settings: + title: 프로젝트별 알림 설정 + add_button: 프로젝트별 알림 추가 + dialog_title: 프로젝트별 알림 추가 + list_header: 특정 알림이 있는 프로젝트 notifications: reasons: assigned: 담당자 @@ -3441,6 +3568,7 @@ ko: invalid_filter: 잘못된 알림 필터 label_accessibility: 접근성 label_account: 계정 + label_actions: 작업 label_active: 활성 label_activate_user: 사용자 활성화 label_active_in_new_projects: 새로운 프로젝트에서 활성 @@ -3481,6 +3609,7 @@ ko: label_ical_access_key_generation_hint: 캘린더 구독 시 자동으로 생성됩니다. label_ical_access_key_latest: 최신 label_ical_access_key_revoke: 취소 + label_integrations: 통합 label_add_column: 열 추가 label_applied_status: 적용된 상태 label_archive_project: 프로젝트 아카이브하기 @@ -3731,7 +3860,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: 마지막 활동 @@ -3769,6 +3898,10 @@ ko: label_custom_pdf_export_settings: 사용자 지정 PDF 내보내기 설정 label_custom_favicon: 사용자 지정 favicon label_custom_touch_icon: 사용자 지정 터치 아이콘 + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: 로그아웃 label_mapping_for: '매핑 대상: %{attribute}' label_main_menu: 측면 메뉴 @@ -4833,6 +4966,7 @@ ko: ' setting_app_subtitle: 응용 프로그램 부제목 setting_app_title: 응용 프로그램 제목 + setting_organization_name: Organization name setting_attachment_max_size: 첨부 파일 최대 크기 setting_show_work_package_attachments: 기본적으로 파일 탭에 첨부 파일 표시 setting_antivirus_scan_mode: 스캔 모드 @@ -4977,12 +5111,12 @@ ko: setting_welcome_text: 환영 블록 텍스트 setting_welcome_title: 환영 블록 제목 setting_welcome_on_homescreen: 홈 화면에 환영 블록 표시 - setting_work_packages_identifier_numeric: 인스턴스 전체의 숫자 시퀀스(기본값) - setting_work_packages_identifier_numeric_caption: '모든 작업 패키지는 1로 시작하는 순차 번호가 있으며, 새로운 패키지마다 순차 번호가 증가합니다. 이 번호는 해당 인스턴스 내에서 고유하므로 작업 패키지가 프로젝트 간에 이동되더라도 동일하게 유지됩니다. + setting_work_packages_identifier_classic: 인스턴스 전체의 숫자 시퀀스(기본값) + setting_work_packages_identifier_classic_caption: '모든 작업 패키지는 1로 시작하는 순차 번호가 있으며, 새로운 패키지마다 순차 번호가 증가합니다. 이 번호는 해당 인스턴스 내에서 고유하므로 작업 패키지가 프로젝트 간에 이동되더라도 동일하게 유지됩니다. ' - setting_work_packages_identifier_alphanumeric: 프로젝트 기반 영숫자 식별자 - setting_work_packages_identifier_alphanumeric_caption: '모든 프로젝트에는 작업 패키지 ID 앞에 붙는 고유 식별자가 있습니다. 작업 패키지가 다른 프로젝트로 이동된 경우 새 식별자가 생성되지만 이전 식별자도 계속 작동됩니다. + setting_work_packages_identifier_semantic: 프로젝트 기반 시맨틱 식별자 + setting_work_packages_identifier_semantic_caption: '모든 프로젝트에는 작업 패키지 ID 앞에 붙는 고유 식별자가 있습니다. 작업 패키지가 다른 프로젝트로 이동된 경우 새 식별자가 생성되지만 이전 식별자도 계속 작동됩니다. ' setting_work_package_list_default_highlighting_mode: 기본 강조 표시 모드 diff --git a/config/locales/crowdin/lt.yml b/config/locales/crowdin/lt.yml index 87167a5ac92..e6240ff1708 100644 --- a/config/locales/crowdin/lt.yml +++ b/config/locales/crowdin/lt.yml @@ -114,7 +114,7 @@ lt: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ lt: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ lt: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ lt: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +400,9 @@ lt: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +411,14 @@ lt: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -430,7 +435,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 @@ -697,6 +702,37 @@ lt: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1374,6 +1410,20 @@ lt: index: no_results_title_text: Šiuo metu darbo sekų. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1571,6 +1621,9 @@ lt: dependencies: Priklausomybės activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1579,7 +1632,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: @@ -1835,6 +1888,7 @@ lt: identity_url: Identiteto URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1952,6 +2006,7 @@ lt: confirmation: nesutampa su %{attribute}. could_not_be_copied: "%{dependency} negali (pilnai) būti nukopijuota(s)." does_not_exist: neegzistuoja. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: negali būti pasiektas. error_readonly: bandytas įrašyti, bet įrašyti nebuvo galima. @@ -2018,6 +2073,7 @@ lt: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2540,7 +2596,6 @@ lt: avatar: Nuotrauka base: 'Bendroji klaida:' body: Body - blocks_ids: Blokuotų darbų paketų ID reikšmės category: Kategorija comment: Komentaras comments: Komentaras @@ -3037,6 +3092,7 @@ lt: title: Enterprise priedas plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Neribota already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3508,6 +3564,11 @@ lt: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3570,6 +3631,72 @@ lt: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Nežinoma saugykla + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Paskirtas @@ -3601,6 +3728,7 @@ lt: invalid_filter: Invalid notification filter label_accessibility: Prieinamumas label_account: Paskyra + label_actions: Veiksmai label_active: Aktyvus label_activate_user: Aktyvus vartotojas label_active_in_new_projects: Aktyvus naujuose projektuose @@ -3641,6 +3769,7 @@ lt: label_ical_access_key_generation_hint: Automatiškai sukurta prenumeruojant kalendorių. label_ical_access_key_latest: vėliausias label_ical_access_key_revoke: Atšaukti + label_integrations: Integrations label_add_column: Add column label_applied_status: Taikomoji būsena label_archive_project: Archyvuoti projektą @@ -3891,7 +4020,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 @@ -3929,6 +4058,10 @@ lt: label_custom_pdf_export_settings: Savi PDF eksporto nustatymai label_custom_favicon: Pasirinktinė piktograma label_custom_touch_icon: Pasirinktinė prisilietimo piktograma + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Atsijungti label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Šoninis meniu @@ -5007,6 +5140,7 @@ lt: ' setting_app_subtitle: Programos paantraštė setting_app_title: Programos pavadinimas + setting_organization_name: Organization name setting_attachment_max_size: Didžiausias prisegamo failo dydis setting_show_work_package_attachments: Pagal nutylėjimą rodyti priedus failų kortelėje setting_antivirus_scan_mode: Skenavimo režimas @@ -5153,12 +5287,12 @@ lt: setting_welcome_text: Pasisveikinimo bloko tekstas setting_welcome_title: Pasisveikinimo bloko pavadinimas setting_welcome_on_homescreen: Rodyti pasisveikinimo bloką namų lange - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Numatytasis paryškinimo būdas diff --git a/config/locales/crowdin/lv.yml b/config/locales/crowdin/lv.yml index f9cbd4abdeb..5000c09b4ea 100644 --- a/config/locales/crowdin/lv.yml +++ b/config/locales/crowdin/lv.yml @@ -114,7 +114,7 @@ lv: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ lv: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ lv: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ lv: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +394,9 @@ lv: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +405,14 @@ lv: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: zero: "... %{count} more projects" one: "... 1 more project" @@ -423,7 +428,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 @@ -690,6 +695,37 @@ lv: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1358,6 +1394,20 @@ lv: index: no_results_title_text: Pašlaik nav neviena darbuplūsma. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1554,6 +1604,9 @@ lv: dependencies: Saistītie projekti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1615,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: @@ -1818,6 +1871,7 @@ lv: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1935,6 +1989,7 @@ lv: confirmation: neatbilst %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: nepastāv. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2001,6 +2056,7 @@ lv: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2503,7 +2559,6 @@ lv: avatar: Avatārs base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Sadaļa comment: Komentârs comments: Komentârs @@ -2980,6 +3035,7 @@ lv: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3450,6 +3506,11 @@ lv: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3512,6 +3573,72 @@ lv: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Pašreizējais atbildīgais @@ -3543,6 +3670,7 @@ lv: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Konts + label_actions: Actions label_active: Aktīvs label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3583,6 +3711,7 @@ lv: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3833,7 +3962,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 @@ -3871,6 +4000,10 @@ lv: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Izrakstīties label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Sānu izvēlne @@ -4948,6 +5081,7 @@ lv: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5094,12 +5228,12 @@ lv: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/mn.yml b/config/locales/crowdin/mn.yml index d6b5e7ab996..07a26d93042 100644 --- a/config/locales/crowdin/mn.yml +++ b/config/locales/crowdin/mn.yml @@ -114,7 +114,7 @@ mn: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ mn: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ mn: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ mn: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ mn: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ mn: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ mn: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ mn: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ mn: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ mn: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ mn: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ mn: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ mn: avatar: Аватар base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ mn: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ mn: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ mn: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Даалгагч @@ -3484,6 +3611,7 @@ mn: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ mn: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ mn: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ mn: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ mn: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/ms.yml b/config/locales/crowdin/ms.yml index e7b986d491f..ed099d1511c 100644 --- a/config/locales/crowdin/ms.yml +++ b/config/locales/crowdin/ms.yml @@ -114,7 +114,7 @@ ms: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ms: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ms: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ms: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +382,9 @@ ms: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +393,14 @@ ms: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +414,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 @@ -674,6 +679,37 @@ ms: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1322,6 +1358,20 @@ ms: index: no_results_title_text: Tiada aliran kerja buat masa ini. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1516,6 +1566,9 @@ ms: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1524,7 +1577,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: @@ -1782,6 +1835,7 @@ ms: identity_url: Identiti URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1899,6 +1953,7 @@ ms: confirmation: tidak sepadan dengan %{attribute}. could_not_be_copied: "%{dependency} tidak dapat disalin (sepenuhnya)." does_not_exist: tidak wujud. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: tidak boleh diakses. error_readonly: telah cuba untuk menulis tetapi tidak dapat ditulis. @@ -1965,6 +2020,7 @@ ms: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2429,7 +2485,6 @@ ms: avatar: Avatar base: 'Ralat Umum:' body: Body - blocks_ids: ID pakej kerja yang disekat category: Kategori comment: Komen comments: Komen @@ -2870,6 +2925,7 @@ ms: title: Alat tambah perusahaan plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Tiada had already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3340,6 +3396,11 @@ ms: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3402,6 +3463,72 @@ ms: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Storan tidak dikenal pasti + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Penerima tugasan @@ -3433,6 +3560,7 @@ ms: invalid_filter: Invalid notification filter label_accessibility: Kebolehcapaian label_account: Akaun + label_actions: Tindakan label_active: Aktif label_activate_user: Aktifkan pengguna label_active_in_new_projects: Aktifkan dalam projek baharu @@ -3473,6 +3601,7 @@ ms: label_ical_access_key_generation_hint: Dijanakan secara automatik apabila melanggan kalendar. label_ical_access_key_latest: terkini label_ical_access_key_revoke: Batalkan + label_integrations: Integrations label_add_column: Tambah lajur label_applied_status: Status yang dilaksanakan label_archive_project: Arkib projek @@ -3725,7 +3854,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 @@ -3763,6 +3892,10 @@ ms: label_custom_pdf_export_settings: Tetapan eksport PDF tersuai label_custom_favicon: Favicon tersuai label_custom_touch_icon: Ikon sentuh tersuai + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Daftar keluar label_mapping_for: 'Pemetaan untuk: %{attribute}' label_main_menu: Menu Sampingan @@ -4830,6 +4963,7 @@ ms: ' setting_app_subtitle: Subtajuk aplikasi setting_app_title: Tajuk aplikasi + setting_organization_name: Organization name setting_attachment_max_size: Saiz maksimum lampiran setting_show_work_package_attachments: Paparkan lampiran di tab fail secara default setting_antivirus_scan_mode: Mod pengimbas @@ -4976,12 +5110,12 @@ ms: setting_welcome_text: Teks blok selamat datang setting_welcome_title: Tajuk blok selamat datang setting_welcome_on_homescreen: Paparkan blok selamat datang di skrin utama - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Mod penyorotan default diff --git a/config/locales/crowdin/ne.yml b/config/locales/crowdin/ne.yml index d97f76fdf04..b7b14fec901 100644 --- a/config/locales/crowdin/ne.yml +++ b/config/locales/crowdin/ne.yml @@ -114,7 +114,7 @@ ne: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ne: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ne: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ne: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ ne: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ ne: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ ne: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ ne: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ ne: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ ne: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ ne: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ ne: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ ne: avatar: 'अवतार ' base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ ne: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ ne: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ ne: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ ne: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: खाता + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ ne: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: रद्द गर्नुहोस् + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ ne: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ ne: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ ne: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/nl.yml b/config/locales/crowdin/nl.yml index e2640f523fb..9cafe9b0d07 100644 --- a/config/locales/crowdin/nl.yml +++ b/config/locales/crowdin/nl.yml @@ -114,7 +114,7 @@ nl: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ nl: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ nl: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ nl: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ nl: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ nl: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -680,6 +685,37 @@ nl: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1339,6 +1375,20 @@ nl: index: no_results_title_text: Er zijn momenteel geen workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1534,6 +1584,9 @@ nl: dependencies: Afhankelijkheden activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1542,7 +1595,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: @@ -1798,6 +1851,7 @@ nl: identity_url: Identiteit URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1915,6 +1969,7 @@ nl: confirmation: komt niet overeen met %{attribute}. could_not_be_copied: "%{dependency} kon niet (volledig) worden gekopieerd." does_not_exist: bestaat niet. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: geen toegang. error_readonly: werd geprobeerd te schrijven maar is niet schrijfbaar. @@ -1981,6 +2036,7 @@ nl: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2461,7 +2517,6 @@ nl: avatar: Avatar base: 'Algemene fout:' body: Body - blocks_ids: It's van geblokkeerde werkpakketten category: Categorie comment: Reactie comments: Commentaar @@ -2918,6 +2973,7 @@ nl: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Onbeperkt already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3387,6 +3443,11 @@ nl: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3449,6 +3510,72 @@ nl: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Onbekende opslag + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Toegewezene @@ -3480,6 +3607,7 @@ nl: invalid_filter: Invalid notification filter label_accessibility: Toegankelijkheid label_account: Account + label_actions: Acties label_active: Actief label_activate_user: Gebruiker activeren label_active_in_new_projects: Actief in nieuwe projecten @@ -3520,6 +3648,7 @@ nl: label_ical_access_key_generation_hint: Automatisch gegenereerd bij het abonneren op een kalender. label_ical_access_key_latest: laatste label_ical_access_key_revoke: Intrekken + label_integrations: Integrations label_add_column: Add column label_applied_status: Toegepaste status label_archive_project: Project archiveren @@ -3770,7 +3899,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 @@ -3808,6 +3937,10 @@ nl: label_custom_pdf_export_settings: Aangepaste PDF-exportinstellingen label_custom_favicon: Aangepaste favicon label_custom_touch_icon: Aangepaste touch-pictogram + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Afmelden label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Submenu @@ -4870,6 +5003,7 @@ nl: ' setting_app_subtitle: Applicatie ondertitel setting_app_title: Toepassingsnaam + setting_organization_name: Organization name setting_attachment_max_size: Bijlage max. grootte setting_show_work_package_attachments: Bijlagen standaard in het tabblad bestanden weergeven setting_antivirus_scan_mode: Scanmodus @@ -5016,12 +5150,12 @@ nl: setting_welcome_text: Welkom blok tekst setting_welcome_title: Welkom blok titel setting_welcome_on_homescreen: Toon het Welkom blok op thuisscherm - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standaardmodus markeren diff --git a/config/locales/crowdin/no.yml b/config/locales/crowdin/no.yml index d38d57f689f..927eafc0458 100644 --- a/config/locales/crowdin/no.yml +++ b/config/locales/crowdin/no.yml @@ -114,7 +114,7 @@ import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1339,6 +1375,20 @@ index: no_results_title_text: Det er ingen arbeidsflyt for øyeblikket. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1534,6 +1584,9 @@ dependencies: Avhengigheter activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1542,7 +1595,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: @@ -1798,6 +1851,7 @@ identity_url: Identitet URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1915,6 +1969,7 @@ confirmation: samsvarer ikke med %{attribute}. could_not_be_copied: "%{dependency} kunne ikke kopieres (fullstendig)." does_not_exist: finnes ikke. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: kan ikke nås error_readonly: ble forsøkt skrevet til, men er skrivebeskyttet @@ -1981,6 +2036,7 @@ attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2463,7 +2519,6 @@ avatar: Profilbilde base: 'Generell feil:' body: Body - blocks_ids: IDer for blokkerte arbeidspakker category: Kategori comment: Kommentar comments: Kommentar @@ -2920,6 +2975,7 @@ title: Enterprise programtillegg plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Ubegrenset already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3389,6 +3445,11 @@ quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3451,6 +3512,72 @@ disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Ukjent lagring + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Deltaker @@ -3482,6 +3609,7 @@ invalid_filter: Invalid notification filter label_accessibility: Tilgjengelighet label_account: Konto + label_actions: Handlinger label_active: Aktiv label_activate_user: Aktiver bruker label_active_in_new_projects: Aktiv i nye prosjekter @@ -3522,6 +3650,7 @@ label_ical_access_key_generation_hint: Automatisk generert ved abonnering på en kalender. label_ical_access_key_latest: siste label_ical_access_key_revoke: Tilbakekall + label_integrations: Integrations label_add_column: Add column label_applied_status: Tildelt status label_archive_project: Arkiver prosjekt @@ -3772,7 +3901,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 @@ -3810,6 +3939,10 @@ label_custom_pdf_export_settings: Egendefinerte PDF eksportinnstillinger label_custom_favicon: Tilpasset favicon label_custom_touch_icon: Egendefinert berøringsikon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Logg av label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Sidemeny @@ -4882,6 +5015,7 @@ ' setting_app_subtitle: Undertittel for applikasjon setting_app_title: Applikasjonsnavn + setting_organization_name: Organization name setting_attachment_max_size: Maksstørrelse for vedlegg setting_show_work_package_attachments: Vis vedlegg i fil-fanen som standard setting_antivirus_scan_mode: Skannemodus @@ -5028,12 +5162,12 @@ setting_welcome_text: Velkommen blokktekst setting_welcome_title: Velkomst blokk tittel setting_welcome_on_homescreen: Vis velkomstblokk på hjemskjermen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standard utheving modus diff --git a/config/locales/crowdin/pl.yml b/config/locales/crowdin/pl.yml index 22913448bf3..405ff235df8 100644 --- a/config/locales/crowdin/pl.yml +++ b/config/locales/crowdin/pl.yml @@ -114,7 +114,7 @@ pl: import: title: Import jira: - title: Import Jira + title: Migrator Jira description: To narzędzie służy do importowania danych z wystąpienia usługi Jira. Można skonfigurować wiele hostów usługi Jira i wybrać, co ma być importowane w każdym przebiegu importu. errors: cannot_delete_with_imports: Nie można usunąć hosta Jira z istniejącym importem @@ -125,8 +125,8 @@ pl: title: Konfiguracja usługi Jira new: Nowa konfiguracja banner: - title: Ograniczony import - description: 'To narzędzie importu jest obecnie w wersji beta i może importować tylko podstawowe dane: projekty, zgłoszenia problemów (nazwa, tytuł, opis, załączniki), użytkowników (nazwa, adres e-mail, członkostwo w projekcie), statusy i typy. Nie może importować przepływów pracy, pól niestandardowych, relacji między problemami ani uprawnień. Obecnie obsługujemy tylko serwery / centra danych Jira w wersjach 10.x i 11.x. Wystąpienia w chmurze nie są obecnie obsługiwane.' + title: Ograniczone możliwości importu + description: 'Ten migrator Jira jest obecnie w wersji beta i może importować tylko podstawowe dane: projekty, zgłoszenia problemów (nazwa, tytuł, opis, załączniki), użytkowników (nazwa, adres e-mail, członkostwo w projekcie), statusy i typy. Nie może importować przepływów pracy, pól niestandardowych, relacji między problemami ani uprawnień. Obecnie obsługujemy tylko serwery / centra danych Jira w wersjach 10.x i 11.x. Wystąpienia w chmurze nie są obecnie obsługiwane.' form: fields: name: Nazwa @@ -154,6 +154,7 @@ pl: connection_timeout: 'Upłynął limit czasu połączenia z serwerem Jira: %{message}' parse_error: 'Nie udało się przeanalizować odpowiedzi interfejsu API usługi Jira: %{message}' api_error: Interfejs API usługi Jira zwrócił status błędu %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projekty last_change: Ostatnia zmiana @@ -399,9 +400,9 @@ pl: notification_text_default: "

Witaj,

Utworzono nowy projekt: projectValue:name

Dziękujemy

\n" work_packages_identifier: page_header: - description: Wybierz między podstawowymi identyfikatorami liczbowymi pakietów roboczych a identyfikatorami specyficznymi dla projektu, które dodają identyfikator projektu jako prefiks do identyfikatora pakietu roboczego. + description: Wybierz pomiędzy klasycznymi numerycznymi identyfikatorami pakietów roboczych albo semantycznymi identyfikatorami specyficznymi dla projektu, które dodają identyfikator projektu do identyfikatora pakietu roboczego. banner: - existing_identifiers_notice: 'Istniejące identyfikatory %{project_count} projektów nie spełniają wymagań dotyczących identyfikatorów alfanumerycznych opartych na projektach. OpenProject może automatycznie zaktualizować je tak, aby były prawidłowe, jak w poniższych przykładach. Kliknij przycisk „Automatyczne napraw i zapisz”, aby zaktualizować identyfikatory wszystkich projektów w ten sposób i włączyć identyfikatory alfanumeryczne oparte na projektach. + existing_identifiers_notice: 'Istniejące identyfikatory %{project_count} projektów nie spełniają wymagań dotyczących identyfikatorów semantycznych opartych na projektach. OpenProject może automatycznie zaktualizować je tak, aby były prawidłowe, jak w poniższych przykładach. Kliknij przycisk „Automatyczne napraw i zapisz”, aby zaktualizować identyfikatory wszystkich projektów w ten sposób i włączyć identyfikatory semantyczne oparte na projektach. ' box_header: @@ -410,10 +411,14 @@ pl: label_autofixed_suggestion: Przyszły identyfikator label_example_work_package_id: Przykładowy identyfikator pakietu roboczego autofix_preview: - error_too_long: Musi składać się z mniej niż 5 znaków + error_too_long: Musi składać się z nie więcej niż 10 znaków + error_numerical: Nie może być czysto liczbowy + error_starts_with_number: Nie może zaczynać się od cyfry error_special_characters: Znaki specjalne są niedozwolone + error_not_fully_uppercased: Musi być pisany wielkimi literami error_in_use: Już używany jako aktywne dojście innego projektu error_reserved: Zarezerwowany przez historię dojść innego projektu + error_unknown: Wymaga ręcznego sprawdzenia remaining_projects: one: "...jeszcze 1 projekt" few: "...jeszcze %{count} projekty" @@ -430,7 +435,7 @@ pl: checkbox_label: Rozumiem, że spowoduje to trwałą zmianę wszystkich identyfikatorów pakietów roboczych success_banner: Pomyślnie zaktualizowano format identyfikatora pakietu roboczego. in_progress: - banner_message: Identyfikatory projektów są obecnie zmieniane na identyfikatory alfanumeryczne oparte na projektach. Może to zająć trochę czasu. + banner_message: Identyfikatory projektów są obecnie zmieniane na identyfikatory semantyczne oparte na projektach. Może to zająć trochę czasu. workflows: tabs: default_transitions: Przejścia domyślne @@ -697,6 +702,37 @@ pl: danger_dialog: confirmation_live_message_checked: Przycisk kontynuowania jest już aktywny. confirmation_live_message_unchecked: Przycisk kontynuowania jest teraz nieaktywny. Aby kontynuować, zaznacz pole wyboru. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Paginacja prev: Wstecz @@ -1373,6 +1409,20 @@ pl: index: no_results_title_text: Nie ma jeszcze żadnego obiegu pracy. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1568,6 +1618,9 @@ pl: dependencies: Zależności activerecord: attributes: + work_package_semantic_alias: + identifier: Identyfikator + work_package: Pakiet roboczy jira_import: projects: Projekty import/jira: @@ -1576,7 +1629,7 @@ pl: personal_access_token: Osobisty token dostępu import/jira_open_project_reference: jira: Jira - jira_import: Import Jira + jira_import: Migrator Jira announcements: show_until: Wyświetlaj do attachment: @@ -1832,6 +1885,7 @@ pl: identity_url: Adres URL tożsamości parent: Grupa nadrzędna organizational_unit: Jednostka organizacyjna + group_users: Group users group_detail: parent: Grupa nadrzędna organizational_unit: Jednostka organizacyjna @@ -1949,6 +2003,7 @@ pl: confirmation: nie pasuje do %{attribute}. could_not_be_copied: Nie można było (w pełni) skopiować %{dependency}. does_not_exist: nie istnieje. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} – dostępne tylko w OpenProject Enterprise edition." error_unauthorized: "— nie można uzyskac dostępu." error_readonly: "— podjęto próbę zapisu, ale nie jest zapisywalny." @@ -2015,6 +2070,7 @@ pl: attributes: parent_id: circular_dependency: spowodowałoby utworzenie cyklicznej hierarchii grup. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2535,7 +2591,6 @@ pl: avatar: Awatar base: 'Błąd ogólny:' body: Treść - blocks_ids: Identyfikatory zablokowanych pakietów roboczych category: Kategoria comment: Typ kosztu comments: Komentarz @@ -3032,6 +3087,7 @@ pl: title: Dodatek wersji Enterprise plan_title: Dodatek do planu Enterprise %{plan} plan_name: Plan Enterprise %{plan} + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Dostępne od wersji %{plan_name}. unlimited: Bez ograniczeń already_have_token: 'Masz już token? Dodaj go za pomocą poniższego przycisku, aby przejść na zarezerwowany plan Enterprise. @@ -3501,6 +3557,11 @@ pl: quick_add: label: Dodaj… my_account: + notifications_and_email: + title: Powiadomienia i wiadomości e-mail + tabs: + notifications: Ustawienia powiadomień + email_reminders: Przypomnienia e-mail access_tokens: description: Tokeny dostawcy są wydawane przez OpenProject, co umożliwia dostęp do nich innym aplikacjom. Tokeny klienta są wydawane przez inne aplikacje, co umożliwia dostęp do nich oprogramowaniu OpenProject. no_results: @@ -3563,6 +3624,72 @@ pl: disabled_text: Tokeny RSS nie zostały włączone przez administratora. Aby użyć tej funkcji, skontaktuj się z administratorem. storages: unknown_storage: Nieznany magazyn + email_reminders: + immediate_reminders: + title: Wyślij mi przypomnienie e-mail + mentioned: Powiadamiaj mnie, gdy ktoś mnie wspomni + personal_reminder: Powiadamiaj mnie o osobistych przypomnieniach + daily_reminders: + title: Wyślij mi codzienne przypomnienia e-mail dla nieprzeczytanych powiadomień + caption: Będziesz otrzymywać te przypomnienia tylko w przypadku nieprzeczytanych powiadomień i tylko w określonych przez Ciebie godzinach. Dopóki nie skonfigurujesz strefy czasowej dla swojego konta, godziny będą interpretowane jako UTC. + enabled: Włącz codzienne przypomnienia e-mail + add_time: Dodaj godzinę + remove_time: Usuń godzinę + time_slot_label: Czas przypomnienia (UTC) + workdays: + title: Otrzymuj przypomnienia e-mail w tych dniach + submit_button: Zaktualizuj dni przypomnienia + pause_reminders: + title: Wstrzymaj powiadomienia e-mail + enabled: Tymczasowo wstrzymuj codzienne przypomnienia e-mail + date_range: Okres wstrzymania + email_alerts: + title: Powiadomienia e-mail dotyczące innych elementów, które nie są pakietami roboczymi + news_added: Wiadomość dodana + news_commented: Komentarz do nowości + document_added: Dodano dokument + forum_messages: Wiadomość na forum + wiki_page_added: Dodano stronę wiki + wiki_page_updated: Zaktualizowano stronę wiki + membership_added: Dodanie członkostwa + membership_updated: Aktualizacja członkostwa + submit_button: Zaktualizuj alerty + notifications: + participating: + title: Uczestnictwo + submit_button: Zaktualizuj preferencje + mentioned: Wzmianka + watched: Obserwowane + assignee: Przypisana osoba + responsible: Osoba odpowiedzialna + shared: Udostępnione dla mnie + date_alerts: + title: Alerty dotyczące dat + submit_button: Zaktualizuj alerty dotyczące dat + start_date: Data rozpoczęcia + due_date: Data zakończenia + overdue: Przekroczony termin + times: + same_day: Tego samego dnia + one_day_before: 1 dzień przed + three_days_before: 3 dni przed + seven_days_before: 7 dni przed + one_day_after: 1 dzień po + three_days_after: 3 dni po + seven_days_after: 7 dni po + non_participating: + title: Brak uczestnictwa + submit_button: Zaktualizuj preferencje + work_package_created: Nowe pakiety robocze + work_package_commented: Wszystkie nowe komentarze + work_package_processed: Wszystkie zmiany statusu + work_package_prioritized: Wszystkie zmiany priorytetów + work_package_scheduled: Wszystkie zmiany daty + project_specific_settings: + title: Ustawienia powiadomień dla danego projektu + add_button: Dodaj powiadomienia specyficzne dla projektu + dialog_title: Dodaj powiadomienia specyficzne dla projektu + list_header: Projekty z określonymi powiadomieniami notifications: reasons: assigned: Przypisana osoba @@ -3594,6 +3721,7 @@ pl: invalid_filter: Nieprawidłowy filtr powiadomień label_accessibility: Dostępność label_account: Konto + label_actions: Działania label_active: Aktywne label_activate_user: Aktywuj użytkownika label_active_in_new_projects: Aktywne w nowych projektach @@ -3634,6 +3762,7 @@ pl: label_ical_access_key_generation_hint: Automatycznie wygenerowano podczas subskrypcji kalendarza. label_ical_access_key_latest: najnowszy label_ical_access_key_revoke: Odwołaj + label_integrations: Integracje label_add_column: Dodaj kolumnę label_applied_status: Nadaj status label_archive_project: Archiwum projektów @@ -3884,7 +4013,7 @@ pl: label_external_links: Linki zewnętrzne label_locale: Język i region label_jump_to_a_project: Skok do projektu... - label_jira_import: Import Jira + label_jira_import: Migrator Jira label_keyword_plural: Słowa kluczowe label_language_based: Na podstawie języka użytkownika label_last_activity: Ostatnia aktywność @@ -3922,6 +4051,10 @@ pl: label_custom_pdf_export_settings: Niestandardowe ustawienia eksportu w formacie PDF label_custom_favicon: Własny favicon label_custom_touch_icon: Własny touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Wyloguj label_mapping_for: 'Mapowanie dla: %{attribute}' label_main_menu: Menu boczne @@ -4996,6 +5129,7 @@ pl: ' setting_app_subtitle: Podtytuł setting_app_title: Tytuł + setting_organization_name: Organization name setting_attachment_max_size: Maks. rozmiar załącznika setting_show_work_package_attachments: Domyślnie pokazuj załączniki na karcie plików setting_antivirus_scan_mode: Tryb skanowania @@ -5142,12 +5276,12 @@ pl: setting_welcome_text: Tekst bloku powitalnego setting_welcome_title: Tytuł bloku powitalnego setting_welcome_on_homescreen: Wyświetl wiadomość powitalną na ekranie głównym - setting_work_packages_identifier_numeric: Sekwencja liczbowa dla całego wystąpienia (domyślnie) - setting_work_packages_identifier_numeric_caption: 'Każdy pakiet roboczy otrzymuje numer kolejny zaczynający się od 1 i zwiększany z każdym nowym pakietem. Numery są unikalne w ramach tego wystąpienia, więc pozostają takie same, nawet jeśli pakiety robocze są przenoszone między projektami. + setting_work_packages_identifier_classic: Sekwencja liczbowa dla całego wystąpienia (domyślnie) + setting_work_packages_identifier_classic_caption: 'Każdy pakiet roboczy otrzymuje numer kolejny zaczynający się od 1 i zwiększany z każdym nowym pakietem. Numery są unikalne w ramach tego wystąpienia, więc pozostają takie same, nawet jeśli pakiety robocze są przenoszone między projektami. ' - setting_work_packages_identifier_alphanumeric: Identyfikatory alfanumeryczne oparte na projektach - setting_work_packages_identifier_alphanumeric_caption: 'Każdy projekt ma unikalny identyfikator, który jest poprzedzony identyfikatorem pakietu roboczego. Jeśli pakiet roboczy zostanie przeniesiony do innego projektu, generowany jest nowy identyfikator, ale stary nadal działa. + setting_work_packages_identifier_semantic: Identyfikatory semantyczne oparte na projektach + setting_work_packages_identifier_semantic_caption: 'Każdy projekt ma unikalny identyfikator, który jest poprzedzony identyfikatorem pakietu roboczego. Jeśli pakiet roboczy zostanie przeniesiony do innego projektu, generowany jest nowy identyfikator, ale stary nadal działa. ' setting_work_package_list_default_highlighting_mode: Domyślny tryb wyróżniania diff --git a/config/locales/crowdin/pt-BR.yml b/config/locales/crowdin/pt-BR.yml index 871ae418944..175caf00f3e 100644 --- a/config/locales/crowdin/pt-BR.yml +++ b/config/locales/crowdin/pt-BR.yml @@ -114,7 +114,7 @@ pt-BR: import: title: Importar jira: - title: Importação do Jira + title: Migrador do Jira description: Use esta ferramenta para importar dados da sua instância do Jira. Você pode configurar vários hosts do Jira e escolher o que importar em cada execução de importação. errors: cannot_delete_with_imports: Não é possível excluir o host do Jira enquanto houver importações existentes @@ -125,8 +125,8 @@ pt-BR: title: Configuração do Jira new: Nova configuração banner: - title: Importação limitada - description: 'Esta ferramenta de importação está atualmente em versão beta e só consegue importar dados básicos: projetos, tarefas (nome, título, descrição, anexos), usuários (nome, e-mail, participação em projetos), status e tipos. Não é possível importar fluxos de trabalho, campos personalizados, relações entre tarefas ou permissões. No momento, só damos suporte às versões 10.x e 11.x do Jira Server/Data Center. Instâncias Cloud não possuem suporte.' + title: Capacidades de importação limitadas + description: 'Este Migrador do Jira está atualmente em versão beta e só consegue importar dados básicos: projetos, chamados (nome, título, descrição, anexos), usuários (nome, e-mail, participação em projetos), status e tipos. Ele não consegue importar fluxos de trabalho, campos personalizados, relações entre chamados ou permissões. No momento, só são suportadas as versões Jira Server/Data Center 10.x e 11.x. Instâncias na nuvem (Cloud) não são compatíveis atualmente.' form: fields: name: Nome @@ -154,6 +154,7 @@ pt-BR: connection_timeout: 'O tempo de conexão com o servidor Jira esgotou: %{message}' parse_error: 'Falha ao analisar a resposta da API do Jira: %{message}' api_error: A API do Jira retornou o status de erro %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projetos last_change: Última alteração @@ -387,9 +388,9 @@ pt-BR: notification_text_default: "

Olá,

Um novo projeto foi criado: projectValue:name

Obrigado

\n" work_packages_identifier: page_header: - description: Escolha entre IDs de pacotes de trabalho numéricos básicos ou IDs específicos do projeto, que adicionam o identificador do projeto ao ID do pacote de trabalho. + description: Escolha entre IDs de pacotes de trabalho numéricos clássicos ou IDs semânticos específicos do projeto, que adicionam o identificador do projeto ao ID do pacote de trabalho. banner: - existing_identifiers_notice: 'Os identificadores existentes de %{project_count} projetos não atendem aos requisitos para identificadores alfanuméricos baseados em projeto. O OpenProject pode atualizá-los automaticamente para que fiquem válidos, como nos exemplos abaixo. Clique em “Autocorrigir e salvar” para atualizar os identificadores de todos os projetos dessa forma e ativar os Ids alfanuméricos baseados em projeto. + existing_identifiers_notice: 'Os identificadores existentes de %{project_count} projetos não atendem aos requisitos para Ids semânticos baseados em projeto. O OpenProject pode atualizá-los automaticamente para que fiquem válidos, como nos exemplos abaixo. Clique em “Autocorrigir e salvar” para atualizar os identificadores de todos os projetos dessa forma e ativar os Ids semânticos baseados em projeto. ' box_header: @@ -398,10 +399,14 @@ pt-BR: label_autofixed_suggestion: Identificador futuro label_example_work_package_id: Exemplo de ID de pacote de trabalho autofix_preview: - error_too_long: Deve ter menos de 5 caracteres + error_too_long: Deve conter no máximo 10 caracteres + error_numerical: Não pode ser exclusivamente numérico + error_starts_with_number: Não pode começar com um número error_special_characters: Caracteres especiais não são permitidos + error_not_fully_uppercased: Deve estar em maiúsculas error_in_use: Já está em uso como identificador ativo de outro projeto error_reserved: Reservado pelo histórico de identificadores de outro projeto + error_unknown: Requer revisão manual remaining_projects: one: "… mais 1 projeto" other: "… mais %{count} projetos" @@ -416,7 +421,7 @@ pt-BR: checkbox_label: Estou ciente de que isso alterará permanentemente todos os IDs dos pacotes de trabalho success_banner: Formato de identificador de pacote de trabalho atualizado com sucesso. in_progress: - banner_message: Os identificadores dos projetos estão sendo atualizados para identificadores alfanuméricos baseados em projeto. Isso pode levar algum tempo. + banner_message: Os identificadores dos projetos estão sendo atualizados para identificadores semânticos baseados em projeto. Isso pode levar algum tempo. workflows: tabs: default_transitions: Transições padrão @@ -682,6 +687,37 @@ pt-BR: danger_dialog: confirmation_live_message_checked: O botão para prosseguir agora está ativo. confirmation_live_message_unchecked: O botão para prosseguir está desativado. É necessário marcar a caixa de seleção para continuar. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Paginação prev: Anterior @@ -1338,6 +1374,20 @@ pt-BR: index: no_results_title_text: Atualmente, não há fluxos de trabalho. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1533,6 +1583,9 @@ pt-BR: dependencies: Dependências activerecord: attributes: + work_package_semantic_alias: + identifier: Identificador + work_package: Pacote de trabalho jira_import: projects: Projetos import/jira: @@ -1541,7 +1594,7 @@ pt-BR: personal_access_token: Token de acesso de pessoal import/jira_open_project_reference: jira: Jira - jira_import: Importação do Jira + jira_import: Migrador do Jira announcements: show_until: Exibir até attachment: @@ -1797,6 +1850,7 @@ pt-BR: identity_url: URL de identidade parent: Grupo principal organizational_unit: Unidade organizacional + group_users: Group users group_detail: parent: Grupo principal organizational_unit: Unidade organizacional @@ -1914,6 +1968,7 @@ pt-BR: confirmation: não coincide com %{attribute}. could_not_be_copied: "%{dependency} não pôde ser copiado (completamente)." does_not_exist: não existe. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} só está disponível na edição OpenProject Enterprise." error_unauthorized: não pode ser acessado. error_readonly: tentou escrever, mas não é gravável. @@ -1980,6 +2035,7 @@ pt-BR: attributes: parent_id: circular_dependency: criaria uma hierarquia circular de grupos. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2462,7 +2518,6 @@ pt-BR: avatar: Avatar base: 'Erro geral:' body: Mensagem - blocks_ids: IDs dos pacotes de trabalho bloqueados category: Categoria comment: Comentário comments: Comentário @@ -2919,6 +2974,7 @@ pt-BR: title: Complemento empresarial plan_title: Complemento do %{plan} Enterprise plan_name: Plano enterprise %{plan} + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Disponível a partir do %{plan_name}. unlimited: Ilimitado already_have_token: 'Já possui um token? Adicione-o usando o botão abaixo para fazer o "upgrade" para o plano Enterprise. @@ -3388,6 +3444,11 @@ pt-BR: quick_add: label: Adicionar… my_account: + notifications_and_email: + title: Notificação e e-mail + tabs: + notifications: Configurações de notificação + email_reminders: Lembretes por e-mail access_tokens: description: Tokens de provedor são emitidos pelo OpenProject, permitindo que outros aplicativos tenham acesso a ele. Tokens de cliente são emitidos por outros aplicativos, permitindo que o OpenProject tenha acesso a eles. no_results: @@ -3450,6 +3511,72 @@ pt-BR: disabled_text: Tokens RSS não estão habilitados pelo administrador. Entre em contato com ele para usar este recurso. storages: unknown_storage: Armazenamento desconhecido + email_reminders: + immediate_reminders: + title: Envie-me um lembrete por e-mail + mentioned: Avise-me quando eu for mencionado + personal_reminder: Notificar-me sobre lembretes pessoais + daily_reminders: + title: Enviar lembretes diários por e-mail para notificações não lidas + caption: Você receberá esses lembretes apenas para notificações não lidas e somente nos horários que você especificar. Até que você configure um fuso horário para sua conta, os horários serão interpretados como UTC. + enabled: Ativar lembretes diários por e-mail + add_time: Adicionar horário + remove_time: Remover horário + time_slot_label: Horário do lembrete (UTC) + workdays: + title: Receber lembretes por e-mail nestes dias + submit_button: Atualizar dias do lembrete + pause_reminders: + title: Pausar notificações por e-mail + enabled: Pausar temporariamente os lembretes diários por e-mail + date_range: Período de pausa + email_alerts: + title: Alertas por e-mail para outros itens que não sejam pacotes de trabalho + news_added: Notícia adicionada + news_commented: Comentar em uma notícia + document_added: Documento adicionado + forum_messages: Mensagem publicada no fórum + wiki_page_added: Página wiki adicionada + wiki_page_updated: Página wiki atualizada + membership_added: Associação adicionada + membership_updated: Associação atualizada + submit_button: Atualizar alertas + notifications: + participating: + title: Participando + submit_button: Atualizar preferências + mentioned: Mencionado + watched: Assistindo + assignee: Encarregado + responsible: Responsável + shared: Compartilhados comigo + date_alerts: + title: Alertas de data + submit_button: Atualizar alertas de data + start_date: Data de início + due_date: Data de conclusão + overdue: Atrasado + times: + same_day: No mesmo dia + one_day_before: 1 dia antes + three_days_before: 3 dias antes + seven_days_before: 7 dias antes + one_day_after: 1 dia depois + three_days_after: 3 dias depois + seven_days_after: 7 dias depois + non_participating: + title: Não participando + submit_button: Atualizar preferências + work_package_created: Novos pacotes de trabalho + work_package_commented: Todos os novos comentários + work_package_processed: Todas as mudanças de status + work_package_prioritized: Todas as mudanças de prioridade + work_package_scheduled: Todas as mudanças de data + project_specific_settings: + title: Configurações de notificação específicas de projetos + add_button: Adicionar notificações específicas do projeto + dialog_title: Adicionar notificações específicas do projeto + list_header: Projetos com notificações específicas notifications: reasons: assigned: Cessionário @@ -3481,6 +3608,7 @@ pt-BR: invalid_filter: Filtro de notificação inválido label_accessibility: Acessibilidade label_account: Conta + label_actions: Ações label_active: Ativo label_activate_user: Ativar usuário label_active_in_new_projects: Ativo em novos projetos @@ -3521,6 +3649,7 @@ pt-BR: label_ical_access_key_generation_hint: Gerado automaticamente ao assinar um calendário. label_ical_access_key_latest: último label_ical_access_key_revoke: Anular + label_integrations: Integrações label_add_column: Adicionar coluna label_applied_status: Situação aplicada label_archive_project: Arquivar projeto @@ -3771,7 +3900,7 @@ pt-BR: label_external_links: Links externos label_locale: Idioma e região label_jump_to_a_project: Saltar para um projeto... - label_jira_import: Importação do Jira + label_jira_import: Migrador do Jira label_keyword_plural: Palavras-chave label_language_based: Com base no idioma do usuário label_last_activity: Última atividade @@ -3809,6 +3938,10 @@ pt-BR: label_custom_pdf_export_settings: Configurações de exportação de PDF personalizadas label_custom_favicon: Ícone personalizado label_custom_touch_icon: Ícone de toque personalizado + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Desconectar label_mapping_for: 'Mapeamento para: %{attribute}' label_main_menu: Menu lateral @@ -4872,6 +5005,7 @@ pt-BR: ' setting_app_subtitle: Subtítulo do aplicativo setting_app_title: Título da aplicação + setting_organization_name: Organization name setting_attachment_max_size: Tamanho máximo dos anexos setting_show_work_package_attachments: Exibir anexos na aba de arquivos por padrão setting_antivirus_scan_mode: Modo escanear @@ -5018,12 +5152,12 @@ pt-BR: setting_welcome_text: Texto do bloco de boas-vindas setting_welcome_title: Título do bloco de boas-vindas setting_welcome_on_homescreen: Exibir bloco de boas-vindas na tela inicial - setting_work_packages_identifier_numeric: Sequência numérica global da instância (padrão) - setting_work_packages_identifier_numeric_caption: 'Cada pacote de trabalho recebe um número sequencial começando em 1, que é aumentado a cada novo item. Os números são únicos nesta instância, portanto permanecem os mesmos mesmo que os pacotes de trabalho sejam movidos entre projetos. + setting_work_packages_identifier_classic: Sequência numérica global da instância (padrão) + setting_work_packages_identifier_classic_caption: 'Cada pacote de trabalho recebe um número sequencial começando em 1, que é aumentado a cada novo item. Os números são únicos nesta instância, portanto permanecem os mesmos mesmo que os pacotes de trabalho sejam movidos entre projetos. ' - setting_work_packages_identifier_alphanumeric: Identificadores alfanuméricos baseados em projeto - setting_work_packages_identifier_alphanumeric_caption: 'Cada projeto possui um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for movido para outro projeto, um novo identificador é gerado, mas o antigo continua funcionando. + setting_work_packages_identifier_semantic: Identificadores semânticos baseados em projeto + setting_work_packages_identifier_semantic_caption: 'Cada projeto possui um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for movido para outro projeto, um novo identificador é gerado, mas o antigo continua funcionando. ' setting_work_package_list_default_highlighting_mode: Modo de destaque padrão diff --git a/config/locales/crowdin/pt-PT.yml b/config/locales/crowdin/pt-PT.yml index e55f5bb7117..bd9237afdbd 100644 --- a/config/locales/crowdin/pt-PT.yml +++ b/config/locales/crowdin/pt-PT.yml @@ -114,7 +114,7 @@ pt-PT: import: title: Importar jira: - title: Importação Jira + title: Jira Migrator description: Utilize esta ferramenta para importar dados da sua instância do Jira. Pode configurar vários anfitriões do Jira, e escolher o que importar em cada execução de importação. errors: cannot_delete_with_imports: Não é possível eliminar o anfitrião do Jira com importações existentes @@ -125,8 +125,8 @@ pt-PT: title: Configuração do Jira new: Nova configuração banner: - title: Importação limitada - description: 'Esta ferramenta de importação está em fase beta, e só consegue importar dados básicos: projetos, problemas (nome, título, descrição, anexos), utilizadores (nome, e-mail, associação ao projeto), estados e tipos. Não pode importar fluxos de trabalho, campos personalizados, relações de problemas ou permissões. Neste momento, só temos suporte para o servidor/centro de dados Jira nas versões 10.x e 11.x. As instâncias na nuvem não são suportadas agora.' + title: Capacidades de importação limitadas + description: 'Jira Migrator está em fase beta, e só consegue importar dados básicos: projetos, problemas (nome, título, descrição, anexos), utilizadores (nome, e-mail, associação ao projeto), estados e tipos. Não pode importar fluxos de trabalho, campos personalizados, relações de problemas ou permissões. Neste momento, só temos suporte para o servidor/centro de dados Jira nas versões 10.x e 11.x. As instâncias na nuvem não são suportadas agora.' form: fields: name: Nome @@ -154,6 +154,7 @@ pt-PT: connection_timeout: 'O tempo de ligação ao servidor Jira expirou: %{message}' parse_error: 'Falha na análise da resposta da API do Jira: %{message}' api_error: A API do Jira devolveu o estado de erro %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projetos last_change: Última alteração @@ -162,7 +163,7 @@ pt-PT: run: title: Importar execução history: Histórico - remove_error: Uma importação do Jira não pode ser removida enquanto está a ser executada + remove_error: Uma execução de importação do Jira não pode ser eliminada enquanto estiver em execução import_blocked_error: Está em curso ou a aguardar revisão outra importação do Jira. Conclua-a ou reverta-a antes de iniciar uma nova importação. project_identifier_taken: 'Está a tentar importar um projeto com um identificador já utilizado: %{taken_identifier}. Atualize o identificador do projeto no Jira e clique em Tentar novamente.' blank: @@ -387,9 +388,9 @@ pt-PT: notification_text_default: "

Olá,

Foi criado um novo projeto: projectValue:name

Obrigado

\n" work_packages_identifier: page_header: - description: Escolha entre IDs numéricos básicos de pacotes de trabalho ou IDs específicos do projeto que acrescentam o identificador do projeto ao ID do pacote de trabalho. + description: Escolha entre IDs de pacotes de trabalho numéricos clássicos ou específicos de projetos semânticos, que acrescentam o identificador do projeto ao ID do pacote de trabalho. banner: - existing_identifiers_notice: 'Os identificadores existentes para os projec~tos %{project_count} não cumprem os requisitos para identificadores alfanuméricos baseados em projetos. O OpenProject pode atualizá-los automaticamente para que sejam válidos, como nos exemplos abaixo. Clique em "Corrigir automaticamente e guardar" para atualizar os identificadores de todos os projetos desta forma, e ativar os identificadores alfanuméricos baseados em projetos. + existing_identifiers_notice: 'Os identificadores existentes para os projec~tos %{project_count} não cumprem os requisitos para identificadores semânticos baseados em projetos. O OpenProject pode atualizá-los automaticamente para que sejam válidos, como nos exemplos abaixo. Clique em "Corrigir automaticamente e guardar" para atualizar os identificadores de todos os projetos desta forma, e ativar os identificadores semânticos baseados em projetos. ' box_header: @@ -398,10 +399,14 @@ pt-PT: label_autofixed_suggestion: Identificador de futuro label_example_work_package_id: Exemplo de ID de pacote de trabalho autofix_preview: - error_too_long: Tem de ter menos de 5 caracteres + error_too_long: Tem de ter 10 caracteres ou menos + error_numerical: Não pode ser puramente numérico + error_starts_with_number: Não pode começar com um número error_special_characters: Caracteres especiais não são permitidos + error_not_fully_uppercased: Deve estar em maiúsculas error_in_use: Já está a ser utilizado como identificador ativo de outro projeto error_reserved: Reservado pelo histórico do identificador de outro projeto + error_unknown: Precisa de revisão manual remaining_projects: one: "... mais 1 projeto" other: "... mais %{count} projetos" @@ -416,7 +421,7 @@ pt-PT: checkbox_label: Compreendo que isto irá alterar permanentemente todos os IDs dos pacotes de trabalho success_banner: Formato de identificador de pacote de trabalho atualizado com sucesso. in_progress: - banner_message: Os identificadores de projetos estão a ser atualizados para identificadores alfanuméricos baseados em projetos. Este processo pode demorar algum tempo. + banner_message: Os identificadores de projetos estão a ser atualizados para identificadores semânticos baseados em projetos. Este processo pode demorar algum tempo. workflows: tabs: default_transitions: Transições padrão @@ -681,6 +686,37 @@ pt-PT: danger_dialog: confirmation_live_message_checked: O botão para prosseguir está agora ativo. confirmation_live_message_unchecked: O botão para prosseguir está agora inativo. Tem de assinalar a caixa de verificação para continuar. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Paginação prev: Anterior @@ -1339,6 +1375,20 @@ pt-PT: index: no_results_title_text: Atualmente, não existem ritmos de trabalho. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1534,6 +1584,9 @@ pt-PT: dependencies: Dependências activerecord: attributes: + work_package_semantic_alias: + identifier: Identificador + work_package: Pacote de trabalho jira_import: projects: Projetos import/jira: @@ -1542,7 +1595,7 @@ pt-PT: personal_access_token: Token de acesso pessoal import/jira_open_project_reference: jira: Jira - jira_import: Importação Jira + jira_import: Jira Migrator announcements: show_until: Exibir até attachment: @@ -1798,6 +1851,7 @@ pt-PT: identity_url: URL de identidade parent: Grupo principal organizational_unit: Unidade organizacional + group_users: Group users group_detail: parent: Grupo principal organizational_unit: Unidade organizacional @@ -1915,6 +1969,7 @@ pt-PT: confirmation: não coincide %{attribute}. could_not_be_copied: "%{dependency} não pode ser copiado (completamente)." does_not_exist: não existe. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} só está disponível na edição OpenProject Enterprise." error_unauthorized: não pode ser acedido. error_readonly: tentou escrever, mas não é gravável. @@ -1981,6 +2036,7 @@ pt-PT: attributes: parent_id: circular_dependency: criaria uma hierarquia circular de grupos. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2463,7 +2519,6 @@ pt-PT: avatar: Avatar base: 'Erro geral:' body: Corpo - blocks_ids: Identificações de pacotes de trabalho bloqueados category: Categoria comment: Comentário comments: Comentario @@ -2920,6 +2975,7 @@ pt-PT: title: Complemento Enterprise plan_title: Complemento %{plan} Enterprise plan_name: Plano Enterprise %{plan} + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Disponível a partir do %{plan_name}. unlimited: Ilimitado already_have_token: 'Já tem um token? Adicione-o com o botão abaixo para atualizar para o plano Enterprise reservado. @@ -3389,6 +3445,11 @@ pt-PT: quick_add: label: Adicionar… my_account: + notifications_and_email: + title: Notificação e e-mail + tabs: + notifications: Definições de notificações + email_reminders: Lembretes por e-mail access_tokens: description: Os tokens de fornecedor são emitidos pelo OpenProject e permitem o acesso de outras aplicações. Os tokens de cliente são emitidos por outras aplicações e permitem que o OpenProject lhes aceda. no_results: @@ -3451,6 +3512,72 @@ pt-PT: disabled_text: Os tokens RSS não são ativados pelo administrador. Contacte o seu administrador para utilizar esta funcionalidade. storages: unknown_storage: Armazenamento desconhecido + email_reminders: + immediate_reminders: + title: Enviar-me um lembrete por e-mail + mentioned: Notificar-me quando eu for mencionado + personal_reminder: Notificar-me para receber lembretes pessoais + daily_reminders: + title: Enviar-me lembretes diários de e-mail para notificações não lidas + caption: Receberá estes lembretes apenas para notificações não lidas e apenas nas horas que especificar. Até configurar um fuso horário para a sua conta, as horas serão interpretadas em UTC. + enabled: Ativar lembretes diários por e-mail + add_time: Adicionar hora + remove_time: Remover hora + time_slot_label: Hora do lembrete (UTC) + workdays: + title: Receber lembretes por e-mail nestes dias + submit_button: Atualizar dias de lembrete + pause_reminders: + title: Pausar notificações por e-mail + enabled: Pausar temporariamente lembretes de e-mail diários + date_range: Período de pausa + email_alerts: + title: Alertas de e-mail para outros elementos que não pacotes de trabalho + news_added: Notícia adicionada + news_commented: Comentar numa notícia + document_added: Documento adicionado + forum_messages: Mensagem do fórum publicada + wiki_page_added: Página wiki adicionada + wiki_page_updated: Página wiki atualizada + membership_added: Adesão adicionada + membership_updated: Adesão atualizada + submit_button: Atualizar alertas + notifications: + participating: + title: Participante + submit_button: Atualizar preferências + mentioned: Mencionou + watched: A ver + assignee: Encarregado + responsible: Responsável + shared: Partilhado comigo + date_alerts: + title: Alertas de data + submit_button: Atualizar alertas de data + start_date: Data de início + due_date: Data de término + overdue: Em atraso + times: + same_day: No mesmo dia + one_day_before: 1 dia antes + three_days_before: 3 dias antes + seven_days_before: 7 dias antes + one_day_after: 1 dia depois + three_days_after: 3 dias depois + seven_days_after: 7 dias depois + non_participating: + title: Não participante + submit_button: Atualizar preferências + work_package_created: Novos pacotes de trabalho + work_package_commented: Todos os novos comentários + work_package_processed: Todas as mudanças de estado + work_package_prioritized: Todas as mudanças de prioridade + work_package_scheduled: Todas as alterações de datas + project_specific_settings: + title: Definições de notificação específicas do projeto + add_button: Adicionar notificações específicas do projeto + dialog_title: Adicionar notificações específicas do projeto + list_header: Projetos com notificações específicas notifications: reasons: assigned: Pessoa atribuída @@ -3482,6 +3609,7 @@ pt-PT: invalid_filter: Filtro de notificação inválido label_accessibility: Accessibilidade label_account: Conta + label_actions: Ações label_active: Ativo label_activate_user: Ativar utilizador label_active_in_new_projects: Ativo em novos projetos @@ -3522,6 +3650,7 @@ pt-PT: label_ical_access_key_generation_hint: Gerado automaticamente ao subscrever um calendário. label_ical_access_key_latest: recente label_ical_access_key_revoke: Revogar + label_integrations: Integrações label_add_column: Adicionar coluna label_applied_status: Status aplicado label_archive_project: Arquivar projeto @@ -3772,7 +3901,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 @@ -3810,6 +3939,10 @@ pt-PT: label_custom_pdf_export_settings: Configurações de exportação de PDF personalizadas label_custom_favicon: Favicon personalizado label_custom_touch_icon: Ícone de toque personalizado + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Terminar Sessão label_mapping_for: 'Mapeamento para: %{attribute}' label_main_menu: Menu lateral @@ -4867,6 +5000,7 @@ pt-PT: ' setting_app_subtitle: Subtítulo da aplicação setting_app_title: Título da aplicação + setting_organization_name: Organization name setting_attachment_max_size: Tamanho máx. do anexo setting_show_work_package_attachments: Mostrar anexos no separador dos ficheiros por padrão setting_antivirus_scan_mode: Modo de digitalização @@ -5013,12 +5147,12 @@ pt-PT: setting_welcome_text: Bloco de texto de boas-vindas setting_welcome_title: Título de texto de boas-vindas setting_welcome_on_homescreen: Exibir o bloco de boas-vindas no ecrã inicial - setting_work_packages_identifier_numeric: Sequência numérica a nível da instância (padrão) - setting_work_packages_identifier_numeric_caption: 'Cada pacote de trabalho recebe um número sequencial que começa em 1 e aumenta com cada novo pacote. Os números são únicos nesta instância, pelo que permanecem os mesmos, mesmo que os pacotes de trabalho sejam transferidos entre projetos. + setting_work_packages_identifier_classic: Sequência numérica a nível da instância (padrão) + setting_work_packages_identifier_classic_caption: 'Cada pacote de trabalho recebe um número sequencial que começa em 1 e aumenta com cada novo pacote. Os números são únicos nesta instância, pelo que permanecem os mesmos, mesmo que os pacotes de trabalho sejam transferidos entre projetos. ' - setting_work_packages_identifier_alphanumeric: Identificadores alfanuméricos baseados em projetos - setting_work_packages_identifier_alphanumeric_caption: 'Cada projeto tem um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for transferido para outro projeto, é gerado um novo identificador, mas o antigo continua a funcionar. + setting_work_packages_identifier_semantic: Identificadores semânticos baseados em projetos + setting_work_packages_identifier_semantic_caption: 'Cada projeto tem um identificador único que é prefixado ao ID do pacote de trabalho. Se um pacote de trabalho for transferido para outro projeto, é gerado um novo identificador, mas o antigo continua a funcionar. ' setting_work_package_list_default_highlighting_mode: Modo de destaque padrão diff --git a/config/locales/crowdin/ro.yml b/config/locales/crowdin/ro.yml index f2e163d0e24..de18dcdbe43 100644 --- a/config/locales/crowdin/ro.yml +++ b/config/locales/crowdin/ro.yml @@ -114,7 +114,7 @@ ro: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ ro: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ ro: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ ro: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +394,9 @@ ro: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +405,14 @@ ro: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -423,7 +428,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 @@ -690,6 +695,37 @@ ro: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1358,6 +1394,20 @@ ro: index: no_results_title_text: În acest moment nu există fluxuri de lucru. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1554,6 +1604,9 @@ ro: dependencies: Dependenţe activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1615,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: @@ -1818,6 +1871,7 @@ ro: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1935,6 +1989,7 @@ ro: confirmation: nu se potrivește cu %{attribute}. could_not_be_copied: "%{dependency} nu a putut fi copiat (integral)." does_not_exist: nu există. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: nu pot fi accesate. error_readonly: a fost încercat să fie scris, dar nu este inscriptibil. @@ -2001,6 +2056,7 @@ ro: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2503,7 +2559,6 @@ ro: avatar: Avatar base: 'Eroare generală:' body: Body - blocks_ids: ID-urile pachetelor de lucru blocate category: Categorie comment: Comentariu comments: Comentariu @@ -2980,6 +3035,7 @@ ro: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Nelimitat already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3450,6 +3506,11 @@ ro: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3512,6 +3573,72 @@ ro: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Executant @@ -3543,6 +3670,7 @@ ro: invalid_filter: Filtru de notificare invalid label_accessibility: Accesibilitate label_account: Cont + label_actions: Acțiuni label_active: Activ label_activate_user: Activați utilizatorul label_active_in_new_projects: Activ în proiecte noi @@ -3583,6 +3711,7 @@ ro: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revocă + label_integrations: Integrations label_add_column: Adaugă coloană label_applied_status: Stare aplicată label_archive_project: Arhivează proiect @@ -3726,7 +3855,7 @@ ro: label_duplicated_by: dublat de label_duplicate: duplicat label_duplicates: dublează - label_edit: Editează + label_edit: Editare label_edit_x: 'Editare: %{x}' label_view_x: 'View: %{x}' label_enable_multi_select: Comutare selecție multiplă @@ -3783,7 +3912,7 @@ ro: label_global_roles: Roluri globale label_git_path: Calea catre directorul .git label_greater_or_equal: ">=" - label_group_by: Grupează după + label_group_by: Grupare după label_group_new: Grupare nouă label_group: Grup label_group_named: Grup %{name} @@ -3833,7 +3962,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 @@ -3871,6 +4000,10 @@ ro: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Pictogramă personalizată label_custom_touch_icon: Pictogramă touch personalizată + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Deconectare label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Meniu lateral @@ -4946,6 +5079,7 @@ ro: ' setting_app_subtitle: Subtitlu aplicație setting_app_title: Titlu aplicație + setting_organization_name: Organization name setting_attachment_max_size: Dimensiune maximă atașament setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5092,12 +5226,12 @@ ro: setting_welcome_text: Text bloc "bun venit" setting_welcome_title: Titlu bloc "bun venit" setting_welcome_on_homescreen: Afişare bloc "bun venit" pe ecranul de start - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Mod de evidențiere implicit diff --git a/config/locales/crowdin/ru.yml b/config/locales/crowdin/ru.yml index 22b1e90d72e..d298f5f7a2c 100644 --- a/config/locales/crowdin/ru.yml +++ b/config/locales/crowdin/ru.yml @@ -114,7 +114,7 @@ ru: import: title: Импорт jira: - title: Импорт из Jira + title: Мигратор Jira description: Используйте этот инструмент для импорта данных из Вашего экземпляра Jira. Вы можете настроить несколько узлов Jira и выбрать, что импортировать в каждом запуске импорта. errors: cannot_delete_with_imports: Невозможно удалить хост Jira с существующими импортами @@ -125,8 +125,8 @@ ru: title: Конфигурация Jira new: Новая конфигурация banner: - title: Ограниченный импорт - description: 'Этот инструмент импорта в настоящее время находится в стадии бета-тестирования и может импортировать только основные данные: проекты, проблемы (имя, название, описание, вложения), пользователей (имя, электронная почта, членство в проекте), статусы и типы. Он не может импортировать рабочие процессы, пользовательские поля, отношения между заданиями или разрешения. В настоящее время мы поддерживаем только Jira Server/Data Center версий 10.x и 11.x. Облачные экземпляры на данный момент не поддерживаются.' + title: Ограниченные возможности импорта + description: 'Этот мигратор Jira в настоящее время находится в бета-версии и может импортировать только основные данные: проекты, проблемы (имя, заголовок, описание, вложения), пользователей (имя, электронная почта, членство в проекте), статусы и типы. Он не может импортировать рабочие процессы, пользовательские поля, отношения между заданиями или разрешения. В настоящее время мы поддерживаем только Jira Server/Data Center версий 10.x и 11.x. Облачные экземпляры на данный момент не поддерживаются.' form: fields: name: Имя @@ -154,6 +154,7 @@ ru: connection_timeout: 'Время подключения к серверу Jira истекло: %{message}' parse_error: 'Не удалось разобрать ответ Jira API: %{message}' api_error: Jira API вернул статус ошибки %{status} + 401_error: Jira API вернул ошибку 401. Ваш токен аутентификации может быть истёк или не хватает необходимых разрешений. Пожалуйста, убедитесь, что токен принадлежит администратору Jira. columns: projects: Проекты last_change: Последнее изменение @@ -162,7 +163,7 @@ ru: run: title: Выполнение импорта history: История - remove_error: Импорт Jira нельзя удалить во время его выполнения + remove_error: Запуск импорта Jira не может быть удалён, пока он запущен import_blocked_error: Другая операция импорта Jira находится в процессе выполнения или ожидает рассмотрения. Пожалуйста, завершите или отмените его, прежде чем начинать новый импорт. project_identifier_taken: 'Вы пытаетесь импортировать проект с уже использованным идентификатором: %{taken_identifier}. Пожалуйста, обновите идентификатор проекта в Jira, затем нажмите «Повторить».' blank: @@ -399,9 +400,9 @@ ru: notification_text_default: "

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

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

Спасибо

\n" work_packages_identifier: page_header: - description: Выберите между базовыми числовыми идентификаторами пакетов работ или специфическими для проекта, которые добавляют идентификатор проекта к идентификатору пакета работ. + description: Выберите между классическими числовыми идентификаторами пакета работ или семантическими, специфичными для проекта, которые добавляют идентификатор проекта к ID пакета работ. banner: - existing_identifiers_notice: 'Существующие идентификаторы для %{project_count} проектов не соответствуют требованиям к буквенно-цифровым идентификаторам проектов. OpenProject может автоматически обновить их, чтобы они были действительными, как показано в примерах ниже. Щелкните на ''Исправить и сохранить'', чтобы обновить таким образом идентификаторы для всех проектов и включить буквенно-цифровые идентификаторы на основе проекта. + existing_identifiers_notice: 'Существующие идентификаторы для проектов %{project_count} не соответствуют требованиям, предъявляемым к семантическим идентификаторам. OpenProject может автоматически обновить их, чтобы они были действительными, как показано в примерах ниже. Щелкните на ''Исправить и сохранить'', чтобы обновить идентификаторы для всех проектов и включить семантические идентификаторы на основе проекта. ' box_header: @@ -410,10 +411,14 @@ ru: label_autofixed_suggestion: Следующий идентификатор label_example_work_package_id: Пример ID пакета работ autofix_preview: - error_too_long: Должен быть менее 5 символов + error_too_long: Должно быть 10 символов или меньше + error_numerical: Не может быть чисто числовым + error_starts_with_number: Не может начинаться с цифры error_special_characters: Специальные символы не допускаются + error_not_fully_uppercased: Должно быть заглавными error_in_use: Уже используется в другом проекте error_reserved: Зарезервировано другим проектом + error_unknown: Требуется ручная проверка remaining_projects: one: "... еще 1 проект" few: "... еще %{count} проектов" @@ -697,6 +702,37 @@ ru: danger_dialog: confirmation_live_message_checked: Кнопка для продолжения активна. confirmation_live_message_unchecked: Кнопка для продолжения неактивна. Чтобы продолжить, поставьте галочку. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Разделение на страницы prev: Назад @@ -1376,6 +1412,20 @@ ru: index: no_results_title_text: В данный момент рабочих потоков нет. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1575,6 +1625,9 @@ ru: dependencies: Связи activerecord: attributes: + work_package_semantic_alias: + identifier: Идентификатор + work_package: Пакет работ jira_import: projects: Проекты import/jira: @@ -1583,7 +1636,7 @@ ru: personal_access_token: Персональный токен доступа import/jira_open_project_reference: jira: Jira - jira_import: Импорт из Jira + jira_import: Мигратор Jira announcements: show_until: Отобразить до attachment: @@ -1839,6 +1892,7 @@ ru: identity_url: URL идентификации parent: Родительская группа organizational_unit: Подразделение организации + group_users: Group users group_detail: parent: Родительская группа organizational_unit: Подразделение организации @@ -1956,6 +2010,7 @@ ru: confirmation: не совпадает со значением поля %{attribute}. could_not_be_copied: "%{dependency} не может быть скопировано (полностью)." does_not_exist: не существует. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} доступно только в корпоративной версии OpenProject." error_unauthorized: не доступен. error_readonly: запись не разрешена. @@ -2022,6 +2077,7 @@ ru: attributes: parent_id: circular_dependency: создаст круговую иерархию групп. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2546,7 +2602,6 @@ ru: ' base: 'Общая ошибка:' body: Сообщение - blocks_ids: Идентификаторы заблокированных рабочих пакетов category: Категория comment: Комментарий comments: Комментарий @@ -3043,6 +3098,7 @@ ru: title: Корпоративное дополнение plan_title: Дополнение корпоративной версии %{plan} plan_name: Корпоративная версия %{plan} + trial_text: Эта функция включена в Вашу активную пробную версию Enterprise. plan_text_html: Доступно начиная с %{plan_name}. unlimited: Без ограничений already_have_token: 'Уже есть токен? Добавьте его с помощью кнопки ниже, чтобы перейти на корпоративную лицензию. @@ -3514,6 +3570,11 @@ ru: quick_add: label: Добавить… my_account: + notifications_and_email: + title: Уведомления и email + tabs: + notifications: Настройки уведомлений + email_reminders: Почтовые напоминания access_tokens: description: Провайдерские токены выпускаются OpenProject, позволяя другим приложениям получать к ним доступ. Клиентские токены выпускаются другими приложениями, позволяя OpenProject получить к ним доступ. no_results: @@ -3576,6 +3637,72 @@ ru: disabled_text: Токены RSS не включены администратором. Пожалуйста, обратитесь к администратору, чтобы использовать эту функцию. storages: unknown_storage: Неизвестное хранилище + email_reminders: + immediate_reminders: + title: Отправить мне напоминание по электронной почте + mentioned: Сообщить мне, когда меня упомянут + personal_reminder: Уведомлять меня о личных напоминаниях + daily_reminders: + title: Отправлять мне ежедневные напоминания по электронной почте о непрочитанных уведомлениях + caption: Вы будете получать эти напоминания только для непрочитанных уведомлений и только в указанные Вами часы. Пока Вы не настроите часовой пояс для своей учетной записи, время будет интерпретироваться как UTC. + enabled: Включить ежедневные напоминания по электронной почте + add_time: Добавить время + remove_time: Удалить время + time_slot_label: Время напоминания (UTC) + workdays: + title: Получать напоминания по электронной почте в эти дни + submit_button: Обновить дни напоминаний + pause_reminders: + title: Приостановить уведомления по электронной почте + enabled: Временно приостановить ежедневные напоминания по электронной почте + date_range: Период приостановки + email_alerts: + title: Уведомления по электронной почте для других элементов (которые не являются пакетами работ) + news_added: Новость добавлена + news_commented: Комментарий к новости + document_added: Документ добавлен + forum_messages: Сообщение на форуме + wiki_page_added: Wiki-страница добавлена + wiki_page_updated: Wiki-страница обновлена + membership_added: Членство добавлено + membership_updated: Членство обновлено + submit_button: Обновить оповещения + notifications: + participating: + title: Участие + submit_button: Обновить настройки + mentioned: Упомянутый + watched: Отслеживает + assignee: Назначенный + responsible: Ответственный + shared: Доступные мне + date_alerts: + title: Дата оповещения + submit_button: Обновить дату оповещения + start_date: Дата начала + due_date: Дата окончания + overdue: Просрочено + times: + same_day: В тот же день + one_day_before: За 1 день до + three_days_before: За 3 дня до + seven_days_before: За 7 дней до + one_day_after: Через 1 день после + three_days_after: Через 3 дня после + seven_days_after: Через 7 дней после + non_participating: + title: Неучастие + submit_button: Обновить настройки + work_package_created: Новые пакеты работ + work_package_commented: Все новые комментарии + work_package_processed: Все изменения статуса + work_package_prioritized: Все изменения приоритетов + work_package_scheduled: Все изменения дат + project_specific_settings: + title: Настройки уведомлений для конкретного проекта + add_button: Добавить уведомления по проекту + dialog_title: Добавить уведомления по проекту + list_header: Проекты с особыми уведомлениями notifications: reasons: assigned: Исполнитель @@ -3607,6 +3734,7 @@ ru: invalid_filter: Неверный фильтр уведомлений label_accessibility: Спец. возможности label_account: Учетная запись + label_actions: Действия label_active: Активен label_activate_user: Активировать пользователя label_active_in_new_projects: Активное участие в новых проектах @@ -3647,6 +3775,7 @@ ru: label_ical_access_key_generation_hint: Автоматически генерируется при подписке на календарь. label_ical_access_key_latest: последний label_ical_access_key_revoke: Отозвать + label_integrations: Интеграции label_add_column: Добавить столбец label_applied_status: Прикладной статус label_archive_project: Архивировать проект @@ -3897,7 +4026,7 @@ ru: label_external_links: Внешние ссылки label_locale: Язык и регион label_jump_to_a_project: Перейти к проекту... - label_jira_import: Импорт из Jira + label_jira_import: Мигратор Jira label_keyword_plural: Ключевые слова label_language_based: Основанный на языке пользователя label_last_activity: Последняя активность @@ -3935,6 +4064,10 @@ ru: label_custom_pdf_export_settings: Настройки экспорта PDF label_custom_favicon: Пользовательские иконки label_custom_touch_icon: Пользовательский значок логотипа + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Выход label_mapping_for: 'Привязки для: %{attribute}' label_main_menu: Боковое меню @@ -5003,6 +5136,7 @@ ru: ' setting_app_subtitle: Второе название приложения setting_app_title: Название приложения + setting_organization_name: Organization name setting_attachment_max_size: Максимальный размер вложения setting_show_work_package_attachments: По умолчанию показывать вложения на вкладке "Файлы". setting_antivirus_scan_mode: Режим сканирования @@ -5149,12 +5283,12 @@ ru: setting_welcome_text: Текст приветствия setting_welcome_title: Заголовок приветствия setting_welcome_on_homescreen: Показывать приветствие на домашней странице - setting_work_packages_identifier_numeric: Числовая последовательность по умолчанию (по умолчанию) - setting_work_packages_identifier_numeric_caption: 'Каждый пакет работ получает порядковый номер, начинающийся с 1 и увеличивающийся с каждым новым пакетом. Номера уникальны в пределах данного экземпляра, поэтому они остаются неизменными, даже если пакеты работ перемещаются между проектами. + setting_work_packages_identifier_classic: Числовая последовательность для всего экземпляра (по умолчанию) + setting_work_packages_identifier_classic_caption: 'Каждый пакет работ получает порядковый номер, начинающийся с 1 и увеличивающийся с каждым новым пакетом. Номера уникальны в пределах данного экземпляра, поэтому они остаются неизменными, даже если пакеты работ перемещаются между проектами. ' - setting_work_packages_identifier_alphanumeric: Семантические идентификаторы на основе проекта - setting_work_packages_identifier_alphanumeric_caption: 'У каждого проекта есть уникальный идентификатор, префикс идентификатора пакета работ. Если пакет работ перемещен в другой проект, генерируется новый идентификатор, но старый продолжает работать. + setting_work_packages_identifier_semantic: Семантические идентификаторы на основе проекта + setting_work_packages_identifier_semantic_caption: 'Каждый проект имеет уникальный идентификатор, который является префиксом к идентификатору рабочего пакета. Если рабочий пакет перемещается в другой проект, генерируется новый идентификатор, но старый продолжает функционировать. ' setting_work_package_list_default_highlighting_mode: Способ выделения по умолчанию diff --git a/config/locales/crowdin/rw.yml b/config/locales/crowdin/rw.yml index 7711ffd5f64..74008c195c4 100644 --- a/config/locales/crowdin/rw.yml +++ b/config/locales/crowdin/rw.yml @@ -114,7 +114,7 @@ rw: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ rw: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ rw: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ rw: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ rw: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ rw: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ rw: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ rw: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ rw: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ rw: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ rw: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ rw: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ rw: avatar: Avatar base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ rw: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ rw: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ rw: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ rw: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ rw: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ rw: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ rw: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ rw: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/si.yml b/config/locales/crowdin/si.yml index 9b00ffeec88..020aa93d340 100644 --- a/config/locales/crowdin/si.yml +++ b/config/locales/crowdin/si.yml @@ -114,7 +114,7 @@ si: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ si: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ si: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ si: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ si: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ si: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ si: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ si: index: no_results_title_text: දැනට වැඩ ප්රවාහයන් නොමැත. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ si: dependencies: පරායත්තතා activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ si: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ si: confirmation: "%{attribute}නොගැලපේ." could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: නොපවතී. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: ප්රවේශ විය නොහැක. error_readonly: ලිවීමට උත්සාහ කළ නමුත් එය ලිවිය නොහැක. @@ -1983,6 +2038,7 @@ si: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ si: avatar: Avatar base: 'සාමාන්ය දෝෂය:' body: Body - blocks_ids: අවහිර කරන ලද වැඩ පැකේජ වල IDS category: ප්රවර්ගය comment: අදහස් දක්වන්න comments: අදහස් දක්වන්න @@ -2922,6 +2977,7 @@ si: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ si: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ si: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: අස්ගිනී @@ -3484,6 +3611,7 @@ si: invalid_filter: Invalid notification filter label_accessibility: ප්රවේශ්යතාව label_account: ගිණුම + label_actions: ක්‍රියාමාර්ග label_active: ක්රියාකාරී label_activate_user: පරිශීලකයා සක්රිය කරන්න label_active_in_new_projects: නව ව්යාපෘතිවල ක්රියාකාරී වේ @@ -3524,6 +3652,7 @@ si: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: අවලංගු + label_integrations: Integrations label_add_column: Add column label_applied_status: ව්යවහාරික තත්ත්වය label_archive_project: සංරක්ෂිත ව්යාපෘතිය @@ -3774,7 +3903,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: අවසාන ක්රියාකාරකම් @@ -3812,6 +3941,10 @@ si: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: අභිරුචි ප්රියතම label_custom_touch_icon: අභිරුචි ස්පර්ශ අයිකනය + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: ලියාපදිංචි වන්න label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: පැති මෙනුව @@ -4884,6 +5017,7 @@ si: ' setting_app_subtitle: අයදුම් උප සිරැසි setting_app_title: අයදුම් මාතෘකාව + setting_organization_name: Organization name setting_attachment_max_size: ඇමුණුමක් උපරිම. ප්රමාණය setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ si: setting_welcome_text: බ්ලොක් පෙළ සාදරයෙන් පිළිගනිමු setting_welcome_title: වාරණ මාතෘකාව සාදරයෙන් පිළිගනිමු setting_welcome_on_homescreen: homescreen මත ප්රදර්ශනය පිළිගැනීමේ වාරණ - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: පෙරනිමි ඉස්මතු මාදිලිය diff --git a/config/locales/crowdin/sk.yml b/config/locales/crowdin/sk.yml index bf84e25d6c0..1aacb9cd243 100644 --- a/config/locales/crowdin/sk.yml +++ b/config/locales/crowdin/sk.yml @@ -114,7 +114,7 @@ sk: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ sk: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ sk: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ sk: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -399,9 +400,9 @@ sk: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -410,10 +411,14 @@ sk: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -430,7 +435,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 @@ -698,6 +703,37 @@ sk: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1375,6 +1411,20 @@ sk: index: no_results_title_text: Momentálne neexistujú žiadne toky činností. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1572,6 +1622,9 @@ sk: dependencies: Závislosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1580,7 +1633,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: @@ -1836,6 +1889,7 @@ sk: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1953,6 +2007,7 @@ sk: confirmation: "%{attribute} sa nezhoduje." could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: neexistuje. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2019,6 +2074,7 @@ sk: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2539,7 +2595,6 @@ sk: avatar: Avatar base: 'Všeobecná chyba:' body: Body - blocks_ids: Identifikátory blokovaných pracovných balíkov category: Kategória comment: Komentár comments: Komentár @@ -3036,6 +3091,7 @@ sk: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3507,6 +3563,11 @@ sk: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3569,6 +3630,72 @@ sk: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Priradené @@ -3600,6 +3727,7 @@ sk: invalid_filter: Invalid notification filter label_accessibility: Prístupnosť label_account: Účet + label_actions: Akcie label_active: Aktívny label_activate_user: Aktívny užívateľ label_active_in_new_projects: Aktívny v nových projektoch @@ -3640,6 +3768,7 @@ sk: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Odvolať + label_integrations: Integrations label_add_column: Add column label_applied_status: Aplikovaný stav label_archive_project: Archivovať projekt @@ -3890,7 +4019,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 @@ -3928,6 +4057,10 @@ sk: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Vlastná favicon label_custom_touch_icon: Vlastná dotyková ikona + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Odhlásiť sa label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Vedľajšie ponuky @@ -5012,6 +5145,7 @@ sk: ' setting_app_subtitle: Podtitulok aplikácie setting_app_title: Názov aplikácie + setting_organization_name: Organization name setting_attachment_max_size: Maximálna veľkosť prílohy setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5158,12 +5292,12 @@ sk: setting_welcome_text: Text pre uvítací blok setting_welcome_title: Názov uvítacieho bloku setting_welcome_on_homescreen: Zobraziť uvítací blok na úvodnej obrazovke - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Predvolený režim zvýraznenia diff --git a/config/locales/crowdin/sl.yml b/config/locales/crowdin/sl.yml index f066e4f040a..3272b0a4241 100644 --- a/config/locales/crowdin/sl.yml +++ b/config/locales/crowdin/sl.yml @@ -116,7 +116,7 @@ sl: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -127,8 +127,8 @@ sl: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -156,6 +156,7 @@ sl: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -164,7 +165,7 @@ sl: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -401,9 +402,9 @@ sl: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -412,10 +413,14 @@ sl: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" two: "... %{count} more projects" @@ -432,7 +437,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 @@ -699,6 +704,37 @@ sl: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1376,6 +1412,20 @@ sl: index: no_results_title_text: Trenutno ni poslovnih procesov work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1573,6 +1623,9 @@ sl: dependencies: Odvisnosti activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1581,7 +1634,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: @@ -1837,6 +1890,7 @@ sl: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1954,6 +2008,7 @@ sl: confirmation: se ne ujema %{attribute} could_not_be_copied: "%{dependency} ni bilo mogoče (v celoti) kopirati." does_not_exist: ne obstaja + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: morda ni mogoče dostopati. error_readonly: poskušano je bilo napisati, vendar ni mogoče pisati. @@ -2020,6 +2075,7 @@ sl: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2548,7 +2604,6 @@ sl: avatar: Avatar base: 'Splošna napaka:' body: Body - blocks_ids: ID blokiranih delovnih paketov category: Kategorija comment: Komentar comments: Komentar @@ -2866,8 +2921,8 @@ sl: - avgust - september - oktober - - november - - december + - November + - December order: - :leto - :mesec @@ -3045,6 +3100,7 @@ sl: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Neomejeno already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3520,6 +3576,11 @@ sl: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3582,6 +3643,72 @@ sl: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Prevzemnik @@ -3613,6 +3740,7 @@ sl: invalid_filter: Invalid notification filter label_accessibility: Dostopnost label_account: Račun + label_actions: Akcije label_active: Aktivno label_activate_user: Aktivirajte uporabnika label_active_in_new_projects: Aktivno v novih projektih @@ -3653,6 +3781,7 @@ sl: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Prekliči + label_integrations: Integrations label_add_column: Add column label_applied_status: Uveljavljeno stanje label_archive_project: Arhivirani projekti @@ -3903,7 +4032,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 @@ -3941,6 +4070,10 @@ sl: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Prilagodi favicon label_custom_touch_icon: Ikona za dotik po meri + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Odjava label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Stranski meni @@ -5029,6 +5162,7 @@ sl: ' setting_app_subtitle: Podnaslov aplikacije setting_app_title: Naslov aplikacije + setting_organization_name: Organization name setting_attachment_max_size: Največja velikost priponke setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5181,12 +5315,12 @@ sl: setting_welcome_text: Dobrodošli blok besedila setting_welcome_title: Dobrodošli blok naslov setting_welcome_on_homescreen: Prikažite blok dobrodošlice na osnovnem zaslonu - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Privzeti način osvetlitve diff --git a/config/locales/crowdin/sr.yml b/config/locales/crowdin/sr.yml index 329a70a966d..d8a7eb7a63b 100644 --- a/config/locales/crowdin/sr.yml +++ b/config/locales/crowdin/sr.yml @@ -114,7 +114,7 @@ sr: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ sr: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ sr: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ sr: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -393,9 +394,9 @@ sr: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -404,10 +405,14 @@ sr: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" few: "... %{count} more projects" @@ -423,7 +428,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 @@ -690,6 +695,37 @@ sr: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1358,6 +1394,20 @@ sr: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1554,6 +1604,9 @@ sr: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1562,7 +1615,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: @@ -1818,6 +1871,7 @@ sr: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1935,6 +1989,7 @@ sr: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -2001,6 +2056,7 @@ sr: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2503,7 +2559,6 @@ sr: avatar: Avatar base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2980,6 +3035,7 @@ sr: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3450,6 +3506,11 @@ sr: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3512,6 +3573,72 @@ sr: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Zadužen @@ -3543,6 +3670,7 @@ sr: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3583,6 +3711,7 @@ sr: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3833,7 +3962,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 @@ -3871,6 +4000,10 @@ sr: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4948,6 +5081,7 @@ sr: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5094,12 +5228,12 @@ sr: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/sv.yml b/config/locales/crowdin/sv.yml index a3b8b2602e8..a8fcc9c1ca4 100644 --- a/config/locales/crowdin/sv.yml +++ b/config/locales/crowdin/sv.yml @@ -114,7 +114,7 @@ sv: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ sv: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ sv: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ sv: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ sv: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ sv: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ sv: danger_dialog: confirmation_live_message_checked: Knappen för att fortsätta är nu aktiv. confirmation_live_message_unchecked: Knappen för att fortsätta är nu inaktiv. Du måste markera kryssrutan för att fortsätta. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ sv: index: no_results_title_text: Det finns för närvarande inga arbetsflöden. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ sv: dependencies: Beroenden activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ sv: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ sv: confirmation: matchar inte %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: finns inte. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: åtkomst nekas. error_readonly: försökte skrivas men är inte skrivbart. @@ -1983,6 +2038,7 @@ sv: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ sv: avatar: Avatar base: 'Allmänt fel:' body: Body - blocks_ids: ID:n för blockerade arbetspaket category: Kategori comment: Kommentar comments: Kommentar @@ -2922,6 +2977,7 @@ sv: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Obegränsad already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ sv: quick_add: label: Lägg till… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ sv: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Okänd lagring + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Tilldelad till @@ -3484,6 +3611,7 @@ sv: invalid_filter: Invalid notification filter label_accessibility: Tillgänglighet label_account: Konto + label_actions: Åtgärder label_active: Aktiv label_activate_user: Aktivera användare label_active_in_new_projects: Aktiv i nya projekt @@ -3524,6 +3652,7 @@ sv: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: senaste label_ical_access_key_revoke: Återkalla + label_integrations: Integrations label_add_column: Lägg till kolumn label_applied_status: Tillämpad status label_archive_project: Arkivera projekt @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ sv: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Anpassad favicon label_custom_touch_icon: Anpassad touch-ikon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Logga ut label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Sidomenyn @@ -4872,6 +5005,7 @@ sv: ' setting_app_subtitle: Undertitel för applikation setting_app_title: Programnamn + setting_organization_name: Organization name setting_attachment_max_size: Bilaga max. storlek setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5018,12 +5152,12 @@ sv: setting_welcome_text: Textblock för välkomstmeddelande setting_welcome_title: Textblock för välkomsttitel setting_welcome_on_homescreen: Visa välkomstblocket på hemsidan - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Standardläge för markering diff --git a/config/locales/crowdin/th.yml b/config/locales/crowdin/th.yml index a8f5aca3495..72371829626 100644 --- a/config/locales/crowdin/th.yml +++ b/config/locales/crowdin/th.yml @@ -114,7 +114,7 @@ th: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ th: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ th: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ th: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +382,9 @@ th: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +393,14 @@ th: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +414,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 @@ -674,6 +679,37 @@ th: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1324,6 +1360,20 @@ th: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1518,6 +1568,9 @@ th: dependencies: ส่วนที่อ้างอิง activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1526,7 +1579,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: @@ -1782,6 +1835,7 @@ th: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1899,6 +1953,7 @@ th: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1965,6 +2020,7 @@ th: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2427,7 +2483,6 @@ th: avatar: รูปโปรไฟล์ base: 'ข้อผิดพลาดทั่วไป:' body: Body - blocks_ids: Id ของแพคเกจการทำงานที่ถูกบล็อก category: ประเภท comment: ความคิดเห็น comments: ความคิดเห็น @@ -2864,6 +2919,7 @@ th: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3332,6 +3388,11 @@ th: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3394,6 +3455,72 @@ th: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: ผู้ได้รับมอบหมาย @@ -3425,6 +3552,7 @@ th: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: บัญชี + label_actions: Actions label_active: ใช้งานอยู่ label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3465,6 +3593,7 @@ th: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: ใช้สถานะ label_archive_project: Archive project @@ -3715,7 +3844,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 @@ -3753,6 +3882,10 @@ th: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: ออกจากระบบ label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: เมนูด้านข้าง @@ -4820,6 +4953,7 @@ th: ' setting_app_subtitle: หัวข้อย่อยของแอบพลิเคชั่น setting_app_title: หัวข้อแอบพลิเคชั่น + setting_organization_name: Organization name setting_attachment_max_size: ขนาดสูงสุดของไฟล์แนบ setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -4966,12 +5100,12 @@ th: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/tr.yml b/config/locales/crowdin/tr.yml index b490ab99604..6fb48551fb9 100644 --- a/config/locales/crowdin/tr.yml +++ b/config/locales/crowdin/tr.yml @@ -114,7 +114,7 @@ tr: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ tr: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ tr: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ tr: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ tr: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ tr: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -683,6 +688,37 @@ tr: danger_dialog: confirmation_live_message_checked: Devam etmek için düğme artık aktif. confirmation_live_message_unchecked: Devam etmek için düğme artık aktif değil. Devam etmek için işratlemeniz gerekli. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1342,6 +1378,20 @@ tr: index: no_results_title_text: Sitemizde şuan iş akışı yok. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1537,6 +1587,9 @@ tr: dependencies: Bağımlılıklar activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1545,7 +1598,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: @@ -1801,6 +1854,7 @@ tr: identity_url: Kimlik URL'si parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1918,6 +1972,7 @@ tr: confirmation: "%{attribute} eşleşmiyor." could_not_be_copied: "%{dependency} (tam olarak) kopyalanamadı." does_not_exist: mevcut değil. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: erişilemez. error_readonly: yazılmaya çalışıldı fakat yazılabilir değil. @@ -1988,6 +2043,7 @@ tr: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2470,7 +2526,6 @@ tr: avatar: Avatar base: 'Genel Hata:' body: Gövde - blocks_ids: Engellenen iş paketlerinin ID'leri category: Kategori comment: Yorum comments: Yorum @@ -2927,6 +2982,7 @@ tr: title: Kurumsal eklenti plan_title: Kurumsal %{plan} eklentisi plan_name: "%{plan} kurumsal plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: "%{plan_name} ile başlayarak kullanılabilir." unlimited: Sınırsız already_have_token: 'Zaten bir anahtarınız mı var? Rezervasyonlu Enterprise plana yükseltmek için aşağıdaki düğmeyi kullanarak ekleyin. @@ -3396,6 +3452,11 @@ tr: quick_add: label: Ekle… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3458,6 +3519,72 @@ tr: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Bilinmeyen depolama + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Atanan @@ -3489,6 +3616,7 @@ tr: invalid_filter: Geçersiz bildirim filtresi label_accessibility: Erişilebilirlik label_account: Hesap + label_actions: Eylemler label_active: Aktif label_activate_user: Aktif kullanıcı label_active_in_new_projects: Yeni projelerde etkin @@ -3529,6 +3657,7 @@ tr: label_ical_access_key_generation_hint: Bir takvime abone olunduğunda otomatik olarak oluşturulur. label_ical_access_key_latest: en son label_ical_access_key_revoke: İptal et + label_integrations: Integrations label_add_column: Sütun ekle label_applied_status: Uygulanan statü label_archive_project: Projeyi arşivle @@ -3779,7 +3908,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 @@ -3817,6 +3946,10 @@ tr: label_custom_pdf_export_settings: Özel PDF dışa aktarma ayarları label_custom_favicon: Özel favicon label_custom_touch_icon: Özel dokunma simgesi + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Oturumu kapat label_mapping_for: "%{attribute} için eşleme" label_main_menu: Yan menü @@ -4888,6 +5021,7 @@ tr: ' setting_app_subtitle: Uygulama alt başlığı setting_app_title: Uygulama başlığı + setting_organization_name: Organization name setting_attachment_max_size: En büyük ek boyutu setting_show_work_package_attachments: Ekleri varsayılan olarak dosyalar sekmesinde göster setting_antivirus_scan_mode: Tarama modu @@ -5032,12 +5166,12 @@ tr: setting_welcome_text: Hoş geldiniz blok metini setting_welcome_title: Hoş geldiniz blok başlığı setting_welcome_on_homescreen: Hoşgeldiniz bloğunu ana ekranda göster - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Varsayılan vurgulama modu diff --git a/config/locales/crowdin/uk.yml b/config/locales/crowdin/uk.yml index ae2569fde7f..0ed8fee4164 100644 --- a/config/locales/crowdin/uk.yml +++ b/config/locales/crowdin/uk.yml @@ -114,7 +114,7 @@ uk: import: title: Iмпорт jira: - title: Імпорт із Jira + title: Jira Migrator description: Використовуйте цей інструмент для імпорту даних зі свого екземпляра Jira. Ви можете налаштувати кілька хостів Jira й вибрати, що імпортувати в кожному циклі імпорту. errors: cannot_delete_with_imports: Не вдається видалити хост Jira з наявними процесами імпорту @@ -125,8 +125,8 @@ uk: title: Конфігурація Jira new: Нова конфігурація banner: - title: Обмежений імпорт - description: 'Цей інструмент зараз доступний лише як бета-версія і може імпортувати тільки основні дані: проєкти, задачі (назву, заголовок, опис, вкладення), користувачів (ім’я, електронну адресу, дані про участь у проєктах), статуси й типи. Він не може імпортувати робочі процеси, користувацькі поля, зв’язки між задачами чи дозволи. Зараз ми підтримуємо лише версії Jira Server / Data Center 10.x і 11.x. Хмарні екземпляри поки що не підтримуються.' + title: Обмежені можливості імпорту + description: 'Інструмент Jira Migrator зараз доступний лише як бета-версія і може імпортувати тільки основні дані: проєкти, задачі (назву, заголовок, опис, вкладення), користувачів (ім’я, електронну адресу, дані про участь у проєктах), статуси й типи. Він не може імпортувати робочі процеси, користувацькі поля, зв’язки між задачами чи дозволи. Зараз ми підтримуємо лише версії Jira Server / Data Center 10.x і 11.x. Хмарні екземпляри поки що не підтримуються.' form: fields: name: Назва @@ -154,6 +154,7 @@ uk: connection_timeout: 'Час очікування на встановлення з’єднання до сервера Jira минув: %{message}' parse_error: 'Не вдалося проаналізувати відповідь Jira API: %{message}' api_error: Інтерфейс Jira API повернув статус помилки «%{status}» + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Проєкти last_change: Остання зміна @@ -162,7 +163,7 @@ uk: run: title: Цикл імпорту history: Історія - remove_error: Імпорт Jira не можна видалити, поки він виконується + remove_error: Цикл імпорту Jira не можна видалити, поки він виконується import_blocked_error: Зараз виконується або очікує на перевірку ще один цикл імпорту Jira. Завершіть або скасуйте його, якщо потрібно почати новий імпорт. project_identifier_taken: 'Ви намагаєтесь імпортувати проєкт з ідентифікатором, який уже використовується: %{taken_identifier}. Будь ласка, оновіть ідентифікатор проєкту в Jira, а потім натисніть на кнопку «Повторити спробу».' blank: @@ -399,9 +400,9 @@ uk: notification_text_default: "

Вітаємо,

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

Дякуємо

\n" work_packages_identifier: page_header: - description: Виберіть звичайні числові ідентифікатори пакетів робіт або ідентифікатори із зазначенням проєкту (до ідентифікатора пакета робіт додається префікс з ідентифікатором проєкту). + description: Виберіть класичні числові ідентифікатори пакетів робіт або семантичні із зазначенням проєкту (до ідентифікатора пакета робіт додається префікс з ідентифікатором проєкту). banner: - existing_identifiers_notice: 'Наявні ідентифікатори для кількох проєктів (%{project_count}) не відповідають вимогам до буквено-цифрових ідентифікаторів із зазначенням проєктів. OpenProject може автоматично оновити їх, щоб вони стали дійсними, як показано в прикладах нижче. Натисніть кнопку «Виправити й зберегти», щоб оновити в такий спосіб ідентифікатори для всіх проєктів і ввімкнути буквено-цифрові ідентифікатори із зазначенням проєктів. + existing_identifiers_notice: 'Наявні ідентифікатори для кількох проєктів (%{project_count}) не відповідають вимогам до семантичних ідентифікаторів із зазначенням проєктів. OpenProject може автоматично оновити їх, щоб вони стали дійсними, як показано в прикладах нижче. Натисніть кнопку «Виправити й зберегти», щоб оновити в такий спосіб ідентифікатори для всіх проєктів і ввімкнути семантичні ідентифікатори із зазначенням проєктів. ' box_header: @@ -410,10 +411,14 @@ uk: label_autofixed_suggestion: Майбутній ідентифікатор label_example_work_package_id: Приклад ідентифікатора пакета робіт autofix_preview: - error_too_long: Має містити менше ніж 5 символів + error_too_long: Має містити щонайбільше 10 символів + error_numerical: Не може містити лише числа + error_starts_with_number: Не може починатись із числа error_special_characters: Не дозволяється використовувати спеціальні символи + error_not_fully_uppercased: Літери мають бути великими error_in_use: Уже використовується як активний дескриптор іншого проєкту error_reserved: Зарезервовано в історії дескрипторів іншого проєкту + error_unknown: Потребує ручної перевірки remaining_projects: one: "… ще 1 проєкт" few: "… ще %{count} проєкти" @@ -430,7 +435,7 @@ uk: checkbox_label: Я розумію, що ця дія назавжди змінить усі ідентифікатори пакетів робіт success_banner: Формат ідентифікаторів пакетів робіт успішно оновлено. in_progress: - banner_message: Ідентифікатори проєктів зараз замінюються на буквено-цифрові ідентифікатори із зазначенням проєктів. Це може зайняти деякий час. + banner_message: Ідентифікатори проєктів зараз замінюються на семантичні ідентифікатори із зазначенням проєктів. Це може зайняти деякий час. workflows: tabs: default_transitions: Стандартні переходи @@ -698,6 +703,37 @@ uk: danger_dialog: confirmation_live_message_checked: Кнопка для продовження активна. confirmation_live_message_unchecked: Кнопка для продовження зараз неактивна. Щоб продовжити, поставте прапорець. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Розбиття на сторінки prev: Попереднє @@ -1372,6 +1408,20 @@ uk: index: no_results_title_text: Наразі немає робочих процесів. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1567,6 +1617,9 @@ uk: dependencies: Залежності activerecord: attributes: + work_package_semantic_alias: + identifier: Ідентифікатор + work_package: Робочий пакет jira_import: projects: Проєкти import/jira: @@ -1575,7 +1628,7 @@ uk: personal_access_token: Персональний маркер доступу import/jira_open_project_reference: jira: Jira - jira_import: Імпорт із Jira + jira_import: Jira Migrator announcements: show_until: Показувати до attachment: @@ -1831,6 +1884,7 @@ uk: identity_url: URL-адреса ідентичності parent: Батьківська група organizational_unit: Організаційний підрозділ + group_users: Group users group_detail: parent: Батьківська група organizational_unit: Організаційний підрозділ @@ -1948,6 +2002,7 @@ uk: confirmation: не збігається %{attribute} could_not_be_copied: "%{dependency} не вдалося скопіювати (повністю)." does_not_exist: не існує. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} можна лише у версії OpenProject Enterprise." error_unauthorized: "– можливо, немає доступу." error_readonly: "– було здійснено спробу запису, але елемент недоступний для запису." @@ -2016,6 +2071,7 @@ uk: attributes: parent_id: circular_dependency: призведе до створення ієрархії циклічної групи. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2540,7 +2596,6 @@ uk: avatar: Аватар base: 'Загальна помилка:' body: Текст - blocks_ids: Ідентифікатори заблокованих робочих пакетів category: Категорія comment: Коментар comments: Коментар @@ -3037,6 +3092,7 @@ uk: title: Доповнення версії Enterprise plan_title: Доповнення версії Enterprise %{plan} plan_name: Версія Enterprise %{plan} + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Доступно, починаючи з версії %{plan_name}. unlimited: Без обмежень already_have_token: 'Уже маєте маркер? Додайте його за допомогою кнопки нижче, щоб перейти на вже замовлений план Enterprise. @@ -3508,6 +3564,11 @@ uk: quick_add: label: Додати… my_account: + notifications_and_email: + title: Сповіщення й електронні листи + tabs: + notifications: Налаштування сповіщень + email_reminders: Нагадування електронною поштою access_tokens: description: Маркери постачальника послуг випускаються в OpenProject, що дає змогу іншим додаткам отримувати до них доступ. Клієнтські маркери випускаються іншими додатками, що дає змогу OpenProject отримати до них доступ. no_results: @@ -3570,6 +3631,72 @@ uk: disabled_text: Маркери API не ввімкнено адміністратором. Зверніться до нього, якщо вам потрібна ця функція. storages: unknown_storage: Невідоме сховище + email_reminders: + immediate_reminders: + title: Надсилати мені нагадування електронною поштою + mentioned: Повідомляти мене, коли мене згадують + personal_reminder: Повідомляти мене про персональні нагадування + daily_reminders: + title: Надсилати мені щоденні нагадування електронною поштою про непрочитані сповіщення + caption: Ви отримуватимете лише нагадування про непрочитані сповіщення, і вони надходитимуть тільки у вказаний вами час. Доки ви не налаштуєте для свого облікового запису часовий пояс, використовуватиметься UTC. + enabled: Увімкнути щоденні нагадування електронною поштою + add_time: Додати час + remove_time: Вилучити час + time_slot_label: Час нагадування (UTC) + workdays: + title: Отримувати нагадування електронною поштою в ці дні + submit_button: Оновити дні нагадувань + pause_reminders: + title: Призупинити сповіщення електронною поштою + enabled: Тимчасово призупинити щоденні нагадування, що надсилаються електронною поштою + date_range: Період призупинення + email_alerts: + title: Сповіщення електронною поштою для інших елементів, які не є пакетами робіт + news_added: Новину додано + news_commented: Коментар щодо новини + document_added: Документ додано + forum_messages: Повідомлення на форумі опубліковано + wiki_page_added: Wiki-сторінку додано + wiki_page_updated: Wiki-сторінку оновлено + membership_added: Членство додано + membership_updated: Членство оновлено + submit_button: Оновити сповіщення + notifications: + participating: + title: Бере участь + submit_button: Оновити налаштування + mentioned: Вас згадали + watched: Відстеження + assignee: Виконавець + responsible: Відповідальний + shared: Доступ надано мені + date_alerts: + title: Сповіщення про дати + submit_button: Оновити сповіщення про дати + start_date: Дата початку + due_date: Дата завершення + overdue: Прострочено + times: + same_day: Того самого дня + one_day_before: За 1 день + three_days_before: За 3 дні + seven_days_before: За 7 днів + one_day_after: Через 1 день + three_days_after: Через 3 дні + seven_days_after: Через 7 днів + non_participating: + title: Не бере участі + submit_button: Оновити налаштування + work_package_created: Нові пакети робіт + work_package_commented: Усі нові коментарі + work_package_processed: Усі зміни статусу + work_package_prioritized: Усі зміни пріоритету + work_package_scheduled: Усі зміни дат + project_specific_settings: + title: Налаштування сповіщень щодо проєктів + add_button: Додати сповіщення щодо проєктів + dialog_title: Додати сповіщення щодо проєктів + list_header: Проєкти зі спеціальними сповіщеннями notifications: reasons: assigned: Виконавець @@ -3601,6 +3728,7 @@ uk: invalid_filter: Недійсний фільтр сповіщень label_accessibility: Розробникам також потрібно оплачувати свої рахунки. З доступністю label_account: Обліковий запис + label_actions: Дії label_active: Активні label_activate_user: Активні користувачі label_active_in_new_projects: Активна участь в нових проектах @@ -3641,6 +3769,7 @@ uk: label_ical_access_key_generation_hint: Автоматично створено під час оформлення підписки на календар. label_ical_access_key_latest: останні label_ical_access_key_revoke: Анулювати + label_integrations: Інтеграції label_add_column: Додати стовпець label_applied_status: Застосовний статус label_archive_project: Архівний проект @@ -3869,7 +3998,7 @@ uk: label_index_by_title: Індекс за назвою label_information: Інформація label_information_plural: Інформація - label_installation_guides: Інструкції із встановлення + label_installation_guides: Інструкції зі встановлення label_integer: Ціле число label_interface: Інтерфейс label_internal: Власне @@ -3891,7 +4020,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: Остання активність @@ -3929,6 +4058,10 @@ uk: label_custom_pdf_export_settings: Налаштування експорту PDF label_custom_favicon: призначені для користувача іконки label_custom_touch_icon: Призначений для користувача значок логотипу + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Вийти label_mapping_for: 'Зіставлення для: %{attribute}' label_main_menu: Бокове меню @@ -5010,6 +5143,7 @@ uk: ' setting_app_subtitle: Підзаголовок додатку setting_app_title: Назва додатку + setting_organization_name: Organization name setting_attachment_max_size: Максимальний розмір вкладення setting_show_work_package_attachments: За замовчуванням показувати вкладення на вкладці «Файли» setting_antivirus_scan_mode: Режим перевірки @@ -5156,12 +5290,12 @@ uk: setting_welcome_text: Текст блоку привітання setting_welcome_title: Заголовок вітального блоку setting_welcome_on_homescreen: Відображати вітальний блок на робочому столі - setting_work_packages_identifier_numeric: Числова послідовність для всього екземпляра (за умовчанням) - setting_work_packages_identifier_numeric_caption: 'Кожному пакету робіт присвоюється порядковий номер, який починається з одиниці й збільшується з кожним новим пакетом. Номери є унікальними в межах цього екземпляра, тому залишаються незмінними, навіть якщо пакети робіт переміщуються між проєктами. + setting_work_packages_identifier_classic: Числова послідовність для всього екземпляра (за умовчанням) + setting_work_packages_identifier_classic_caption: 'Кожному пакету робіт присвоюється порядковий номер, який починається з одиниці й збільшується з кожним новим пакетом. Номери є унікальними в межах цього екземпляра, тому залишаються незмінними, навіть якщо пакети робіт переміщуються між проєктами. ' - setting_work_packages_identifier_alphanumeric: Буквено-цифрові ідентифікатори із зазначенням проєктів - setting_work_packages_identifier_alphanumeric_caption: 'Кожен проєкт має унікальний ідентифікатор, який додається як префікс до ідентифікатора пакета робіт. Якщо пакет робіт переміщується в інший проєкт, генерується новий ідентифікатор, однак старий продовжує працювати. + setting_work_packages_identifier_semantic: Семантичні ідентифікатори із зазначенням проєктів + setting_work_packages_identifier_semantic_caption: 'Кожен проєкт має унікальний ідентифікатор, який додається як префікс до ідентифікатора пакета робіт. Якщо пакет робіт переміщується в інший проєкт, генерується новий ідентифікатор, однак старий продовжує працювати. ' setting_work_package_list_default_highlighting_mode: Режим виділення за умовчанням diff --git a/config/locales/crowdin/uz.yml b/config/locales/crowdin/uz.yml index a89d5fcb699..f841cfed500 100644 --- a/config/locales/crowdin/uz.yml +++ b/config/locales/crowdin/uz.yml @@ -114,7 +114,7 @@ uz: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ uz: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ uz: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ uz: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -387,9 +388,9 @@ uz: notification_text_default: "

Hello,

A new project has been created: projectValue:name

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -398,10 +399,14 @@ uz: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: one: "... 1 more project" other: "... %{count} more projects" @@ -416,7 +421,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 @@ -682,6 +687,37 @@ uz: danger_dialog: confirmation_live_message_checked: The button to proceed is now active. confirmation_live_message_unchecked: The button to proceed is now inactive. You need to tick the checkbox to continue. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1341,6 +1377,20 @@ uz: index: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1536,6 +1586,9 @@ uz: dependencies: Dependencies activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1544,7 +1597,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: @@ -1800,6 +1853,7 @@ uz: identity_url: Identity URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1917,6 +1971,7 @@ uz: confirmation: doesn't match %{attribute}. could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: does not exist. + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: may not be accessed. error_readonly: was attempted to be written but is not writable. @@ -1983,6 +2038,7 @@ uz: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2465,7 +2521,6 @@ uz: avatar: Avatar base: 'General Error:' body: Body - blocks_ids: IDs of blocked work packages category: Category comment: Comment comments: Comment @@ -2922,6 +2977,7 @@ uz: title: Enterprise add-on plan_title: Enterprise %{plan} add-on plan_name: "%{plan} enterprise plan" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Available starting with the %{plan_name}. unlimited: Unlimited already_have_token: 'Already have a token? Add it using the button below to upgrade to the booked Enterprise plan. @@ -3391,6 +3447,11 @@ uz: quick_add: label: Add… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Provider tokens are issued by OpenProject, allowing other applications to access it. Client tokens are issued by other applications, allowing OpenProject to access them. no_results: @@ -3453,6 +3514,72 @@ uz: disabled_text: RSS tokens are not enabled by the administrator. Please contact your administrator to use this feature. storages: unknown_storage: Unknown storage + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Assignee @@ -3484,6 +3611,7 @@ uz: invalid_filter: Invalid notification filter label_accessibility: Accessibility label_account: Account + label_actions: Actions label_active: Active label_activate_user: Activate user label_active_in_new_projects: Active in new projects @@ -3524,6 +3652,7 @@ uz: label_ical_access_key_generation_hint: Automatically generated when subscribing to a calendar. label_ical_access_key_latest: latest label_ical_access_key_revoke: Revoke + label_integrations: Integrations label_add_column: Add column label_applied_status: Applied status label_archive_project: Archive project @@ -3774,7 +3903,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 @@ -3812,6 +3941,10 @@ uz: label_custom_pdf_export_settings: Custom PDF export settings label_custom_favicon: Custom favicon label_custom_touch_icon: Custom touch icon + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Sign out label_mapping_for: 'Mapping for: %{attribute}' label_main_menu: Side Menu @@ -4884,6 +5017,7 @@ uz: ' setting_app_subtitle: Application subtitle setting_app_title: Application title + setting_organization_name: Organization name setting_attachment_max_size: Attachment max. size setting_show_work_package_attachments: Show attachments in the files tab by default setting_antivirus_scan_mode: Scan mode @@ -5030,12 +5164,12 @@ uz: setting_welcome_text: Welcome block text setting_welcome_title: Welcome block title setting_welcome_on_homescreen: Display welcome block on homescreen - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Default highlighting mode diff --git a/config/locales/crowdin/vi.yml b/config/locales/crowdin/vi.yml index 253835d638b..8c27085ad87 100644 --- a/config/locales/crowdin/vi.yml +++ b/config/locales/crowdin/vi.yml @@ -114,7 +114,7 @@ vi: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ vi: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ vi: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ vi: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +382,9 @@ vi: notification_text_default: "

Xin chào,

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

Thank you

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +393,14 @@ vi: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +414,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 @@ -676,6 +681,37 @@ vi: danger_dialog: confirmation_live_message_checked: Nút để tiếp tục hiện đang hoạt động. confirmation_live_message_unchecked: Nút để tiếp tục hiện không hoạt động. Bạn cần đánh dấu vào hộp kiểm để tiếp tục. + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1326,6 +1362,20 @@ vi: index: no_results_title_text: Hiện tại không có quy trình làm việc nào. work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1520,6 +1570,9 @@ vi: dependencies: phụ thuộc activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1528,7 +1581,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: @@ -1784,6 +1837,7 @@ vi: identity_url: URL nhận dạng parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1901,6 +1955,7 @@ vi: confirmation: không khớp %{attribute}. could_not_be_copied: "%{dependency} không thể sao chép (đầy đủ)." does_not_exist: không tồn tại + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} chỉ có trong phiên bản OpenProject Enterprise." error_unauthorized: Có thể không được truy cập. error_readonly: đã được cố gắng viết nhưng không thể ghi được. @@ -1967,6 +2022,7 @@ vi: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2429,9 +2485,8 @@ vi: avatar: hình đại diện base: 'Lỗi tổng quan:' 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 @@ -2866,6 +2921,7 @@ vi: title: Tiện ích bổ sung dành cho doanh nghiệp plan_title: Tiện ích bổ sung %{plan} dành cho doanh nghiệp plan_name: "%{plan} kế hoạch doanh nghiệp" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: Có sẵn bắt đầu với %{plan_name}. unlimited: Không giới hạn already_have_token: 'Bạn đã có mã thông báo chưa? Thêm nó bằng nút bên dưới để nâng cấp lên gói Enterprise đã đặt. @@ -3334,6 +3390,11 @@ vi: quick_add: label: Thêm vào… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: Mã thông báo của nhà cung cấp được OpenProject phát hành, cho phép các ứng dụng khác truy cập vào nó. Mã thông báo của khách hàng được phát hành bởi các ứng dụng khác, cho phép OpenProject truy cập chúng. no_results: @@ -3396,6 +3457,72 @@ vi: disabled_text: Mã thông báo RSS không được quản trị viên kích hoạt. Vui lòng liên hệ với quản trị viên của bạn để sử dụng tính năng này. storages: unknown_storage: Bộ nhớ không xác định + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: Người được chuyển nhượng @@ -3427,6 +3554,7 @@ vi: invalid_filter: Bộ lọc thông báo không hợp lệ label_accessibility: Trợ năng label_account: tài khoản + label_actions: hành động label_active: Đang hoạt động label_activate_user: Người dùng kích họat label_active_in_new_projects: Tích cực trong các dự án mới @@ -3467,6 +3595,7 @@ vi: label_ical_access_key_generation_hint: Tự động được tạo khi đăng ký lịch. label_ical_access_key_latest: mới nhất label_ical_access_key_revoke: Thu hồi + label_integrations: Integrations label_add_column: Thêm cột label_applied_status: Tình trạng áp dụng label_archive_project: Lưu trữ dự án @@ -3717,7 +3846,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 @@ -3755,6 +3884,10 @@ vi: label_custom_pdf_export_settings: Cài đặt xuất PDF tùy chỉnh label_custom_favicon: Favicon tuỳ chỉnh label_custom_touch_icon: Tùy chỉnh biểu tượng ICON + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: Đăng xuất label_mapping_for: 'Ánh xạ cho: %{attribute}' label_main_menu: Menu bên @@ -4039,7 +4172,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" @@ -4830,6 +4963,7 @@ vi: ' setting_app_subtitle: phụ đề ứng dụng setting_app_title: Tiêu đề ứng dụng + setting_organization_name: Organization name setting_attachment_max_size: Tệp đính kèm tối đa. kích cỡ setting_show_work_package_attachments: Hiển thị tệp đính kèm trong tab tệp theo mặc định setting_antivirus_scan_mode: Chế độ quét @@ -4976,12 +5110,12 @@ vi: setting_welcome_text: Văn bản chặn chào mừng setting_welcome_title: Tiêu đề khối chào mừng setting_welcome_on_homescreen: Hiển thị khối chào mừng trên màn hình chính - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: Chế độ đánh dấu mặc định diff --git a/config/locales/crowdin/zh-CN.seeders.yml b/config/locales/crowdin/zh-CN.seeders.yml index 30e63f026bc..212a0e3692c 100644 --- a/config/locales/crowdin/zh-CN.seeders.yml +++ b/config/locales/crowdin/zh-CN.seeders.yml @@ -97,7 +97,7 @@ zh-CN: demo-project: name: 演示项目 status_explanation: 所有任务都按计划进行。相关人员均知晓各自任务。系统已完全建立。 - description: 这是对此演示 Scrum 项目目标的简短摘要。 + description: 这是对此演示项目目标的简短摘要。 news: item_0: title: 欢迎来到您的演示项目 @@ -201,7 +201,7 @@ zh-CN: scrum-project: name: Scrum 项目 status_explanation: 所有任务都按计划进行。相关人员均知晓各自任务。系统已完全建立。 - description: 这是对此演示 Scrum 项目目标的简短摘要。 + description: 这是对此演示Scrum项目目标的简短摘要。 news: item_0: title: 欢迎来到您的 Scrum 演示项目 diff --git a/config/locales/crowdin/zh-CN.yml b/config/locales/crowdin/zh-CN.yml index 3e314bd7d38..be2321e0d20 100644 --- a/config/locales/crowdin/zh-CN.yml +++ b/config/locales/crowdin/zh-CN.yml @@ -89,7 +89,7 @@ zh-CN: token_caption: 要详细了解如何激活企业版,请查阅我们的[文档](docs_url)。 add_token: 上传企业版支持令牌 replace_token: 替换您当前的支持令牌 - order: 订购本地部署版的 Enterprise edition + order: 订购本地部署的 Enterprise edition paste: 粘贴您企业版的支持令牌 required_for_feature: 此功能仅限具激活的企业版支持令牌的订阅者使用。 enterprise_link: 如需了解详细信息,请单击此处。 @@ -114,7 +114,7 @@ zh-CN: import: title: 导入 jira: - title: Jira 导入 + title: Jira Migrator description: 使用此工具从您的 Jira 实例中导入数据。您可以配置多个 Jira 主机,并选择每次导入运行要导入的内容。 errors: cannot_delete_with_imports: 无法删除具有现有导入的 Jira 主机 @@ -125,8 +125,8 @@ zh-CN: title: Jira 配置 new: 新配置 banner: - title: 受限导入 - description: 此导入工具目前处于测试阶段,只能导入基本数据:项目、问题(名称、标题、描述、附件)、用户(名称、电子邮件地址、项目成员资格)、状态和类型。不能导入工作流、自定义字段、问题关系或权限。我们目前仅支持 Jira Server/Data Center 版本 10.x 和 11.x。目前不支持云实例。 + title: 有限导入功能 + description: Jira Migrator 目前处于测试阶段,只能导入基本数据:项目、问题(名称、标题、描述、附件)、用户(名称、电子邮件地址、项目成员资格)、状态和类型。不能导入工作流、自定义字段、问题关系或权限。我们目前仅支持 Jira Server/Data Center 版本 10.x 和 11.x。目前不支持云实例。 form: fields: name: 名称 @@ -154,6 +154,7 @@ zh-CN: connection_timeout: 与 Jira 服务器的连接超时:%{message} parse_error: 无法解析 Jira API 响应:%{message} api_error: Jira API 返回错误状态 %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: 项目 last_change: 上次更改 @@ -162,7 +163,7 @@ zh-CN: run: title: 导入运行 history: 历史记录 - remove_error: 无法移除正在运行的 Jira 导入 + remove_error: 无法移除正在运行的 Jira 导入运行 import_blocked_error: 另一个 Jira 导入运行当前正在执行或等待审核。请先完成或撤消当前运行,然后再开始新的导入。 project_identifier_taken: 您尝试导入的项目所使用的标识符 %{taken_identifier} 已经用过。请在 Jira 中更新项目标识符,然后点击“重试”。 blank: @@ -381,19 +382,23 @@ zh-CN: notification_text_default: "

您好!

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

谢谢

\n" work_packages_identifier: page_header: - description: 可以选择经典的数字工作包 ID,或者将项目标识符作为工作包 ID 前缀的项目特定工作包 ID。 + description: 可以选择经典的数字工作包 ID,或者将项目标识符作为工作包 ID 前缀的语义化项目特定工作包 ID。 banner: - existing_identifiers_notice: "%{project_count} 个项目的现有标识符不符合基于项目的字母数字标识符要求。OpenProject 可以自动更新这些标识符,使其成为如下例中所示的有效格式。点击“自动修正并保存”按钮,以这种方式更新所有项目的标识符,并启用基于项目的字母数字标识符。\n" + existing_identifiers_notice: "%{project_count} 个项目的现有标识符不符合基于项目的语义标识符要求。OpenProject 可以自动更新这些标识符,使其成为如下例中所示的有效格式。点击“自动修正并保存”按钮,以这种方式更新所有项目的标识符,并启用基于项目的语义标识符。\n" box_header: label_project: 项目 label_previous_identifier: 上一个标识符 label_autofixed_suggestion: 未来标识符 label_example_work_package_id: 示例工作包 ID autofix_preview: - error_too_long: 必须少于 5 个字符 + error_too_long: 必须为 10 个字符或更少字符 + error_numerical: 不能只包含数字 + error_starts_with_number: 不能以数字开头 error_special_characters: 不允许使用特殊字符 + error_not_fully_uppercased: 必须为大写字母 error_in_use: 已用作另一个项目的有效标识名 error_reserved: 由另一个项目的标识名历史保留 + error_unknown: 需要手动检查 remaining_projects: other: "…其他 %{count} 个项目" button_autofix: 自动修正并保存 @@ -407,7 +412,7 @@ zh-CN: checkbox_label: 我了解此操作将永久更改所有工作包 ID success_banner: 已成功更新工作包标识符格式。 in_progress: - banner_message: 项目标识符目前正在更新为基于项目的字母数字标识符。此过程可能需要一些时间。 + banner_message: 项目标识符目前正在更新为基于项目的语义标识符。此过程可能需要一些时间。 workflows: tabs: default_transitions: 默认转换 @@ -671,6 +676,37 @@ zh-CN: danger_dialog: confirmation_live_message_checked: 继续按钮现已激活。 confirmation_live_message_unchecked: 继续按钮现已失效。您需要勾选复选框才能继续。 + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: 分页 prev: 上一页 @@ -1320,6 +1356,20 @@ zh-CN: index: no_results_title_text: 目前没有工作流。 work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1514,6 +1564,9 @@ zh-CN: dependencies: 依赖项 activerecord: attributes: + work_package_semantic_alias: + identifier: 标识符 + work_package: 工作包 jira_import: projects: 项目 import/jira: @@ -1522,7 +1575,7 @@ zh-CN: personal_access_token: 个人访问令牌 import/jira_open_project_reference: jira: Jira - jira_import: Jira 导入 + jira_import: Jira Migrator announcements: show_until: 显示截止日期 attachment: @@ -1600,7 +1653,7 @@ zh-CN: page: 页 row_count: 行数 column_count: 列数 - widgets: 微件 + widgets: 小部件 journal: notes: 备注 cause_type: Cause 类型 @@ -1778,6 +1831,7 @@ zh-CN: identity_url: 身份 URL parent: 父群组 organizational_unit: 组织单元 + group_users: Group users group_detail: parent: 父群组 organizational_unit: 组织单元 @@ -1895,6 +1949,7 @@ zh-CN: confirmation: 不匹配 %{attribute}。 could_not_be_copied: 无法(完全)复制 %{dependency}。 does_not_exist: 不存在。 + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action}仅在 OpenProject 企业版中可用。" error_unauthorized: 无法访问。 error_readonly: 曾尝试被写入,但不可写。 @@ -1961,6 +2016,7 @@ zh-CN: attributes: parent_id: circular_dependency: 将创建循环群组层次结构。 + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2423,7 +2479,6 @@ zh-CN: avatar: 头像 base: 一般错误: body: 正文 - blocks_ids: 被工作包阻止的IDs category: 类别 comment: 注释 comments: 评论 @@ -2860,6 +2915,7 @@ zh-CN: title: 企业附加组件 plan_title: 企业 %{plan} 附加组件 plan_name: "%{plan} 企业计划" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: 从 %{plan_name} 开始可用。 unlimited: 无限制 already_have_token: '已经有令牌?使用下方按钮添加以升级到预订的企业版方案。 @@ -3328,6 +3384,11 @@ zh-CN: quick_add: label: 添加… my_account: + notifications_and_email: + title: 通知和电子邮件 + tabs: + notifications: 通知设置 + email_reminders: 电子邮件提醒 access_tokens: description: 提供商令牌由 OpenProject 签发,允许其他应用程序访问。客户端令牌由其他应用程序签发,允许 OpenProject 对其进行访问。 no_results: @@ -3390,6 +3451,72 @@ zh-CN: disabled_text: 管理员未启用 RSS 令牌。请联系管理员以使用此功能。 storages: unknown_storage: 未知存储 + email_reminders: + immediate_reminders: + title: 向我发送电子邮件提醒 + mentioned: 当我被提及时通知我 + personal_reminder: 有个人提醒时通知我 + daily_reminders: + title: 向我发送针对未读通知的每日电子邮件提醒 + caption: 您仅会收到针对未读通知的提醒,且仅会在您指定的时间收到提醒。在您为帐户配置时区前,时间将解释为采用 UTC 格式。 + enabled: 启用每日电子邮件提醒 + add_time: 添加时间 + remove_time: 移除时间 + time_slot_label: 提醒时间 (UTC) + workdays: + title: 在如下日期接收电子邮件提醒 + submit_button: 更新提醒日期 + pause_reminders: + title: 暂停电子邮件通知 + enabled: 临时暂停每日电子邮件提醒 + date_range: 暂停时间段 + email_alerts: + title: 其他条目(非工作包)的电子邮件提醒 + news_added: 新闻已添加 + news_commented: 评论新闻条目 + document_added: 文档已添加 + forum_messages: 论坛消息已发布 + wiki_page_added: Wiki 页面已添加 + wiki_page_updated: Wiki 页面已更新 + membership_added: 成员资格已添加 + membership_updated: 成员资格已更新 + submit_button: 更新提醒 + notifications: + participating: + title: 参与 + submit_button: 更新偏好设置 + mentioned: 被提及 + watched: 关注中 + assignee: 受理人 + responsible: 负责人 + shared: 与我共享 + date_alerts: + title: 日期提醒 + submit_button: 更新日期提醒 + start_date: 开始日期 + due_date: 完成日期 + overdue: 逾期 + times: + same_day: 当天 + one_day_before: 提前 1 天 + three_days_before: 提前 3 天 + seven_days_before: 提前 7 天 + one_day_after: 推后 1 天 + three_days_after: 推后 3 天 + seven_days_after: 推后 7 天 + non_participating: + title: 不参与 + submit_button: 更新偏好设置 + work_package_created: 新工作包 + work_package_commented: 所有新评论 + work_package_processed: 所有状态更改 + work_package_prioritized: 所有优先级更改 + work_package_scheduled: 所有日期更改 + project_specific_settings: + title: 项目特定通知设置 + add_button: 添加项目特定通知 + dialog_title: 添加项目特定通知 + list_header: 具有特定通知的项目 notifications: reasons: assigned: 指定人 @@ -3421,6 +3548,7 @@ zh-CN: invalid_filter: 无效的通知过滤器 label_accessibility: 辅助功能 label_account: 帐户 + label_actions: 操作 label_active: 激活 label_activate_user: 激活用户 label_active_in_new_projects: 新项目动态 @@ -3461,6 +3589,7 @@ zh-CN: label_ical_access_key_generation_hint: 订阅日历时自动生成的。 label_ical_access_key_latest: 最近 label_ical_access_key_revoke: 撤消 + label_integrations: 集成 label_add_column: 添加列 label_applied_status: 应用的状态 label_archive_project: 归档项目 @@ -3711,7 +3840,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: 最近一次活动 @@ -3749,6 +3878,10 @@ zh-CN: label_custom_pdf_export_settings: 自定义 PDF 导出设置 label_custom_favicon: 自定义图标 label_custom_touch_icon: 自定义触摸图标 + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: 注销 label_mapping_for: 映射: %{attribute} label_main_menu: 侧边菜单 @@ -3951,7 +4084,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} 超时" @@ -4711,7 +4844,7 @@ zh-CN: managed: 在 OpenProject 中创建新的存储库 storage: not_available: 磁盘存储开销不可用于此存储库。 - update_timeout: 在 N 分钟内保留存储库最后所需的磁盘空间信息。由于计算存储库所需的磁盘空间可能增加系统开销,增加该值可以减少性能影响。 + update_timeout: 在 N 分钟内保留存储库最后所需磁盘空间的信息。由于计算存储库所需的磁盘空间可能增加系统开销,增加该值可以减少性能影响。 oauth_application_details_html: 关闭此窗口后,将无法再次访问客户端密钥值。请将这些值复制到 Nextcloud OpenProject 集成设置中: oauth_application_details_link_text: 转到设置页面 setup_documentation_details: 如果您在配置新文件存储方面需要帮助,请查看文档: @@ -4809,6 +4942,7 @@ zh-CN: ' setting_app_subtitle: 应用副标题 setting_app_title: 应用程序标题 + setting_organization_name: Organization name setting_attachment_max_size: 最大的附件大小 setting_show_work_package_attachments: 默认在文件选项卡中显示附件 setting_antivirus_scan_mode: 扫描模式 @@ -4939,7 +5073,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: 计算 完成% 层次结构总数 @@ -4955,12 +5089,12 @@ zh-CN: setting_welcome_text: 欢迎块文本 setting_welcome_title: 欢迎块标题 setting_welcome_on_homescreen: 在主屏幕上显示欢迎信息 - setting_work_packages_identifier_numeric: 实例范围的数字序列(默认) - setting_work_packages_identifier_numeric_caption: '每个工作包会获得一个从 1 开始的顺序号,每新增一个工作包,编号会加 1。这些编号在此实例中是唯一的,因此即使工作包在项目之间移动,编号也不会改变。 + setting_work_packages_identifier_classic: 实例范围的数字序列(默认) + setting_work_packages_identifier_classic_caption: '每个工作包会获得一个从 1 开始的顺序号,每新增一个工作包,编号会加 1。这些编号在此实例中是唯一的,因此即使工作包在项目之间移动,编号也不会改变。 ' - setting_work_packages_identifier_alphanumeric: 基于项目的字母数字标识符 - setting_work_packages_identifier_alphanumeric_caption: '每个项目都有一个唯一标识符,该标识符会附加到工作包 ID 前面。如果工作包移至另一个项目,则会生成新的标识符,但原标识符仍可继续使用。 + setting_work_packages_identifier_semantic: 基于项目的语义标识符 + setting_work_packages_identifier_semantic_caption: '每个项目都有一个唯一标识符,该标识符会附加到工作包 ID 前面。如果工作包移至另一个项目,则会生成新的标识符,但原标识符仍可继续使用。 ' setting_work_package_list_default_highlighting_mode: 默认突出显示模式 @@ -5425,7 +5559,7 @@ zh-CN: warning_user_limit_reached_admin_html: '添加额外的用户将超出当前限值。请[升级您的方案](upgrade_url),以确保外部用户能够访问此实例。 ' - warning_user_limit_reached_instructions: '您已达到用户限制(%{current}/%{max} 活跃用户)。请联系 sales@openproject.com 升级您的企业版计划以添加额外用户。 + warning_user_limit_reached_instructions: '您达到了用户限制(%{current}/%{max}活跃用户)。 请联系sales@openproject.com以升级您的Enterprise edition计划并添加其他用户。 ' warning_protocol_mismatch_html: '' diff --git a/config/locales/crowdin/zh-TW.yml b/config/locales/crowdin/zh-TW.yml index c46fb37257e..fd9cb8eca7d 100644 --- a/config/locales/crowdin/zh-TW.yml +++ b/config/locales/crowdin/zh-TW.yml @@ -114,7 +114,7 @@ zh-TW: import: title: Import jira: - title: Jira Import + title: Jira Migrator description: Use this tool to import data from your Jira instance. You can configure multiple Jira hosts and choose what to import in each import run. errors: cannot_delete_with_imports: Cannot delete Jira host with existing imports @@ -125,8 +125,8 @@ zh-TW: title: Jira configuration new: New configuration banner: - title: Limited import - description: 'This import tool is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' + title: Limited import capabilities + description: 'This Jira Migrator is currently in beta and can only import basic data: projects, issues (name, title, description, attachments), users (name, email, project membership), statuses, and types. It cannot import workflows, custom fields, issue relations, or permissions. We currently only support Jira Server/Data Center versions 10.x and 11.x. Cloud instances are not supported at this time.' form: fields: name: Name @@ -154,6 +154,7 @@ zh-TW: connection_timeout: 'Connection to Jira server timed out: %{message}' parse_error: 'Failed to parse Jira API response: %{message}' api_error: Jira API returned error status %{status} + 401_error: Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator. columns: projects: Projects last_change: Last change @@ -162,7 +163,7 @@ zh-TW: run: title: Import run history: History - remove_error: A Jira import cannot be removed while it is running + remove_error: A Jira import run cannot be removed while it is running import_blocked_error: Another Jira import run is currently in progress or awaiting review. Please complete or revert it before starting a new import. project_identifier_taken: 'You are trying to import a project with an already used identifier: %{taken_identifier}. Please update the project identifier in Jira then click on Retry.' blank: @@ -381,9 +382,9 @@ zh-TW: notification_text_default: "

您好,

已建立新專案: projectValue:name

謝謝

\n" work_packages_identifier: page_header: - description: Choose between basic numerical work packages IDs or project-specific ones that prepend the project identifier to the work package ID. + description: Choose between classic numerical work package IDs or semantic project-specific ones that prepend the project identifier to the work package ID. banner: - existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based alphanumerical identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based alphanumerical identifiers. + existing_identifiers_notice: 'Existing identifiers for %{project_count} projects don''t meet requirements for project-based semantic identifiers. OpenProject can automatically update these so that they are valid as in the examples below. Click on ''Autofix and save'' to update identifiers for all projects in this manner and enable project-based semantic identifiers. ' box_header: @@ -392,10 +393,14 @@ zh-TW: label_autofixed_suggestion: Future identifier label_example_work_package_id: Example work package ID autofix_preview: - error_too_long: Has to be fewer than 5 characters + error_too_long: Has to be 10 characters or fewer + error_numerical: Cannot be purely numerical + error_starts_with_number: Cannot start with a number error_special_characters: Special characters not allowed + error_not_fully_uppercased: Must be uppercase error_in_use: Already in use as another project's active handle error_reserved: Reserved by another project's handle history + error_unknown: Needs manual review remaining_projects: other: "... %{count} more projects" button_autofix: Autofix and save @@ -409,7 +414,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: 預設轉換 @@ -674,6 +679,37 @@ zh-TW: danger_dialog: confirmation_live_message_checked: 繼續的按鈕現已啟用。 confirmation_live_message_unchecked: 繼續的按鈕現在沒有作用。您需要勾選核取方塊才能繼續。 + departments: + edit: Edit department + add_user: Add user + add_department: Add department + blankslate: + heading: Your organization has no departments + description: 'Start by adding departments or users to the organization. Each department can be used to create a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + + ' + add_button: Add + detail_blankslate: + heading: This department doesn’t have any hierarchy level below + description: Add departments or users to create sub-items inside another one. + add_button: Add + add_department_form: + name_label: Department name + name_placeholder: Enter department name + move_user_dialog: + title: User already in a department + heading: Move user to this department? + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: Move user + context_menu: + add_sub_department: Add sub-department + add_user: Add user + flash: + user_added: User was successfully added to the department. + user_removed: User was successfully removed from the department. + department_created: Department was successfully created. + errors: + move_user_failed: Failed to move user between departments. pagination: label: Pagination prev: Previous @@ -1322,6 +1358,20 @@ zh-TW: index: no_results_title_text: 目前沒有工作流程 work_packages: + delete_dialog: + title: Delete work package + heading: Permanently delete this work package? + description: Are you sure you want to delete the work package "%{name}"? + confirm_descendants_deletion: I acknowledge that ALL descendants of this work package will be recursively removed. + cross_project_warning: 'Work packages from the following projects will be deleted: %{projects}' + bulk_delete_dialog: + title: Delete %{count} work packages + heading: Permanently delete these %{count} work packages? + description: 'The following work packages, including children and all associated data, will permanently be deleted:' + description_with_children: 'The following work packages, including child work packages, and all associated data will be permanently deleted:' + confirm_children_deletion: I acknowledge that all selected work packages and their children will be permanently deleted. + cross_project_warning: 'These work packages span multiple projects: %{projects}' + children_label: 'The following children will also be deleted:' datepicker_modal: banner: description: @@ -1514,6 +1564,9 @@ zh-TW: dependencies: 依賴套件 activerecord: attributes: + work_package_semantic_alias: + identifier: Identifier + work_package: Work package jira_import: projects: Projects import/jira: @@ -1522,7 +1575,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: @@ -1778,6 +1831,7 @@ zh-TW: identity_url: 身份識別 URL parent: Parent group organizational_unit: Organizational unit + group_users: Group users group_detail: parent: Parent group organizational_unit: Organizational unit @@ -1895,6 +1949,7 @@ zh-TW: confirmation: 不吻合 %{attribute}。 could_not_be_copied: "%{dependency} 無法完整複製." does_not_exist: 不存在 + user_already_in_department: User %{user_id} is already a member of department %{department_id}. error_enterprise_only: "%{action} 只適用於 OpenProject 企業版。" error_unauthorized: 無法被存取。 error_readonly: 被嘗試寫入但是無法寫入。 @@ -1961,6 +2016,7 @@ zh-TW: attributes: parent_id: circular_dependency: would create a circular group hierarchy. + organizational_unit_mismatch: must have the same organizational unit setting as the group. ldap_auth_source: attributes: tls_certificate_string: @@ -2423,7 +2479,6 @@ zh-TW: avatar: 大頭貼 base: '一般錯誤:' body: 本體 - blocks_ids: 已被限制的工作套件 IDs category: 類別 comment: 留言 comments: 留言 @@ -2860,6 +2915,7 @@ zh-TW: title: 企業版附加元件 plan_title: 企業版 %{plan} 外掛 plan_name: "%{plan} 企業版計畫" + trial_text: This feature is included in your active Enterprise trial. plan_text_html: 需 %{plan_name} 方案以上才可使用。 unlimited: 不限 already_have_token: '已經有令牌了嗎?請使用下方按鈕新增,以升級至預約的 Enterprise 方案。 @@ -3326,6 +3382,11 @@ zh-TW: quick_add: label: 新增… my_account: + notifications_and_email: + title: Notification and email + tabs: + notifications: Notification settings + email_reminders: Email reminders access_tokens: description: 提供者令牌由 OpenProject 發行,允許其他應用程式存取。用戶端識別碼由其他應用程式發行,允許 OpenProject 存取它們。 no_results: @@ -3388,6 +3449,72 @@ zh-TW: disabled_text: RSS 令牌並非由管理員啟用。請聯絡您的管理員以使用此功能。 storages: unknown_storage: 不明的儲存區 + email_reminders: + immediate_reminders: + title: Send me an email reminder + mentioned: Notify me when I am mentioned + personal_reminder: Notify me for personal reminders + daily_reminders: + title: Send me daily email reminders for unread notifications + caption: You will receive these reminders only for unread notifications and only at hours you specify. Until you configure a time zone for your account, the times will be interpreted to be in UTC. + enabled: Enable daily email reminders + add_time: Add time + remove_time: Remove time + time_slot_label: Reminder time (UTC) + workdays: + title: Receive email reminders on these days + submit_button: Update reminder days + pause_reminders: + title: Pause email notifications + enabled: Temporarily pause daily email reminders + date_range: Pause period + email_alerts: + title: Email alerts for other items that are not work packages + news_added: News added + news_commented: Comment on a news item + document_added: Document added + forum_messages: Forum message posted + wiki_page_added: Wiki page added + wiki_page_updated: Wiki page updated + membership_added: Membership added + membership_updated: Membership updated + submit_button: Update alerts + notifications: + participating: + title: Participating + submit_button: Update preferences + mentioned: Mentioned + watched: Watching + assignee: Assignee + responsible: Accountable + shared: Shared with me + date_alerts: + title: Date alerts + submit_button: Update date alerts + start_date: Start date + due_date: Finish date + overdue: Overdue + times: + same_day: On the same day + one_day_before: 1 day before + three_days_before: 3 days before + seven_days_before: 7 days before + one_day_after: 1 day after + three_days_after: 3 days after + seven_days_after: 7 days after + non_participating: + title: Non-participating + submit_button: Update preferences + work_package_created: New work packages + work_package_commented: All new comments + work_package_processed: All status changes + work_package_prioritized: All priority changes + work_package_scheduled: All date changes + project_specific_settings: + title: Project-specific notification settings + add_button: Add project-specific notifications + dialog_title: Add project-specific notifications + list_header: Projects with specific notifications notifications: reasons: assigned: 執行者 @@ -3419,6 +3546,7 @@ zh-TW: invalid_filter: 無效的通知過濾器 label_accessibility: 輔助功能 label_account: 帳號 + label_actions: 操作 label_active: 啟用 label_activate_user: 啟動使用者 label_active_in_new_projects: 在新專案中啟用 @@ -3459,6 +3587,7 @@ zh-TW: label_ical_access_key_generation_hint: 訂閱日曆時自動生成的。 label_ical_access_key_latest: 最新 label_ical_access_key_revoke: 撤銷 + label_integrations: Integrations label_add_column: 新增欄位 label_applied_status: 套用的狀態 label_archive_project: 封存專案 @@ -3644,7 +3773,7 @@ zh-TW: label_filter_add: 新增條件 label_filter_by: 篩選條件: label_filter_any_name_attribute: 名稱屬性 - label_filter_plural: 篩選條件 + label_filter_plural: 篩選器 label_filters_toggle: 顯示/隱藏篩選條件 label_float: 浮點數 label_folder: 資料夾 @@ -3658,8 +3787,8 @@ zh-TW: label_global_modules: 全域模組 label_global_roles: 全域角色 label_git_path: ".git 目錄的路徑" - label_greater_or_equal: 之前 - label_group_by: 分類 + label_greater_or_equal: ">=" + label_group_by: 分組依據 label_group_new: 新增群組 label_group: 群組 label_group_named: 群組名稱 %{name} @@ -3671,7 +3800,7 @@ zh-TW: label_hierarchy: 階層 label_hierarchy_leaf: 頁面結構頁 label_home: Home - label_subject_or_id: 名稱或 id + label_subject_or_id: 主旨或 id label_calendar_subscriptions: 訂閱行事曆 label_identifier: 識別碼 label_project_identifier: Project identifier @@ -3709,7 +3838,7 @@ zh-TW: label_external_links: 外部連結 label_locale: 語言和地區 label_jump_to_a_project: 前往一個專案... - label_jira_import: Jira Import + label_jira_import: Jira Migrator label_keyword_plural: 關鍵字 label_language_based: 根據使用者的語言 label_last_activity: 最後的活動 @@ -3723,7 +3852,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: 清單 @@ -3747,6 +3876,10 @@ zh-TW: label_custom_pdf_export_settings: 匯出PDF設定 label_custom_favicon: 自訂圖示 label_custom_touch_icon: 自訂觸控圖示 + label_departments: Organization + label_departments_description_html: 'Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). + + ' label_logout: 登出 label_mapping_for: 對應為: %{attribute} label_main_menu: 側邊選單 @@ -4815,6 +4948,7 @@ zh-TW: ' setting_app_subtitle: 應用程式副標題 setting_app_title: 應用程式標題 + setting_organization_name: Organization name setting_attachment_max_size: 附件最大容量 setting_show_work_package_attachments: 默認在文件選項卡中顯示附件 setting_antivirus_scan_mode: 掃描模式 @@ -4961,12 +5095,12 @@ zh-TW: setting_welcome_text: 歡迎區塊文字 setting_welcome_title: 歡迎區塊標題 setting_welcome_on_homescreen: 在主頁面上顯示歡迎區塊 - setting_work_packages_identifier_numeric: Instance-wide numerical sequence (default) - setting_work_packages_identifier_numeric_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. + setting_work_packages_identifier_classic: Instance-wide numerical sequence (default) + setting_work_packages_identifier_classic_caption: 'Every work package gets a sequential number starting with 1 and incremented with every new one. The numbers are unique within this instance so they remain the same even if work packages are moved between projects. ' - setting_work_packages_identifier_alphanumeric: Project-based alphanumerical identifiers - setting_work_packages_identifier_alphanumeric_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. + setting_work_packages_identifier_semantic: Project-based semantic identifiers + setting_work_packages_identifier_semantic_caption: 'Every project has a unique identifier that is prefixed to the work package ID. If a work package moved to another project, a new identifier is generated but the old one continues to function. ' setting_work_package_list_default_highlighting_mode: 預設顯示模式 diff --git a/config/locales/en.yml b/config/locales/en.yml index 8987cd93c14..da07c8eae12 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" @@ -161,6 +161,7 @@ en: connection_timeout: "Connection to Jira server timed out: %{message}" parse_error: "Failed to parse Jira API response: %{message}" api_error: "Jira API returned error status %{status}" + 401_error: "Jira API returned a 401 error. Your authentication token may have expired or lack the required permissions. Please ensure the token belongs to a Jira administrator." columns: projects: "Projects" last_change: "Last change" @@ -169,8 +170,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 +400,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 +435,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" @@ -717,6 +722,38 @@ en: confirmation_live_message_checked: "The button to proceed is now active." confirmation_live_message_unchecked: "The button to proceed is now inactive. You need to tick the checkbox to continue." + departments: + edit: "Edit department" + add_user: "Add user" + add_department: "Add department" + blankslate: + heading: "Your organization has no departments" + description: > + Start by adding departments or users to the organization. Each department can be used to create + a hierarchy below it, to navigate and create sub-department inside a hierarchy click on the created item. + add_button: "Add" + detail_blankslate: + heading: "This department doesn’t have any hierarchy level below" + description: "Add departments or users to create sub-items inside another one." + add_button: "Add" + add_department_form: + name_label: "Department name" + name_placeholder: "Enter department name" + move_user_dialog: + title: "User already in a department" + heading: "Move user to this department?" + description: "%{user} is currently a member of %{from_department}. Moving them will remove them from that department." + confirm: "Move user" + context_menu: + add_sub_department: "Add sub-department" + add_user: "Add user" + flash: + user_added: "User was successfully added to the department." + user_removed: "User was successfully removed from the department." + department_created: "Department was successfully created." + errors: + move_user_failed: "Failed to move user between departments." + pagination: label: "Pagination" prev: "Previous" @@ -1412,6 +1449,20 @@ en: no_results_title_text: There are currently no workflows. work_packages: + delete_dialog: + title: "Delete work package" + heading: "Permanently delete this work package?" + description: 'Are you sure you want to delete the work package "%{name}"?' + confirm_descendants_deletion: "I acknowledge that ALL descendants of this work package will be recursively removed." + cross_project_warning: "Work packages from the following projects will be deleted: %{projects}" + bulk_delete_dialog: + title: "Delete %{count} work packages" + heading: "Permanently delete these %{count} work packages?" + description: "The following work packages, including children and all associated data, will permanently be deleted:" + description_with_children: "The following work packages, including child work packages, and all associated data will be permanently deleted:" + confirm_children_deletion: "I acknowledge that all selected work packages and their children will be permanently deleted." + cross_project_warning: "These work packages span multiple projects: %{projects}" + children_label: "The following children will also be deleted:" datepicker_modal: banner: description: @@ -1619,6 +1670,9 @@ en: activerecord: attributes: + work_package_semantic_alias: + identifier: "Identifier" + work_package: "Work package" jira_import: projects: "Projects" "import/jira": @@ -1627,7 +1681,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: @@ -1883,6 +1937,7 @@ en: identity_url: "Identity URL" parent: "Parent group" organizational_unit: "Organizational unit" + group_users: "Group users" group_detail: parent: "Parent group" organizational_unit: "Organizational unit" @@ -2001,6 +2056,7 @@ en: confirmation: "doesn't match %{attribute}." could_not_be_copied: "%{dependency} could not be (fully) copied." does_not_exist: "does not exist." + user_already_in_department: "User %{user_id} is already a member of department %{department_id}." error_enterprise_only: "%{action} is only available in the OpenProject Enterprise edition." error_unauthorized: "may not be accessed." error_readonly: "was attempted to be written but is not writable." @@ -2066,6 +2122,7 @@ en: attributes: parent_id: circular_dependency: "would create a circular group hierarchy." + organizational_unit_mismatch: "must have the same organizational unit setting as the group." ldap_auth_source: attributes: tls_certificate_string: @@ -2554,7 +2611,6 @@ en: avatar: "Avatar" base: "General Error:" body: "Body" - blocks_ids: "IDs of blocked work packages" category: "Category" comment: "Comment" comments: "Comment" @@ -3033,6 +3089,7 @@ en: title: "Enterprise add-on" plan_title: "Enterprise %{plan} add-on" plan_name: "%{plan} enterprise plan" + trial_text: "This feature is included in your active Enterprise trial." plan_text_html: "Available starting with the %{plan_name}." unlimited: "Unlimited" already_have_token: > @@ -3526,6 +3583,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 +3650,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 +3749,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 +3790,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 +4041,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" @@ -3949,6 +4079,11 @@ en: label_custom_pdf_export_settings: "Custom PDF export settings" label_custom_favicon: "Custom favicon" label_custom_touch_icon: "Custom touch icon" + label_departments: "Organization" + label_departments_description_html: > + Define your company’s structure by creating departments and sub-departments in a hierarchical way. This allows you + to reflect reporting lines and maintain a clear, structured overview of your organization within OpenProject. You + can also import an existing organization structure through [LDAP group synchronisation](ldap_docs_article). label_logout: "Sign out" label_mapping_for: "Mapping for: %{attribute}" label_main_menu: "Side Menu" @@ -5042,6 +5177,7 @@ en: APIs of OpenProject, such as APIv3 and MCP. setting_app_subtitle: "Application subtitle" setting_app_title: "Application title" + setting_organization_name: "Organization name" setting_attachment_max_size: "Attachment max. size" setting_show_work_package_attachments: "Show attachments in the files tab by default" setting_antivirus_scan_mode: "Scan mode" @@ -5201,11 +5337,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..50545ecb125 100644 --- a/config/locales/js-en.yml +++ b/config/locales/js-en.yml @@ -551,12 +551,6 @@ en: sidebar_arrow: "Use the return arrow in the top left corner to return to the project’s main menu." welcome: "Take a three-minute introduction tour to learn the most important features.
We recommend completing the steps until the end. You can restart the tour any time." wiki: "Within the wiki you can document and share knowledge together with your team." - backlogs: - overview: "Manage your work in the backlogs view." - sprints: "On the right you have the product backlog and the bug backlog, on the left you have the respective sprints. Here you can create epics, user stories, and bugs, prioritize via drag & drop and add them to a sprint." - task_board_arrow: "To see your task board, open the sprint drop-down..." - task_board_select: "...and select the task board entry." - task_board: "The task board visualizes the progress for this sprint. Click on the plus (+) icon next to a user story to add new tasks or impediments.
The status can be updated by drag and drop." boards: overview: "Select boards to shift the view and manage your project using the agile boards view." lists_kanban: "Here you can create multiple lists (columns) within your board. This feature allows you to create a Kanban board, for example." @@ -631,49 +625,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 +652,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" @@ -1123,13 +1040,6 @@ en: form_submit: title: "Confirm to continue" text: "Are you sure you want to perform this action?" - destroy_work_package: - title: "Confirm deletion of %{label}" - single_text: "Are you sure you want to delete the work package" - bulk_text: "Are you sure you want to delete the following %{label}?" - has_children: "The work package has %{childUnits}:" - confirm_deletion_children: "I acknowledge that ALL descendants of the listed work packages will be recursively removed." - deletes_children: "All child work packages and their descendants will also be recursively deleted." destroy_time_entry: title: "Confirm deletion of time entry" text: "Are you sure you want to delete the following time entry?" diff --git a/config/routes.rb b/config/routes.rb index ad657256f9a..cdb2dd4b32f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -801,6 +801,32 @@ Rails.application.routes.draw do post :delete_token end end + + resources :departments, + only: %i[index show edit update destroy], + constraints: lambda { |_request| OpenProject::FeatureDecisions.departments_active? } do + member do + get :new_user + post :add_user + delete "remove_user/:user_id" => "departments#remove_user", as: :remove_user + get :change_parent, action: :change_parent_dialog + post :change_parent + + # old routes for old group style management, might remove when new interface + patch "/memberships:membership_id" => "departments#edit_membership", as: "membership_of" + put "/memberships:membership_id" => "departments#edit_membership" + delete "/memberships:membership_id" => "departments#destroy_membership" + post "/memberships" => "departments#create_memberships", as: "memberships_of" + end + + collection do + get :new_department + post :add_department + get :edit_organization_name + patch :cancel_edit_organization_name + patch :update_organization_name + end + end end resources :workflows, only: %i[index edit update], param: :type_id do @@ -821,6 +847,7 @@ Rails.application.routes.draw do resource :bulk, controller: "bulk", only: %i[edit update destroy] do collection do match :reassign, via: %i[get delete] + get :delete_dialog end end end @@ -950,6 +977,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 +1077,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/20260402094525_add_group_detail_index_for_ous.rb b/db/migrate/20260402094525_add_group_detail_index_for_ous.rb new file mode 100644 index 00000000000..ed448582527 --- /dev/null +++ b/db/migrate/20260402094525_add_group_detail_index_for_ous.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddGroupDetailIndexForOus < ActiveRecord::Migration[8.1] + def change + add_index :group_details, :organizational_unit + 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/dev/xwiki/README.md b/docker/dev/xwiki/README.md index 4935b6d3109..7832c8e3e63 100644 --- a/docker/dev/xwiki/README.md +++ b/docker/dev/xwiki/README.md @@ -1,7 +1,6 @@ # Setup guide -A minimal setup guide for using a local XWiki inside a docker stack. The example compose file is connected to the -standard setup of the TLS-ready stack with `traefik`. +A minimal setup guide for using a local XWiki inside a docker stack. The example compose file is connected to the standard setup of the [TLS-ready](https://www.openproject.org/docs/development/development-environment/docker/#tls-support) stack with `traefik`. ## First steps @@ -9,4 +8,37 @@ standard setup of the TLS-ready stack with `traefik`. - Go to https://xwiki.local - Wait for initialisation to succeed - Create admin user -- Select XWiki standard flavor and install it +- Select XWiki standard flavor and install it — **this is highly recommended** as many XWiki + features and the OpenProject plugin depend on it + +## Recommended extensions + +For integration with OpenProject, install the following after the standard flavor is set up: + +- **[OpenProject Integration](https://store.xwiki.com/xwiki/bin/view/Extension/OpenProjectIntegration)** — connects XWiki with OpenProject + +Install it via the Extension Manager (Administration → Extensions → search for "OpenProject Integration"). + +## Updating XWiki + +To update XWiki to a newer version, pull the latest image and recreate the container: + +```bash +docker compose --project-directory docker/dev/xwiki/ pull +docker compose --project-directory docker/dev/xwiki/ up -d +``` + +After the container starts, go to — XWiki will detect the new version and +present an upgrade wizard. Follow it to completion before using XWiki again. + +## Certificates + +### Trusting the local CA in XWiki (for outbound HTTPS calls) + +XWiki runs on Java/Tomcat which has its own certificate truststore, independent of the system CA +bundle. If XWiki needs to make HTTPS requests to OpenProject (e.g. for OAuth), it must trust the +local step-ca root certificate. + +Copy `docker-compose.override.example.yml` to `docker-compose.override.yml` — it wraps the XWiki +entrypoint to automatically import the step-ca certificate into Java's truststore on every container +start, including after recreations. Requires the TLS stack (`docker/dev/tls`) to be running. diff --git a/docker/dev/xwiki/docker-compose.override.example.yml b/docker/dev/xwiki/docker-compose.override.example.yml new file mode 100644 index 00000000000..39b8877bbc4 --- /dev/null +++ b/docker/dev/xwiki/docker-compose.override.example.yml @@ -0,0 +1,22 @@ +services: + web: + volumes: + - step-certs:/step:ro + # Automatically imports the local step-ca root certificate into Java's truststore on every + # container start, so XWiki can make HTTPS calls to OpenProject without certificate errors. + # Requires the TLS stack (docker/dev/tls) to be running. + entrypoint: + - /bin/bash + - -c + - | + keytool -import -trustcacerts \ + -keystore /opt/java/openjdk/lib/security/cacerts \ + -storepass changeit -noprompt \ + -alias step-ca \ + -file /step/certs/root_ca.crt 2>/dev/null || true + exec docker-entrypoint.sh xwiki + +volumes: + step-certs: + external: true + name: tls_step # volume created by the TLS stack (docker/dev/tls) diff --git a/docker/prod/Dockerfile b/docker/prod/Dockerfile index 2a9177b642a..6f13a27e083 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/api/apiv3/components/examples/meeting_create_request.yml b/docs/api/apiv3/components/examples/meeting_create_request.yml new file mode 100644 index 00000000000..44572a181fc --- /dev/null +++ b/docs/api/apiv3/components/examples/meeting_create_request.yml @@ -0,0 +1,12 @@ +# Example: Meeting create request +--- +description: Request to create a basic meeting +value: + title: "Weekly Standup" + location: "Conference Room A" + startTime: "2026-06-01T10:00:00Z" + duration: "PT1H" + _links: + project: + href: "/api/v3/projects/10" + title: "Death Star" diff --git a/docs/api/apiv3/components/examples/meeting_update_request.yml b/docs/api/apiv3/components/examples/meeting_update_request.yml new file mode 100644 index 00000000000..219f1973f36 --- /dev/null +++ b/docs/api/apiv3/components/examples/meeting_update_request.yml @@ -0,0 +1,7 @@ +# Example: Meeting update request +--- +description: Example request body to update the title and location of a meeting. +value: + title: "Updated Standup" + location: "Room B" + lockVersion: 13 diff --git a/docs/api/apiv3/components/schemas/meeting_agenda_item_collection_model.yml b/docs/api/apiv3/components/schemas/meeting_agenda_item_collection_model.yml new file mode 100644 index 00000000000..d469ebae6e6 --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_agenda_item_collection_model.yml @@ -0,0 +1,39 @@ +# Schema: MeetingAgendaItemCollectionModel +--- +type: object +required: + - _type + - count + - total + - _embedded + - _links +properties: + _type: + type: string + enum: + - Collection + count: + type: integer + total: + type: integer + _embedded: + type: object + required: + - elements + properties: + elements: + type: array + items: + $ref: './meeting_agenda_item_model.yml' + _links: + type: object + required: + - self + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + The link to this agenda item collection resource + + **Resource**: MeetingAgendaItemCollection diff --git a/docs/api/apiv3/components/schemas/meeting_agenda_item_model.yml b/docs/api/apiv3/components/schemas/meeting_agenda_item_model.yml new file mode 100644 index 00000000000..3da594f9198 --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_agenda_item_model.yml @@ -0,0 +1,99 @@ +# Schema: MeetingAgendaItemModel +--- +type: object +required: + - _type + - id + - title + - itemType + - position + - createdAt + - updatedAt +properties: + _type: + type: string + enum: + - MeetingAgendaItem + id: + type: integer + description: Identifier of this agenda item + minimum: 1 + title: + type: string + description: The agenda item's title. Required for simple items. + notes: + $ref: "./formattable.yml" + position: + type: integer + description: The position of the agenda item within its section. + durationInMinutes: + type: + - integer + - "null" + description: The agenda item's duration in minutes. + itemType: + type: string + description: |- + The type of this agenda item. Possible values: + + - *simple*: a simple text agenda item + - *work_package*: an agenda item linked to a work package + enum: + - simple + - work_package + lockVersion: + type: integer + description: The version of the item as used for optimistic locking. + createdAt: + type: string + format: date-time + description: Time of creation. + updatedAt: + type: string + format: date-time + description: Time of the most recent change. + _links: + type: object + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + This agenda item + + **Resource**: MeetingAgendaItem + meeting: + allOf: + - $ref: './link.yml' + - description: |- + The meeting this agenda item belongs to + + **Resource**: Meeting + author: + allOf: + - $ref: './link.yml' + - description: |- + The user who created this agenda item + + **Resource**: User + presenter: + allOf: + - $ref: './link.yml' + - description: |- + The user presenting this agenda item + + **Resource**: User + workPackage: + allOf: + - $ref: './link.yml' + - description: |- + The linked work package (for work_package type items) + + **Resource**: WorkPackage + section: + allOf: + - $ref: './link.yml' + - description: |- + The section this agenda item belongs to + + **Resource**: MeetingSection diff --git a/docs/api/apiv3/components/schemas/meeting_agenda_item_write_model.yml b/docs/api/apiv3/components/schemas/meeting_agenda_item_write_model.yml new file mode 100644 index 00000000000..0bfa4b7d88d --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_agenda_item_write_model.yml @@ -0,0 +1,47 @@ +# Schema: MeetingAgendaItemWriteModel +--- +type: object +properties: + title: + type: string + description: The agenda item's title. + notes: + $ref: "./formattable.yml" + durationInMinutes: + type: + - integer + - "null" + description: The agenda item's duration in minutes. + itemType: + type: string + description: The type of this agenda item (simple or work_package). + enum: + - simple + - work_package + lockVersion: + type: integer + description: The version of the item as used for optimistic locking. Required for PATCH operations. + _links: + type: object + properties: + workPackage: + allOf: + - $ref: "./link.yml" + - description: |- + The linked work package (for work_package type items) + + **Resource**: WorkPackage + presenter: + allOf: + - $ref: "./link.yml" + - description: |- + The user presenting this agenda item + + **Resource**: User + section: + allOf: + - $ref: "./link.yml" + - description: |- + The section this agenda item belongs to + + **Resource**: MeetingSection diff --git a/docs/api/apiv3/components/schemas/meeting_model.yml b/docs/api/apiv3/components/schemas/meeting_model.yml index 0b518a1d20e..66f35a97841 100644 --- a/docs/api/apiv3/components/schemas/meeting_model.yml +++ b/docs/api/apiv3/components/schemas/meeting_model.yml @@ -36,10 +36,45 @@ properties: endTime: type: string format: date-time - description: The scheduled meeting start time. + description: The scheduled meeting end time. duration: - type: number + type: string + format: duration description: The meeting duration in hours. + state: + type: string + description: |- + The current state of the meeting. Possible values: + + - *open*: the meeting is open + - *draft*: the meeting is in draft state + - *in_progress*: the meeting is currently in progress + - *cancelled*: the meeting has been cancelled + - *closed*: the meeting is closed + enum: + - open + - draft + - in_progress + - cancelled + - closed + sharing: + type: string + description: |- + How the meeting template is shared. Only applicable for one-time templates. Possible values: + + - *none*: not shared + - *descendants*: shared with descendant projects + - *system*: shared globally + enum: + - none + - descendants + - system + template: + type: boolean + description: Whether this meeting is a template. + notify: + type: boolean + description: Whether to send email notifications to participants. createdAt: type: string format: date-time @@ -70,6 +105,40 @@ properties: This meeting **Resource**: Meeting + schema: + allOf: + - $ref: './link.yml' + - description: |- + The meeting schema + + **Resource**: Schema + update: + allOf: + - $ref: './link.yml' + - description: |- + Form endpoint to update this meeting + + # Conditions + + **Permission**: edit meetings + updateImmediately: + allOf: + - $ref: './link.yml' + - description: |- + Directly update this meeting + + # Conditions + + **Permission**: edit meetings + delete: + allOf: + - $ref: './link.yml' + - description: |- + Delete this meeting + + # Conditions + + **Permission**: delete meetings author: allOf: - $ref: './link.yml' @@ -88,7 +157,7 @@ properties: allOf: - $ref: './link.yml' - description: |- - The attachment collection of this grid. + The attachment collection of this meeting. **Resource**: AttachmentCollection addAttachment: @@ -99,4 +168,4 @@ properties: # Conditions - **Permission**: edit meeting + **Permission**: edit meetings diff --git a/docs/api/apiv3/components/schemas/meeting_section_collection_model.yml b/docs/api/apiv3/components/schemas/meeting_section_collection_model.yml new file mode 100644 index 00000000000..cb623c6f122 --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_section_collection_model.yml @@ -0,0 +1,39 @@ +# Schema: MeetingSectionCollectionModel +--- +type: object +required: + - _type + - count + - total + - _embedded + - _links +properties: + _type: + type: string + enum: + - Collection + count: + type: integer + total: + type: integer + _embedded: + type: object + required: + - elements + properties: + elements: + type: array + items: + $ref: './meeting_section_model.yml' + _links: + type: object + required: + - self + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + The link to this section collection resource + + **Resource**: MeetingSectionCollection diff --git a/docs/api/apiv3/components/schemas/meeting_section_model.yml b/docs/api/apiv3/components/schemas/meeting_section_model.yml new file mode 100644 index 00000000000..d749e3ee1ca --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_section_model.yml @@ -0,0 +1,50 @@ +# Schema: MeetingSectionModel +--- +type: object +required: + - _type + - id + - title + - position + - createdAt + - updatedAt +properties: + _type: + type: string + enum: + - MeetingSection + id: + type: integer + description: Identifier of this section + minimum: 1 + title: + type: string + description: The section's title. + position: + type: integer + description: The position of the section within the meeting. + createdAt: + type: string + format: date-time + description: Time of creation. + updatedAt: + type: string + format: date-time + description: Time of the most recent change. + _links: + type: object + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + This section + + **Resource**: MeetingSection + meeting: + allOf: + - $ref: './link.yml' + - description: |- + The meeting this section belongs to + + **Resource**: Meeting diff --git a/docs/api/apiv3/components/schemas/meeting_section_write_model.yml b/docs/api/apiv3/components/schemas/meeting_section_write_model.yml new file mode 100644 index 00000000000..814ab4e51c7 --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_section_write_model.yml @@ -0,0 +1,7 @@ +# Schema: MeetingSectionWriteModel +--- +type: object +properties: + title: + type: string + description: The section's title. diff --git a/docs/api/apiv3/components/schemas/meeting_write_model.yml b/docs/api/apiv3/components/schemas/meeting_write_model.yml new file mode 100644 index 00000000000..60c6f7acf4c --- /dev/null +++ b/docs/api/apiv3/components/schemas/meeting_write_model.yml @@ -0,0 +1,64 @@ +# Schema: MeetingWriteModel +--- +type: object +properties: + title: + type: string + description: The meeting's title + location: + type: string + description: The meeting's location + startTime: + type: string + format: date-time + description: The scheduled meeting start time. + duration: + type: string + description: |- + The meeting duration as an ISO 8601 duration (e.g. `PT1H` for 1 hour, `PT1H30M` for 1.5 hours). + state: + type: string + description: |- + The current state of the meeting. Possible values: + + - *open* + - *draft* + - *in_progress* + - *cancelled* + - *closed* + enum: + - open + - draft + - in_progress + - cancelled + - closed + sharing: + type: string + description: |- + How the meeting template is shared. Only applicable for one-time templates. + enum: + - none + - descendants + - system + template: + type: boolean + description: Whether this meeting is a template. + notify: + type: boolean + description: Whether to send email notifications to participants. + lockVersion: + type: integer + description: |- + The version of the item as used for optimistic locking. + + Required for PATCH operations to detect concurrent modifications. + _links: + type: object + properties: + project: + allOf: + - $ref: "./link.yml" + - description: |- + The project the meeting belongs to. Required on creation. + + **Resource**: Project diff --git a/docs/api/apiv3/components/schemas/recurring_meeting_collection_model.yml b/docs/api/apiv3/components/schemas/recurring_meeting_collection_model.yml new file mode 100644 index 00000000000..107ffdf21fb --- /dev/null +++ b/docs/api/apiv3/components/schemas/recurring_meeting_collection_model.yml @@ -0,0 +1,30 @@ +# Schema: RecurringMeetingCollectionModel +--- +allOf: + - $ref: './paginated_collection_model.yml' + - type: object + required: + - _embedded + - _links + properties: + _embedded: + type: object + required: + - elements + properties: + elements: + type: array + items: + $ref: './recurring_meeting_model.yml' + _links: + type: object + required: + - self + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + The link to this recurring meeting collection resource + + **Resource**: RecurringMeetingCollection diff --git a/docs/api/apiv3/components/schemas/recurring_meeting_model.yml b/docs/api/apiv3/components/schemas/recurring_meeting_model.yml new file mode 100644 index 00000000000..4bdfcbfc387 --- /dev/null +++ b/docs/api/apiv3/components/schemas/recurring_meeting_model.yml @@ -0,0 +1,171 @@ +# Schema: RecurringMeetingModel +--- +type: object +required: + - _type + - id + - title + - frequency + - interval + - endAfter + - startTime + - createdAt + - updatedAt +properties: + _type: + type: string + enum: + - RecurringMeeting + id: + type: integer + description: Identifier of this recurring meeting + minimum: 1 + title: + type: string + description: The recurring meeting's title + frequency: + type: string + description: |- + The recurrence frequency. Possible values: + + - *daily*: repeats every day (or every N days depending on interval) + - *working_days*: repeats on working days only + - *weekly*: repeats every week (or every N weeks depending on interval) + enum: + - daily + - working_days + - weekly + interval: + type: integer + description: |- + The interval between occurrences. For example, an interval of 2 with a weekly frequency + means the meeting occurs every 2 weeks. Not applicable for working_days frequency. + minimum: 1 + endAfter: + type: string + description: |- + How the recurrence ends. Possible values: + + - *specific_date*: ends on a specific date + - *iterations*: ends after a specific number of occurrences + - *never*: never ends + enum: + - specific_date + - iterations + - never + endDate: + type: + - 'string' + - 'null' + format: date + description: |- + The date on which the recurrence ends. Only present when endAfter is `specific_date`. + iterations: + type: + - 'integer' + - 'null' + description: |- + The number of occurrences after which the recurrence ends. + Only present when endAfter is `iterations`. + minimum: 1 + timeZone: + type: string + description: The time zone in which the recurring meeting is scheduled (e.g. "Europe/Berlin"). + startTime: + type: string + format: date-time + description: The scheduled start time of the recurring meeting. + location: + type: string + description: The meeting's location (inherited from the template meeting). + duration: + type: number + description: The meeting duration in hours (inherited from the template meeting). + notify: + type: boolean + description: Whether to send email notifications to participants (inherited from the template meeting). + createdAt: + type: string + format: date-time + description: Time of creation. + updatedAt: + type: string + format: date-time + description: Time of the most recent change to the recurring meeting. + _links: + type: object + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + This recurring meeting + + **Resource**: RecurringMeeting + updateImmediately: + allOf: + - $ref: './link.yml' + - description: |- + Directly update this recurring meeting + + # Conditions + + **Permission**: edit meetings + delete: + allOf: + - $ref: './link.yml' + - description: |- + Delete this recurring meeting + + # Conditions + + **Permission**: delete meetings + template: + allOf: + - $ref: './link.yml' + - description: |- + The template meeting associated with this recurring meeting + + **Resource**: Meeting + project: + allOf: + - $ref: './link.yml' + - description: |- + The project the recurring meeting belongs to + + **Resource**: Project + author: + allOf: + - $ref: './link.yml' + - description: |- + The user who created this recurring meeting + + **Resource**: User + occurrencesUpcoming: + allOf: + - $ref: './link.yml' + - description: |- + The upcoming occurrences of this recurring meeting + + **Resource**: MeetingOccurrenceCollection + occurrencesPast: + allOf: + - $ref: './link.yml' + - description: |- + The past occurrences of this recurring meeting + + **Resource**: MeetingOccurrenceCollection + occurrencesCancelled: + allOf: + - $ref: './link.yml' + - description: |- + The cancelled occurrences of this recurring meeting + + **Resource**: MeetingOccurrenceCollection + occurrencesOpen: + allOf: + - $ref: './link.yml' + - description: |- + The open (instantiated) occurrences of this recurring meeting + + **Resource**: MeetingOccurrenceCollection diff --git a/docs/api/apiv3/components/schemas/recurring_meeting_occurrence_collection_model.yml b/docs/api/apiv3/components/schemas/recurring_meeting_occurrence_collection_model.yml new file mode 100644 index 00000000000..ba00903bc96 --- /dev/null +++ b/docs/api/apiv3/components/schemas/recurring_meeting_occurrence_collection_model.yml @@ -0,0 +1,30 @@ +# Schema: RecurringMeetingOccurrenceCollectionModel +--- +allOf: + - $ref: './collection_model.yml' + - type: object + required: + - _links + - _embedded + properties: + _links: + type: object + required: + - self + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + This occurrence collection + + **Resource**: MeetingOccurrenceCollection + _embedded: + type: object + required: + - elements + properties: + elements: + type: array + items: + $ref: './recurring_meeting_occurrence_model.yml' diff --git a/docs/api/apiv3/components/schemas/recurring_meeting_occurrence_model.yml b/docs/api/apiv3/components/schemas/recurring_meeting_occurrence_model.yml new file mode 100644 index 00000000000..7139f25c980 --- /dev/null +++ b/docs/api/apiv3/components/schemas/recurring_meeting_occurrence_model.yml @@ -0,0 +1,52 @@ +# Schema: RecurringMeetingOccurrenceModel +--- +type: object +required: + - _type + - startTime + - state +properties: + _type: + type: string + enum: + - MeetingOccurrence + startTime: + type: string + format: date-time + description: The start time of this occurrence. + state: + type: string + description: |- + The current state of the occurrence. Possible values: + + - *open*: the occurrence has been instantiated as a meeting + - *cancelled*: the occurrence has been cancelled + - *scheduled*: the occurrence is scheduled but not yet instantiated + enum: + - open + - cancelled + - scheduled + _links: + type: object + properties: + self: + allOf: + - $ref: './link.yml' + - description: |- + This occurrence + + **Resource**: MeetingOccurrence + meeting: + allOf: + - $ref: './link.yml' + - description: |- + The instantiated meeting for this occurrence, if it exists. + + **Resource**: Meeting + recurringMeeting: + allOf: + - $ref: './link.yml' + - description: |- + The recurring meeting this occurrence belongs to + + **Resource**: RecurringMeeting diff --git a/docs/api/apiv3/components/schemas/recurring_meeting_write_model.yml b/docs/api/apiv3/components/schemas/recurring_meeting_write_model.yml new file mode 100644 index 00000000000..b6e9c44c335 --- /dev/null +++ b/docs/api/apiv3/components/schemas/recurring_meeting_write_model.yml @@ -0,0 +1,74 @@ +# Schema: RecurringMeetingWriteModel +--- +type: object +properties: + title: + type: string + description: The recurring meeting's title + frequency: + type: string + description: |- + The recurrence frequency. Possible values: + + - *daily* + - *working_days* + - *weekly* + enum: + - daily + - working_days + - weekly + interval: + type: integer + description: |- + The interval between occurrences. Not applicable for working_days frequency. + minimum: 1 + endAfter: + type: string + description: |- + How the recurrence ends. Possible values: + + - *specific_date* + - *iterations* + - *never* + enum: + - specific_date + - iterations + - never + endDate: + type: + - 'string' + - 'null' + format: date + description: |- + The date on which the recurrence ends. Required when endAfter is `specific_date`. + iterations: + type: + - 'integer' + - 'null' + description: |- + The number of occurrences after which the recurrence ends. + Required when endAfter is `iterations`. + minimum: 1 + startTime: + type: string + format: date-time + description: The scheduled start time of the recurring meeting. + location: + type: string + description: The meeting's location. + duration: + type: number + description: The meeting duration in hours. + notify: + type: boolean + description: Whether to send email notifications to participants. + _links: + type: object + properties: + project: + allOf: + - $ref: "./link.yml" + - description: |- + The project the recurring meeting belongs to. Required on creation. + + **Resource**: Project diff --git a/docs/api/apiv3/components/schemas/work_package_model.yml b/docs/api/apiv3/components/schemas/work_package_model.yml index 2343dcb7555..0caf6b15f71 100644 --- a/docs/api/apiv3/components/schemas/work_package_model.yml +++ b/docs/api/apiv3/components/schemas/work_package_model.yml @@ -12,6 +12,14 @@ allOf: description: Work package id readOnly: true minimum: 1 + displayId: + type: string + description: |- + The user-facing identifier for the work package. + Its format depends on the `work_packages_identifier` setting. + When set to `semantic`: the project-based identifier (e.g. "PROJ-42"). + When set to `classic`: the numeric ID as a string (e.g. "123"). + readOnly: true lockVersion: type: integer description: The version of the item as used for optimistic locking diff --git a/docs/api/apiv3/openapi-spec.yml b/docs/api/apiv3/openapi-spec.yml index 36fbb00300d..2a50ea44013 100644 --- a/docs/api/apiv3/openapi-spec.yml +++ b/docs/api/apiv3/openapi-spec.yml @@ -270,8 +270,22 @@ paths: "$ref": "./paths/meetings.yml" "/api/v3/meetings/{id}": "$ref": "./paths/meeting.yml" + "/api/v3/meetings/{id}/agenda_items": + "$ref": "./paths/meeting_agenda_items.yml" + "/api/v3/meetings/{meeting_id}/agenda_items/{id}": + "$ref": "./paths/meeting_agenda_item.yml" "/api/v3/meetings/{id}/attachments": "$ref": "./paths/meeting_attachments.yml" + "/api/v3/meetings/{id}/form": + "$ref": "./paths/meeting_form.yml" + "/api/v3/meetings/{id}/sections": + "$ref": "./paths/meeting_sections.yml" + "/api/v3/meetings/{meeting_id}/sections/{id}": + "$ref": "./paths/meeting_section.yml" + "/api/v3/meetings/form": + "$ref": "./paths/meetings_form.yml" + "/api/v3/meetings/schema": + "$ref": "./paths/meeting_schema.yml" "/api/v3/memberships": "$ref": "./paths/memberships.yml" "/api/v3/memberships/available_projects": @@ -420,6 +434,22 @@ paths: "$ref": "./paths/query_star.yml" "/api/v3/queries/{id}/unstar": "$ref": "./paths/query_unstar.yml" + "/api/v3/recurring_meetings": + "$ref": "./paths/recurring_meetings.yml" + "/api/v3/recurring_meetings/{id}": + "$ref": "./paths/recurring_meeting.yml" + "/api/v3/recurring_meetings/{id}/occurrences/upcoming": + "$ref": "./paths/recurring_meeting_occurrences_upcoming.yml" + "/api/v3/recurring_meetings/{id}/occurrences/past": + "$ref": "./paths/recurring_meeting_occurrences_past.yml" + "/api/v3/recurring_meetings/{id}/occurrences/cancelled": + "$ref": "./paths/recurring_meeting_occurrences_cancelled.yml" + "/api/v3/recurring_meetings/{id}/occurrences/open": + "$ref": "./paths/recurring_meeting_occurrences_open.yml" + "/api/v3/recurring_meetings/{id}/occurrences/{start_time}": + "$ref": "./paths/recurring_meeting_occurrence.yml" + "/api/v3/recurring_meetings/{id}/occurrences/{start_time}/init": + "$ref": "./paths/recurring_meeting_occurrence_init.yml" "/api/v3/relations": "$ref": "./paths/relations.yml" "/api/v3/relations/{id}": @@ -609,6 +639,10 @@ components: $ref: "./components/examples/meeting_collection_simple_response.yml" MeetingSimpleResponse: $ref: "./components/examples/meeting_simple_response.yml" + MeetingCreateRequest: + $ref: "./components/examples/meeting_create_request.yml" + MeetingUpdateRequest: + $ref: "./components/examples/meeting_update_request.yml" MembershipCreateRequestCustomMessage: $ref: "./components/examples/membership-create-request-custom-message.yml" MembershipCreateRequestGlobalRole: @@ -851,10 +885,24 @@ components: "$ref": "./components/schemas/list_of_news_model.yml" List_workspaces_by_versionModel: "$ref": "./components/schemas/list_workspaces_by_version_model.yml" + MeetingAgendaItemModel: + "$ref": "./components/schemas/meeting_agenda_item_model.yml" + MeetingAgendaItemWriteModel: + "$ref": "./components/schemas/meeting_agenda_item_write_model.yml" + MeetingAgendaItemCollectionModel: + "$ref": "./components/schemas/meeting_agenda_item_collection_model.yml" MeetingCollectionModel: "$ref": "./components/schemas/meeting_collection_model.yml" MeetingModel: "$ref": "./components/schemas/meeting_model.yml" + MeetingSectionModel: + "$ref": "./components/schemas/meeting_section_model.yml" + MeetingSectionWriteModel: + "$ref": "./components/schemas/meeting_section_write_model.yml" + MeetingSectionCollectionModel: + "$ref": "./components/schemas/meeting_section_collection_model.yml" + MeetingWriteModel: + "$ref": "./components/schemas/meeting_write_model.yml" MarkdownModel: "$ref": "./components/schemas/markdown_model.yml" MembershipCollectionModel: @@ -955,6 +1003,16 @@ components: "$ref": "./components/schemas/query_sort_by_model.yml" Query_Update_Form: "$ref": "./components/schemas/query_update_form.yml" + RecurringMeetingCollectionModel: + "$ref": "./components/schemas/recurring_meeting_collection_model.yml" + RecurringMeetingModel: + "$ref": "./components/schemas/recurring_meeting_model.yml" + RecurringMeetingWriteModel: + "$ref": "./components/schemas/recurring_meeting_write_model.yml" + RecurringMeetingOccurrenceCollectionModel: + "$ref": "./components/schemas/recurring_meeting_occurrence_collection_model.yml" + RecurringMeetingOccurrenceModel: + "$ref": "./components/schemas/recurring_meeting_occurrence_model.yml" RelationCollectionModel: "$ref": "./components/schemas/relation_collection_model.yml" RelationReadModel: diff --git a/docs/api/apiv3/paths/meeting.yml b/docs/api/apiv3/paths/meeting.yml index e0ec3ef46f9..5ba946d8f45 100644 --- a/docs/api/apiv3/paths/meeting.yml +++ b/docs/api/apiv3/paths/meeting.yml @@ -38,4 +38,131 @@ get: description: |- Returned if the meeting does not exist or the client does not have sufficient permissions to see it. - **Required permission:** view meetings in the page's project + **Required permission:** view meetings in the meeting's project + +patch: + summary: Update meeting + operationId: update_meeting + tags: + - Meetings + description: |- + Updates the given meeting by applying the attributes provided in the body. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/meeting_write_model.yml" + examples: + 'update meeting': + $ref: "../components/examples/meeting_update_request.yml" + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_model.yml" + examples: + response: + $ref: "../components/examples/meeting_simple_response.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** edit meetings in the meeting's project + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the meeting's project + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) + +delete: + summary: Delete meeting + operationId: delete_meeting + tags: + - Meetings + description: Deletes the meeting. Participants will be notified of the cancellation. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '204': + description: Returned if the meeting was successfully deleted + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** delete meetings + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the meeting's project + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" diff --git a/docs/api/apiv3/paths/meeting_agenda_item.yml b/docs/api/apiv3/paths/meeting_agenda_item.yml new file mode 100644 index 00000000000..4c11743260b --- /dev/null +++ b/docs/api/apiv3/paths/meeting_agenda_item.yml @@ -0,0 +1,169 @@ +# /api/v3/meetings/{meeting_id}/agenda_items/{id} +--- +get: + summary: Get a meeting agenda item + operationId: get_meeting_agenda_item + tags: + - Meetings + description: Retrieve an individual agenda item of a meeting. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: meeting_id + required: true + schema: + type: integer + - description: Agenda item identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_agenda_item_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the agenda item or meeting does not exist or the client does not have sufficient permissions. + +patch: + summary: Update a meeting agenda item + operationId: update_meeting_agenda_item + tags: + - Meetings + description: Updates the given agenda item. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: meeting_id + required: true + schema: + type: integer + - description: Agenda item identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/meeting_agenda_item_write_model.yml" + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_agenda_item_model.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** manage agendas + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the agenda item or meeting does not exist. + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) + +delete: + summary: Delete a meeting agenda item + operationId: delete_meeting_agenda_item + tags: + - Meetings + description: Deletes the agenda item. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: meeting_id + required: true + schema: + type: integer + - description: Agenda item identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '204': + description: Returned if the agenda item was successfully deleted + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** manage agendas + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the agenda item or meeting does not exist. diff --git a/docs/api/apiv3/paths/meeting_agenda_items.yml b/docs/api/apiv3/paths/meeting_agenda_items.yml new file mode 100644 index 00000000000..590180f157d --- /dev/null +++ b/docs/api/apiv3/paths/meeting_agenda_items.yml @@ -0,0 +1,104 @@ +# /api/v3/meetings/{id}/agenda_items +--- +get: + summary: List meeting agenda items + operationId: list_meeting_agenda_items + tags: + - Meetings + description: Lists all agenda items for the given meeting. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_agenda_item_collection_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings + +post: + summary: Create meeting agenda item + operationId: create_meeting_agenda_item + tags: + - Meetings + description: Creates a new agenda item for the given meeting. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/meeting_agenda_item_write_model.yml" + responses: + '201': + description: Created + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_agenda_item_model.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** manage agendas + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the meeting does not exist or the client does not have sufficient permissions to see it. + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) diff --git a/docs/api/apiv3/paths/meeting_form.yml b/docs/api/apiv3/paths/meeting_form.yml new file mode 100644 index 00000000000..f354e2b5027 --- /dev/null +++ b/docs/api/apiv3/paths/meeting_form.yml @@ -0,0 +1,42 @@ +# /api/v3/meetings/{id}/form +--- +post: + summary: Meeting update form + operationId: meeting_update_form + tags: + - Meetings + description: '' + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + headers: {} + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** edit meetings in the meeting's project + headers: {} + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" diff --git a/docs/api/apiv3/paths/meeting_schema.yml b/docs/api/apiv3/paths/meeting_schema.yml new file mode 100644 index 00000000000..28dc2d01491 --- /dev/null +++ b/docs/api/apiv3/paths/meeting_schema.yml @@ -0,0 +1,29 @@ +# /api/v3/meetings/schema +--- +get: + summary: Get meeting schema + operationId: get_meeting_schema + tags: + - Meetings + description: |- + Retrieves the schema for meetings, providing information about which properties are writable, + their types, and constraints. + responses: + '200': + description: OK + headers: {} + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** view meetings in any project diff --git a/docs/api/apiv3/paths/meeting_section.yml b/docs/api/apiv3/paths/meeting_section.yml new file mode 100644 index 00000000000..79c167db431 --- /dev/null +++ b/docs/api/apiv3/paths/meeting_section.yml @@ -0,0 +1,169 @@ +# /api/v3/meetings/{meeting_id}/sections/{id} +--- +get: + summary: Get a meeting section + operationId: get_meeting_section + tags: + - Meetings + description: Retrieve an individual section of a meeting. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: meeting_id + required: true + schema: + type: integer + - description: Section identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_section_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the section or meeting does not exist or the client does not have sufficient permissions. + +patch: + summary: Update a meeting section + operationId: update_meeting_section + tags: + - Meetings + description: Updates the given section. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: meeting_id + required: true + schema: + type: integer + - description: Section identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/meeting_section_write_model.yml" + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_section_model.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** manage agendas + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the section or meeting does not exist. + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) + +delete: + summary: Delete a meeting section + operationId: delete_meeting_section + tags: + - Meetings + description: Deletes the section and all its agenda items. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: meeting_id + required: true + schema: + type: integer + - description: Section identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '204': + description: Returned if the section was successfully deleted + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** manage agendas + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the section or meeting does not exist. diff --git a/docs/api/apiv3/paths/meeting_sections.yml b/docs/api/apiv3/paths/meeting_sections.yml new file mode 100644 index 00000000000..5d5bf04218a --- /dev/null +++ b/docs/api/apiv3/paths/meeting_sections.yml @@ -0,0 +1,104 @@ +# /api/v3/meetings/{id}/sections +--- +get: + summary: List meeting sections + operationId: list_meeting_sections + tags: + - Meetings + description: Lists all sections for the given meeting. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_section_collection_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings + +post: + summary: Create meeting section + operationId: create_meeting_section + tags: + - Meetings + description: Creates a new section for the given meeting. + parameters: + - description: Meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/meeting_section_write_model.yml" + responses: + '201': + description: Created + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_section_model.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** manage agendas + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the meeting does not exist or the client does not have sufficient permissions to see it. + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) diff --git a/docs/api/apiv3/paths/meetings.yml b/docs/api/apiv3/paths/meetings.yml index 8550f7ebabe..6901a7126c1 100644 --- a/docs/api/apiv3/paths/meetings.yml +++ b/docs/api/apiv3/paths/meetings.yml @@ -5,7 +5,7 @@ get: operationId: list_meetings tags: - Meetings - description: |- + description: |- Retrieve a paginated collection of meetings visible to the authenticated user. responses: '200': @@ -17,3 +17,58 @@ get: $ref: "../components/examples/meeting_collection_simple_response.yml" schema: $ref: "../components/schemas/meeting_collection_model.yml" + +post: + summary: Create meeting + operationId: create_meeting + tags: + - Meetings + description: |- + Creates a new meeting applying the attributes provided in the body. + + You can use the form and schema to retrieve the valid attribute values and by that be guided towards + successful creation. + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/meeting_write_model.yml" + examples: + 'create meeting': + $ref: '../components/examples/meeting_create_request.yml' + responses: + '201': + description: Created + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_model.yml" + examples: + response: + $ref: '../components/examples/meeting_simple_response.yml' + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** create meetings + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) diff --git a/docs/api/apiv3/paths/meetings_form.yml b/docs/api/apiv3/paths/meetings_form.yml new file mode 100644 index 00000000000..f3fb3c2b7e4 --- /dev/null +++ b/docs/api/apiv3/paths/meetings_form.yml @@ -0,0 +1,34 @@ +# /api/v3/meetings/form +--- +post: + summary: Meeting create form + operationId: meeting_create_form + tags: + - Meetings + description: '' + responses: + '200': + description: OK + headers: {} + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** create meetings in any project + headers: {} + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" diff --git a/docs/api/apiv3/paths/recurring_meeting.yml b/docs/api/apiv3/paths/recurring_meeting.yml new file mode 100644 index 00000000000..340f0dafc7e --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting.yml @@ -0,0 +1,164 @@ +# /api/v3/recurring_meetings/{id} +--- +get: + summary: Get a recurring meeting + operationId: get_recurring_meeting + tags: + - Recurring Meetings + description: Retrieve an individual recurring meeting series as identified by the id parameter. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project + +patch: + summary: Update recurring meeting + operationId: update_recurring_meeting + tags: + - Recurring Meetings + description: |- + Updates the given recurring meeting series by applying the attributes provided in the body. + If schedule-related attributes (frequency, interval, startTime, endAfter, etc.) are changed, + the meeting schedule is automatically recalculated. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/recurring_meeting_write_model.yml" + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_model.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** edit meetings in the recurring meeting's project + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) + +delete: + summary: Delete recurring meeting + operationId: delete_recurring_meeting + tags: + - Recurring Meetings + description: |- + Deletes the entire recurring meeting series, including the template meeting and all + scheduled occurrences that have not yet been instantiated. Already-created meeting + instances are not deleted. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '204': + description: Returned if the recurring meeting was successfully deleted. + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** delete meetings + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" diff --git a/docs/api/apiv3/paths/recurring_meeting_occurrence.yml b/docs/api/apiv3/paths/recurring_meeting_occurrence.yml new file mode 100644 index 00000000000..7e1bf67e5ea --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting_occurrence.yml @@ -0,0 +1,76 @@ +# /api/v3/recurring_meetings/{id}/occurrences/{start_time} +--- +delete: + summary: Cancel an occurrence + operationId: cancel_recurring_meeting_occurrence + tags: + - Recurring Meetings + description: |- + Cancels the occurrence at the given `start_time` for this recurring meeting series. + A `ScheduledMeeting` record is created (or updated) with `cancelled: true`. + + An occurrence that has already been instantiated as a meeting cannot be cancelled via this + endpoint — delete the meeting directly via `DELETE /api/v3/meetings/{id}` instead. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + - description: The ISO 8601 start time of the occurrence to cancel (e.g. `2025-06-02T10:00:00Z`) + example: "2025-06-02T10:00:00Z" + in: path + name: start_time + required: true + schema: + type: string + format: date-time + responses: + '204': + description: Returned if the occurrence was successfully cancelled. + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** edit meetings in the recurring meeting's project + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project + '409': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:Conflict + message: Cannot cancel an already instantiated occurrence. Delete the meeting instead. + description: |- + Returned if the occurrence has already been instantiated as a meeting. + Delete the meeting directly via `DELETE /api/v3/meetings/{id}` instead. diff --git a/docs/api/apiv3/paths/recurring_meeting_occurrence_init.yml b/docs/api/apiv3/paths/recurring_meeting_occurrence_init.yml new file mode 100644 index 00000000000..8f7d024a47c --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting_occurrence_init.yml @@ -0,0 +1,68 @@ +# /api/v3/recurring_meetings/{id}/occurrences/{start_time}/init +--- +post: + summary: Instantiate an occurrence + operationId: init_recurring_meeting_occurrence + tags: + - Recurring Meetings + description: |- + Instantiates the occurrence at the given `start_time` by creating a real meeting from + the recurring meeting's template. Returns the newly created `Meeting` resource. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + - description: The ISO 8601 start time of the occurrence to instantiate (e.g. `2025-06-02T10:00:00Z`) + example: "2025-06-02T10:00:00Z" + in: path + name: start_time + required: true + schema: + type: string + format: date-time + responses: + '201': + description: Created — the occurrence was successfully instantiated as a meeting. + content: + application/hal+json: + schema: + $ref: "../components/schemas/meeting_model.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** create meetings in the recurring meeting's project + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) diff --git a/docs/api/apiv3/paths/recurring_meeting_occurrences_cancelled.yml b/docs/api/apiv3/paths/recurring_meeting_occurrences_cancelled.yml new file mode 100644 index 00000000000..9b54e7e4dfe --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting_occurrences_cancelled.yml @@ -0,0 +1,40 @@ +# /api/v3/recurring_meetings/{id}/occurrences/cancelled +--- +get: + summary: List cancelled occurrences of a recurring meeting + operationId: list_recurring_meeting_occurrences_cancelled + tags: + - Recurring Meetings + description: |- + Returns a collection of cancelled occurrences for the given recurring meeting. + Only persisted `ScheduledMeeting` records with `cancelled: true` are returned. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_occurrence_collection_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project diff --git a/docs/api/apiv3/paths/recurring_meeting_occurrences_open.yml b/docs/api/apiv3/paths/recurring_meeting_occurrences_open.yml new file mode 100644 index 00000000000..35fd2dddad0 --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting_occurrences_open.yml @@ -0,0 +1,40 @@ +# /api/v3/recurring_meetings/{id}/occurrences/open +--- +get: + summary: List open occurrences of a recurring meeting + operationId: list_recurring_meeting_occurrences_open + tags: + - Recurring Meetings + description: |- + Returns a collection of open (instantiated, non-cancelled) occurrences for the given recurring meeting. + Only persisted `ScheduledMeeting` records that have an associated meeting and are not cancelled are returned. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_occurrence_collection_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project diff --git a/docs/api/apiv3/paths/recurring_meeting_occurrences_past.yml b/docs/api/apiv3/paths/recurring_meeting_occurrences_past.yml new file mode 100644 index 00000000000..f85e8b54b6d --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting_occurrences_past.yml @@ -0,0 +1,40 @@ +# /api/v3/recurring_meetings/{id}/occurrences/past +--- +get: + summary: List past occurrences of a recurring meeting + operationId: list_recurring_meeting_occurrences_past + tags: + - Recurring Meetings + description: |- + Returns a collection of past, non-cancelled occurrences for the given recurring meeting. + Only persisted `ScheduledMeeting` records with a `start_time` in the past are returned. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_occurrence_collection_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project diff --git a/docs/api/apiv3/paths/recurring_meeting_occurrences_upcoming.yml b/docs/api/apiv3/paths/recurring_meeting_occurrences_upcoming.yml new file mode 100644 index 00000000000..f063b3f910c --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meeting_occurrences_upcoming.yml @@ -0,0 +1,51 @@ +# /api/v3/recurring_meetings/{id}/occurrences/upcoming +--- +get: + summary: List upcoming occurrences of a recurring meeting + operationId: list_recurring_meeting_occurrences_upcoming + tags: + - Recurring Meetings + description: |- + Returns a collection of upcoming occurrences for the given recurring meeting. + The list merges computed occurrences from the recurrence schedule (via IceCube) with + any persisted `ScheduledMeeting` records, including already-instantiated open meetings. + + Occurrences are ordered chronologically and limited by the `limit` parameter. + parameters: + - description: Recurring meeting identifier + example: 1 + in: path + name: id + required: true + schema: + type: integer + - description: Maximum number of occurrences to return. Defaults to 20. + example: 20 + in: query + name: limit + required: false + schema: + type: integer + default: 20 + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_occurrence_collection_model.yml" + '404': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:NotFound + message: The requested resource could not be found. + description: |- + Returned if the recurring meeting does not exist or the client does not have sufficient permissions to see it. + + **Required permission:** view meetings in the recurring meeting's project diff --git a/docs/api/apiv3/paths/recurring_meetings.yml b/docs/api/apiv3/paths/recurring_meetings.yml new file mode 100644 index 00000000000..f007b219a92 --- /dev/null +++ b/docs/api/apiv3/paths/recurring_meetings.yml @@ -0,0 +1,63 @@ +# /api/v3/recurring_meetings +--- +get: + summary: List all visible recurring meetings + operationId: list_recurring_meetings + tags: + - Recurring Meetings + description: |- + Retrieve a paginated collection of recurring meetings visible to the authenticated user. + responses: + '200': + description: OK + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_collection_model.yml" + +post: + summary: Create recurring meeting + operationId: create_recurring_meeting + tags: + - Recurring Meetings + description: |- + Creates a new recurring meeting series applying the attributes provided in the body. + A template meeting is automatically created alongside the recurring meeting. + requestBody: + content: + application/json: + schema: + $ref: "../components/schemas/recurring_meeting_write_model.yml" + responses: + '201': + description: Created + content: + application/hal+json: + schema: + $ref: "../components/schemas/recurring_meeting_model.yml" + '400': + $ref: "../components/responses/invalid_request_body.yml" + '403': + content: + application/hal+json: + schema: + $ref: "../components/schemas/error_response.yml" + examples: + response: + value: + _type: Error + errorIdentifier: urn:openproject-org:api:v3:errors:MissingPermission + message: You are not authorized to access this resource. + description: |- + Returned if the client does not have sufficient permissions. + + **Required permission:** create meetings in the given project + '406': + $ref: "../components/responses/missing_content_type.yml" + '415': + $ref: "../components/responses/unsupported_media_type.yml" + '422': + description: |- + Returned if: + + * a constraint for a property was violated (`PropertyConstraintViolation`) diff --git a/docs/development/code-review-guidelines/README.md b/docs/development/code-review-guidelines/README.md index 7b389298cd5..4c3756dffe6 100644 --- a/docs/development/code-review-guidelines/README.md +++ b/docs/development/code-review-guidelines/README.md @@ -16,7 +16,10 @@ The same is true for eslint. Your editor will likely have support for eslint che **Lefthook** -For automatically linting your files on committing them, please have a look at [Lefthook](https://github.com/evilmartians/lefthook). You can install these rules by using `bundle exec lefthook install`. +For automatically linting your files on committing them, please have a look +at [Lefthook](https://github.com/evilmartians/lefthook). You can install these rules by using `lefthook install`. There +are several options on how to install `lefthook` on your system, including using `brew`, `apt`, `bundle`, or `npm`. For +example, one can add a `Gemfile.local` and thus the `lefthook` to the bundle. ### Structure of commit messages 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..73538ba4860 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -67,7 +67,6 @@ "@stimulus-components/auto-submit": "^6.0.0", "@stimulus-components/reveal": "^5.0.0", "@tiptap/extensions": "^3.20.0", - "@types/jquery.cookie": "^1.4.36", "@uirouter/angular": "^17.0.0", "@uirouter/core": "^6.1.0", "@uirouter/rx": "^1.0.0", @@ -93,7 +92,6 @@ "idiomorph": "^0.7.4", "jquery": "^3.7.1", "jquery.caret": "^0.3.1", - "jquery.cookie": "^1.4.1", "json5": "^2.2.2", "lit-html": "^3.3.2", "lodash": "^4.17.23", @@ -126,8 +124,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", @@ -139,8 +136,8 @@ "@angular-eslint/template-parser": "20.7.0", "@angular/language-service": "21.1.6", "@eslint/js": "^9.39.2", - "@html-eslint/eslint-plugin": "^0.57.1", - "@html-eslint/parser": "^0.57.1", + "@html-eslint/eslint-plugin": "^0.58.1", + "@html-eslint/parser": "^0.58.1", "@jsdevtools/coverage-istanbul-loader": "3.0.5", "@stylistic/eslint-plugin": "^5.7.1", "@types/codemirror": "5.60.5", @@ -173,7 +170,7 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^17.3.0", - "jasmine-core": "~6.0.1", + "jasmine-core": "~6.1.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", @@ -4804,19 +4801,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/css-tree": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.9.tgz", - "integrity": "sha512-3D5/OHibNEGk+wKwNwMbz63NMf367EoR4mVNNpxddCHKEb2Nez7z62J2U6YjtErSsZDoY0CsccmoUpdEbkogNA==", - "dev": true, - "dependencies": { - "mdn-data": "2.23.0", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/@eslint/eslintrc": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", @@ -5269,9 +5253,9 @@ } }, "node_modules/@hono/node-server": { - "version": "1.19.10", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.10.tgz", - "integrity": "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==", + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==", "engines": { "node": ">=18.14.1" }, @@ -5302,13 +5286,12 @@ } }, "node_modules/@html-eslint/core": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/core/-/core-0.57.0.tgz", - "integrity": "sha512-X/cKrOmXrxZSdgyKwtbaCuuJ1k/u82MK58Q6p1TzfwPatwIYx+icfBv1Vp1dLui0L0y1fwBW4H+TKhBf7mMKmg==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/core/-/core-0.58.1.tgz", + "integrity": "sha512-GHYDt2Q3ws9aa0/bmMhkv21ExQJnrjKY/iByjdBVp3lBq49wlzIzvAfcx4Bsp+RMV3oPZhzlnLhPpXLuVYt2mQ==", "dev": true, "dependencies": { - "@html-eslint/types": "^0.57.0", - "eslint": "^9.39.1", + "@html-eslint/types": "^0.58.1", "html-standard": "^0.0.13" }, "engines": { @@ -5316,17 +5299,17 @@ } }, "node_modules/@html-eslint/eslint-plugin": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.57.1.tgz", - "integrity": "sha512-IDfdk3V27eebNpdXD2NLy/lnTSbUuKrro/6YJICBn/9aiXPXagNqWJB38qcSWEoxADbXfSSn17DJWcXvQTkHBg==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.58.1.tgz", + "integrity": "sha512-aizTTKbNF2sW+lXWP+uWBoo5Ud9xtUkr70+0pYhItwJF0yhRqLQ91PhW+9afC0daymQjn13MunzDPwGPG0seDg==", "dev": true, "dependencies": { "@eslint/plugin-kit": "^0.4.1", - "@html-eslint/core": "^0.57.0", - "@html-eslint/parser": "^0.57.1", - "@html-eslint/template-parser": "^0.57.0", - "@html-eslint/template-syntax-parser": "^0.57.0", - "@html-eslint/types": "^0.57.0", + "@html-eslint/core": "^0.58.1", + "@html-eslint/parser": "^0.58.1", + "@html-eslint/template-parser": "^0.58.1", + "@html-eslint/template-syntax-parser": "^0.58.1", + "@html-eslint/types": "^0.58.1", "@rviscomi/capo.js": "^2.1.0", "html-standard": "^0.0.13" }, @@ -5338,47 +5321,45 @@ } }, "node_modules/@html-eslint/parser": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.57.1.tgz", - "integrity": "sha512-nQ5vw7Os+Snjxq9hLLBak2bv502Obn77BNOWfGK2+GIrShxtGd8w1ehlKW3EB5/RQzqBk6VDK8nPfexlR3M7kg==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.58.1.tgz", + "integrity": "sha512-a87peH9HcVDrKZZIYdfMlPZ+72nIktAitKcdoHQevuaXWsgvDtClKihJyy5dZS9md6hIbCh62Og5gQRhl85ZMg==", "dev": true, "dependencies": { - "@eslint/css-tree": "^3.6.9", - "@html-eslint/template-syntax-parser": "^0.57.0", - "@html-eslint/types": "^0.57.0", + "@html-eslint/template-syntax-parser": "^0.58.1", + "@html-eslint/types": "^0.58.1", "css-tree": "^3.1.0", "es-html-parser": "0.3.1" } }, "node_modules/@html-eslint/template-parser": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.57.0.tgz", - "integrity": "sha512-tddyBo4dEl4W4Ehxuyd6H4jsSqvsfL5F7Bj9/aFfdQyv36q7BGWM2BRHb6FMmYKAPGZ3VzyEbUlcqIwXpDkY3w==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.58.1.tgz", + "integrity": "sha512-qo6jTc4Y6vVgwPc2w+EQigH7uCAn+LExxE5oG1URRT98UiJ7dItX0Qk44r/+5XQwSS1TsdvBNLxM2NAktETSWA==", "dev": true, "dependencies": { - "@html-eslint/types": "^0.57.0", + "@html-eslint/types": "^0.58.1", "es-html-parser": "0.3.1" } }, "node_modules/@html-eslint/template-syntax-parser": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.57.0.tgz", - "integrity": "sha512-vHp5y4TR+HhgMDi3rAkgm90LBptSZaQUJudZSj+WdvnSBjLe/fgJC4aVjtLVHTS9ynORrFio8AmH1Bz20kYk4g==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.58.1.tgz", + "integrity": "sha512-P1ZhxIPm9qFWSees2/EZ7Etg1OXziqzRZEuI9goO91fJS6dmdT4JnHLugN06FLL706RwpvenBUlE0iZA9/MXdg==", "dev": true, "dependencies": { - "@html-eslint/types": "^0.57.0" + "@html-eslint/types": "^0.58.1" } }, "node_modules/@html-eslint/types": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.57.0.tgz", - "integrity": "sha512-wZAHc9FHZRVAcKyx1NdMNGpw1Jo/Anh+9y+bTQ/cKjh5MHJlbs8ogthIG8efBVFIVlIgzxEA8yrX+DPXmuWisA==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.58.1.tgz", + "integrity": "sha512-1F2A5XXpgfHQ8dm14E/EztyERoVldT91VGMZCJECZpidf5Cbc21vxeHLT6/POTJm0ICJOmyBlocF62i/rkoVEQ==", "dev": true, "dependencies": { "@types/css-tree": "^2.3.11", "@types/estree": "^1.0.6", - "es-html-parser": "0.3.1", - "eslint": "^9.39.1" + "es-html-parser": "0.3.1" } }, "node_modules/@humanfs/core": { @@ -9404,18 +9385,11 @@ "version": "3.5.33", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz", "integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==", + "dev": true, "dependencies": { "@types/sizzle": "*" } }, - "node_modules/@types/jquery.cookie": { - "version": "1.4.36", - "resolved": "https://registry.npmjs.org/@types/jquery.cookie/-/jquery.cookie-1.4.36.tgz", - "integrity": "sha512-qtTnH4jHqFWyYX4deNBklWoaK5myKm0WtKf7LbGQB7DUKt6tdAVWfYQ4Kl8Hw/7eNth864Jjts5rBsHjNveR4Q==", - "dependencies": { - "@types/jquery": "*" - } - }, "node_modules/@types/jqueryui": { "version": "1.12.24", "resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.24.tgz", @@ -9601,7 +9575,8 @@ "node_modules/@types/sizzle": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true }, "node_modules/@types/sockjs": { "version": "0.3.36", @@ -15765,9 +15740,9 @@ } }, "node_modules/hono": { - "version": "4.12.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", - "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", + "version": "4.12.14", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", + "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", "engines": { "node": ">=16.9.0" } @@ -17030,9 +17005,9 @@ } }, "node_modules/jasmine-core": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.0.1.tgz", - "integrity": "sha512-gUtzV5ASR0MLBwDNqri4kBsgKNCcRQd9qOlNw/w/deavD0cl3JmWXXfH8JhKM4LTg6LPTt2IOQ4px3YYfgh2Xg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.1.0.tgz", + "integrity": "sha512-p/tjBw58O6vxKIWMlrU+yys8lqR3+l3UrqwNTT7wpj+dQ7N4etQekFM8joI+cWzPDYqZf54kN+hLC1+s5TvZvg==", "dev": true }, "node_modules/jasmine-spec-reporter": { @@ -17102,12 +17077,6 @@ "resolved": "https://registry.npmjs.org/jquery.caret/-/jquery.caret-0.3.1.tgz", "integrity": "sha512-nS2mjMZzP4e4tIOgeTLSs+jFhUsUVZUPgkUMpi4DlJq9SgKEg6w2jf7q8joMJp6v+voJHXrH8rzAnbyxWHwAeA==" }, - "node_modules/jquery.cookie": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", - "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==", - "license": "MIT" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -17967,9 +17936,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", @@ -18578,12 +18547,6 @@ "url": "https://opencollective.com/unified" } }, - "node_modules/mdn-data": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.23.0.tgz", - "integrity": "sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==", - "dev": true - }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -20702,9 +20665,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 +22150,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 +25919,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", @@ -28761,16 +28719,6 @@ "@types/json-schema": "^7.0.15" } }, - "@eslint/css-tree": { - "version": "3.6.9", - "resolved": "https://registry.npmjs.org/@eslint/css-tree/-/css-tree-3.6.9.tgz", - "integrity": "sha512-3D5/OHibNEGk+wKwNwMbz63NMf367EoR4mVNNpxddCHKEb2Nez7z62J2U6YjtErSsZDoY0CsccmoUpdEbkogNA==", - "dev": true, - "requires": { - "mdn-data": "2.23.0", - "source-map-js": "^1.0.1" - } - }, "@eslint/eslintrc": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", @@ -29107,9 +29055,9 @@ } }, "@hono/node-server": { - "version": "1.19.10", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.10.tgz", - "integrity": "sha512-hZ7nOssGqRgyV3FVVQdfi+U4q02uB23bpnYpdvNXkYTRRyWx84b7yf1ans+dnJ/7h41sGL3CeQTfO+ZGxuO+Iw==" + "version": "1.19.13", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.13.tgz", + "integrity": "sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==" }, "@hotwired/stimulus": { "version": "3.2.2", @@ -29131,74 +29079,71 @@ } }, "@html-eslint/core": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/core/-/core-0.57.0.tgz", - "integrity": "sha512-X/cKrOmXrxZSdgyKwtbaCuuJ1k/u82MK58Q6p1TzfwPatwIYx+icfBv1Vp1dLui0L0y1fwBW4H+TKhBf7mMKmg==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/core/-/core-0.58.1.tgz", + "integrity": "sha512-GHYDt2Q3ws9aa0/bmMhkv21ExQJnrjKY/iByjdBVp3lBq49wlzIzvAfcx4Bsp+RMV3oPZhzlnLhPpXLuVYt2mQ==", "dev": true, "requires": { - "@html-eslint/types": "^0.57.0", - "eslint": "^9.39.1", + "@html-eslint/types": "^0.58.1", "html-standard": "^0.0.13" } }, "@html-eslint/eslint-plugin": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.57.1.tgz", - "integrity": "sha512-IDfdk3V27eebNpdXD2NLy/lnTSbUuKrro/6YJICBn/9aiXPXagNqWJB38qcSWEoxADbXfSSn17DJWcXvQTkHBg==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/eslint-plugin/-/eslint-plugin-0.58.1.tgz", + "integrity": "sha512-aizTTKbNF2sW+lXWP+uWBoo5Ud9xtUkr70+0pYhItwJF0yhRqLQ91PhW+9afC0daymQjn13MunzDPwGPG0seDg==", "dev": true, "requires": { "@eslint/plugin-kit": "^0.4.1", - "@html-eslint/core": "^0.57.0", - "@html-eslint/parser": "^0.57.1", - "@html-eslint/template-parser": "^0.57.0", - "@html-eslint/template-syntax-parser": "^0.57.0", - "@html-eslint/types": "^0.57.0", + "@html-eslint/core": "^0.58.1", + "@html-eslint/parser": "^0.58.1", + "@html-eslint/template-parser": "^0.58.1", + "@html-eslint/template-syntax-parser": "^0.58.1", + "@html-eslint/types": "^0.58.1", "@rviscomi/capo.js": "^2.1.0", "html-standard": "^0.0.13" } }, "@html-eslint/parser": { - "version": "0.57.1", - "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.57.1.tgz", - "integrity": "sha512-nQ5vw7Os+Snjxq9hLLBak2bv502Obn77BNOWfGK2+GIrShxtGd8w1ehlKW3EB5/RQzqBk6VDK8nPfexlR3M7kg==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/parser/-/parser-0.58.1.tgz", + "integrity": "sha512-a87peH9HcVDrKZZIYdfMlPZ+72nIktAitKcdoHQevuaXWsgvDtClKihJyy5dZS9md6hIbCh62Og5gQRhl85ZMg==", "dev": true, "requires": { - "@eslint/css-tree": "^3.6.9", - "@html-eslint/template-syntax-parser": "^0.57.0", - "@html-eslint/types": "^0.57.0", + "@html-eslint/template-syntax-parser": "^0.58.1", + "@html-eslint/types": "^0.58.1", "css-tree": "^3.1.0", "es-html-parser": "0.3.1" } }, "@html-eslint/template-parser": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.57.0.tgz", - "integrity": "sha512-tddyBo4dEl4W4Ehxuyd6H4jsSqvsfL5F7Bj9/aFfdQyv36q7BGWM2BRHb6FMmYKAPGZ3VzyEbUlcqIwXpDkY3w==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/template-parser/-/template-parser-0.58.1.tgz", + "integrity": "sha512-qo6jTc4Y6vVgwPc2w+EQigH7uCAn+LExxE5oG1URRT98UiJ7dItX0Qk44r/+5XQwSS1TsdvBNLxM2NAktETSWA==", "dev": true, "requires": { - "@html-eslint/types": "^0.57.0", + "@html-eslint/types": "^0.58.1", "es-html-parser": "0.3.1" } }, "@html-eslint/template-syntax-parser": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.57.0.tgz", - "integrity": "sha512-vHp5y4TR+HhgMDi3rAkgm90LBptSZaQUJudZSj+WdvnSBjLe/fgJC4aVjtLVHTS9ynORrFio8AmH1Bz20kYk4g==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/template-syntax-parser/-/template-syntax-parser-0.58.1.tgz", + "integrity": "sha512-P1ZhxIPm9qFWSees2/EZ7Etg1OXziqzRZEuI9goO91fJS6dmdT4JnHLugN06FLL706RwpvenBUlE0iZA9/MXdg==", "dev": true, "requires": { - "@html-eslint/types": "^0.57.0" + "@html-eslint/types": "^0.58.1" } }, "@html-eslint/types": { - "version": "0.57.0", - "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.57.0.tgz", - "integrity": "sha512-wZAHc9FHZRVAcKyx1NdMNGpw1Jo/Anh+9y+bTQ/cKjh5MHJlbs8ogthIG8efBVFIVlIgzxEA8yrX+DPXmuWisA==", + "version": "0.58.1", + "resolved": "https://registry.npmjs.org/@html-eslint/types/-/types-0.58.1.tgz", + "integrity": "sha512-1F2A5XXpgfHQ8dm14E/EztyERoVldT91VGMZCJECZpidf5Cbc21vxeHLT6/POTJm0ICJOmyBlocF62i/rkoVEQ==", "dev": true, "requires": { "@types/css-tree": "^2.3.11", "@types/estree": "^1.0.6", - "es-html-parser": "0.3.1", - "eslint": "^9.39.1" + "es-html-parser": "0.3.1" } }, "@humanfs/core": { @@ -31583,18 +31528,11 @@ "version": "3.5.33", "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.33.tgz", "integrity": "sha512-SeyVJXlCZpEki5F0ghuYe+L+PprQta6nRZqhONt9F13dWBtR/ftoaIbdRQ7cis7womE+X2LKhsDdDtkkDhJS6g==", + "dev": true, "requires": { "@types/sizzle": "*" } }, - "@types/jquery.cookie": { - "version": "1.4.36", - "resolved": "https://registry.npmjs.org/@types/jquery.cookie/-/jquery.cookie-1.4.36.tgz", - "integrity": "sha512-qtTnH4jHqFWyYX4deNBklWoaK5myKm0WtKf7LbGQB7DUKt6tdAVWfYQ4Kl8Hw/7eNth864Jjts5rBsHjNveR4Q==", - "requires": { - "@types/jquery": "*" - } - }, "@types/jqueryui": { "version": "1.12.24", "resolved": "https://registry.npmjs.org/@types/jqueryui/-/jqueryui-1.12.24.tgz", @@ -31774,7 +31712,8 @@ "@types/sizzle": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", - "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==" + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true }, "@types/sockjs": { "version": "0.3.36", @@ -36029,9 +35968,9 @@ } }, "hono": { - "version": "4.12.7", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", - "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==" + "version": "4.12.14", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", + "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==" }, "hosted-git-info": { "version": "9.0.2", @@ -36873,9 +36812,9 @@ } }, "jasmine-core": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.0.1.tgz", - "integrity": "sha512-gUtzV5ASR0MLBwDNqri4kBsgKNCcRQd9qOlNw/w/deavD0cl3JmWXXfH8JhKM4LTg6LPTt2IOQ4px3YYfgh2Xg==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jasmine-core/-/jasmine-core-6.1.0.tgz", + "integrity": "sha512-p/tjBw58O6vxKIWMlrU+yys8lqR3+l3UrqwNTT7wpj+dQ7N4etQekFM8joI+cWzPDYqZf54kN+hLC1+s5TvZvg==", "dev": true }, "jasmine-spec-reporter": { @@ -36930,11 +36869,6 @@ "resolved": "https://registry.npmjs.org/jquery.caret/-/jquery.caret-0.3.1.tgz", "integrity": "sha512-nS2mjMZzP4e4tIOgeTLSs+jFhUsUVZUPgkUMpi4DlJq9SgKEg6w2jf7q8joMJp6v+voJHXrH8rzAnbyxWHwAeA==" }, - "jquery.cookie": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jquery.cookie/-/jquery.cookie-1.4.1.tgz", - "integrity": "sha512-c/hZOOL+8VSw/FkTVH637gS1/6YzMSCROpTZ2qBYwJ7s7sHajU7uBkSSiE5+GXWwrfCCyO+jsYjUQ7Hs2rIxAA==" - }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -37560,9 +37494,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", @@ -37992,12 +37926,6 @@ "@types/mdast": "^4.0.0" } }, - "mdn-data": { - "version": "2.23.0", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.23.0.tgz", - "integrity": "sha512-786vq1+4079JSeu2XdcDjrhi/Ry7BWtjDl9WtGPWLiIHb2T66GvIVflZTBoSNZ5JqTtJGYEVMuFA/lbQlMOyDQ==", - "dev": true - }, "mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", @@ -39390,9 +39318,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 +40318,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 +42772,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..23d47db5df8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -14,8 +14,8 @@ "@angular-eslint/template-parser": "20.7.0", "@angular/language-service": "21.1.6", "@eslint/js": "^9.39.2", - "@html-eslint/eslint-plugin": "^0.57.1", - "@html-eslint/parser": "^0.57.1", + "@html-eslint/eslint-plugin": "^0.58.1", + "@html-eslint/parser": "^0.58.1", "@jsdevtools/coverage-istanbul-loader": "3.0.5", "@stylistic/eslint-plugin": "^5.7.1", "@types/codemirror": "5.60.5", @@ -48,7 +48,7 @@ "eslint-plugin-react": "^7.37.5", "eslint-plugin-react-hooks": "^7.0.1", "globals": "^17.3.0", - "jasmine-core": "~6.0.1", + "jasmine-core": "~6.1.0", "jasmine-spec-reporter": "~7.0.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", @@ -122,7 +122,6 @@ "@stimulus-components/auto-submit": "^6.0.0", "@stimulus-components/reveal": "^5.0.0", "@tiptap/extensions": "^3.20.0", - "@types/jquery.cookie": "^1.4.36", "@uirouter/angular": "^17.0.0", "@uirouter/core": "^6.1.0", "@uirouter/rx": "^1.0.0", @@ -148,7 +147,6 @@ "idiomorph": "^0.7.4", "jquery": "^3.7.1", "jquery.caret": "^0.3.1", - "jquery.cookie": "^1.4.1", "json5": "^2.2.2", "lit-html": "^3.3.2", "lodash": "^4.17.23", @@ -181,8 +179,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/global_search/input/global-search-input.component.ts b/frontend/src/app/core/global_search/input/global-search-input.component.ts index 5a48ecf60b7..35158e66d93 100644 --- a/frontend/src/app/core/global_search/input/global-search-input.component.ts +++ b/frontend/src/app/core/global_search/input/global-search-input.component.ts @@ -7,7 +7,6 @@ import { ElementRef, HostListener, Input, - NgZone, OnDestroy, ViewChild, ViewEncapsulation, @@ -141,7 +140,6 @@ export class GlobalSearchInputComponent implements AfterViewInit, OnDestroy { readonly deviceService:DeviceService, readonly cdRef:ChangeDetectorRef, readonly halNotification:HalResourceNotificationService, - readonly ngZone:NgZone, readonly recentItemsService:RecentItemsService, ) { populateInputsFromDataset(this); 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..5a5f305c589 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`; } @@ -407,6 +411,12 @@ export class PathHelperService { return `${this.workPackagesPath(null)}/bulk`; } + public workPackagesBulkDeleteDialogPath(ids:string[], backUrl?:string) { + const params = ids.map((id) => `ids[]=${encodeURIComponent(id)}`).join('&'); + const backParam = backUrl ? `&back_url=${encodeURIComponent(backUrl)}` : ''; + return `${this.workPackagesPath(null)}/bulk/delete_dialog?${params}${backParam}`; + } + public workPackagesBulkReassignmentPath() { return `${this.workPackagesPath(null)}/bulk/reassign`; } 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/bcf/fields/display/bcf-thumbnail-field.module.ts b/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts index c0dcd569e31..98a531bca47 100644 --- a/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts +++ b/frontend/src/app/features/bim/bcf/fields/display/bcf-thumbnail-field.module.ts @@ -34,13 +34,14 @@ import { HalLink } from 'core-app/features/hal/hal-link/hal-link'; export class BcfThumbnailDisplayField extends DisplayField { @InjectField() bcfPathHelper:BcfPathHelperService; - public render(element:HTMLElement, displayText:string):void { - const viewpoints = this.resource.bcfViewpoints; + public render(element:HTMLElement, _displayText:string):void { + const viewpoints = this.resource.bcfViewpoints as HalLink[]; if (viewpoints && viewpoints.length > 0) { const viewpoint = viewpoints[0]; - element.innerHTML = ` - - `; + const img = document.createElement('img'); + img.src = this.bcfPathHelper.snapshotPath(viewpoint); + img.classList.add('thumbnail'); + element.appendChild(img); } else { element.innerHTML = ''; } diff --git a/frontend/src/app/features/bim/ifc_models/bcf/list/bcf-list.component.ts b/frontend/src/app/features/bim/ifc_models/bcf/list/bcf-list.component.ts index d41eef463e6..6593200dd8f 100644 --- a/frontend/src/app/features/bim/ifc_models/bcf/list/bcf-list.component.ts +++ b/frontend/src/app/features/bim/ifc_models/bcf/list/bcf-list.component.ts @@ -27,7 +27,7 @@ //++ import { - ChangeDetectionStrategy, Component, Input, NgZone, OnInit, + ChangeDetectionStrategy, Component, Input, OnInit, } from '@angular/core'; import { UIRouterGlobals } from '@uirouter/core'; import { States } from 'core-app/core/states/states.service'; @@ -81,8 +81,6 @@ export class BcfListComponent extends WorkPackageListViewComponent implements Un @InjectField() bcfApi:BcfApiService; - @InjectField() zone:NgZone; - public wpTableConfiguration = { dragAndDropEnabled: false, }; @@ -105,9 +103,7 @@ export class BcfListComponent extends WorkPackageListViewComponent implements Un if (!this.showViewPointInFlight) { this.showViewPointInFlight = true; - this.zone.runOutsideAngular(() => { - setTimeout(() => { this.showViewPointInFlight = false; }, 500); - }); + setTimeout(() => { this.showViewPointInFlight = false; }, 500); const wp = this.states.workPackages.get(workPackageId).value; 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/plugins/plugin-context.ts b/frontend/src/app/features/plugins/plugin-context.ts index c51d0a50d79..fd8ac7963db 100644 --- a/frontend/src/app/features/plugins/plugin-context.ts +++ b/frontend/src/app/features/plugins/plugin-context.ts @@ -1,4 +1,4 @@ -import { ApplicationRef, Injector, NgZone } from '@angular/core'; +import { ApplicationRef, Injector } from '@angular/core'; import { ToastService } from 'core-app/shared/components/toaster/toast.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; import { @@ -89,8 +89,13 @@ export class OpenProjectPluginContext { /* eslint-disable-next-line @typescript-eslint/no-explicit-any */ public readonly hooks:Record unknown) => void> = {}; - // Angular zone reference - @InjectField() public readonly zone:NgZone; + /** + * @deprecated Noop shim — the app is zoneless. Remove usages. + */ + public readonly zone = { + run: (cb:() => T):T => cb(), + runOutsideAngular: (cb:() => T):T => cb(), + }; // Angular application reference @InjectField() public readonly appRef:ApplicationRef; @@ -105,12 +110,10 @@ export class OpenProjectPluginContext { } /** - * Run the given callback in the angular zone, - * resulting in triggered change detection that would otherwise not occur. - * - * @param cb + * @deprecated This method is a no-op since the app is zoneless. + * Replace calls with direct invocation of the callback. */ public runInZone(cb:() => void) { - this.zone.run(cb); + cb(); } } 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.spec.ts b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.spec.ts new file mode 100644 index 00000000000..e9ff1d4b079 --- /dev/null +++ b/frontend/src/app/features/work-packages/components/wp-custom-actions/date-action/custom-date-action-admin.component.spec.ts @@ -0,0 +1,81 @@ +//-- 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 { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { By } from '@angular/platform-browser'; +import { I18nService } from 'core-app/core/i18n/i18n.service'; +import { CustomDateActionAdminComponent } from './custom-date-action-admin.component'; + +describe('CustomDateActionAdminComponent', () => { + let fixture:ComponentFixture; + let component:CustomDateActionAdminComponent; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [CustomDateActionAdminComponent], + imports: [FormsModule], + providers: [ + { + provide: I18nService, + useValue: { + t:(key:string) => { + switch (key) { + case 'js.custom_actions.date.specific': + return 'on'; + case 'js.custom_actions.date.current_date': + return 'Current date'; + default: + return key; + } + }, + }, + }, + ], + schemas: [CUSTOM_ELEMENTS_SCHEMA], + }).compileComponents(); + + fixture = TestBed.createComponent(CustomDateActionAdminComponent); + component = fixture.componentInstance; + fixture.nativeElement.dataset.fieldName = 'custom_action[actions][date]'; + }); + + it('stores the current date sentinel when the operator is changed to current date', () => { + fixture.detectChanges(); + + const select = fixture.debugElement.query(By.css('select')).nativeElement as HTMLSelectElement; + const hiddenInput = fixture.debugElement.query(By.css('input[type="hidden"]')).nativeElement as HTMLInputElement; + + select.value = 'current'; + select.dispatchEvent(new Event('change')); + + expect(component.selectedOperatorKey).toBe('current'); + expect(hiddenInput.value).toBe('%CURRENT_DATE%'); + }); +}); 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..cfb0e4f987e 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,28 +77,28 @@ 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 = ''; } this.updateDbValue(); + this.cdRef.detectChanges(); } 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-autocomplete/wp-relations-autocomplete.component.ts b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts index 5e79e5fb615..d126b2a93b9 100644 --- a/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-relations/wp-relations-create/wp-relations-autocomplete/wp-relations-autocomplete.component.ts @@ -94,14 +94,12 @@ export class WorkPackageRelationsAutocompleteComponent extends OpAutocompleterCo opened() { // Force reposition as a workaround for BUG // https://github.com/ng-select/ng-select/issues/1259 - this.ngZone.runOutsideAngular(() => { - setTimeout(() => { - this.ngSelectInstance.dropdownPanel.adjustPosition(); - document.querySelector(this.hiddenOverflowContainer)?.addEventListener('scroll', () => { - this.ngSelectInstance.close(); - }, { once: true }); - }, 25); - }); + setTimeout(() => { + this.ngSelectInstance.dropdownPanel.adjustPosition(); + document.querySelector(this.hiddenOverflowContainer)?.addEventListener('scroll', () => { + this.ngSelectInstance.close(); + }, { once: true }); + }, 25); } getAutocompleterData(query:string|null):Observable { 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 ef14bf00ed9..8afa03857a1 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'; @@ -32,6 +32,10 @@ import { PortalOutletTarget } from 'core-app/shared/components/modal/portal-outl 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; @@ -97,7 +101,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(); + }); }); } @@ -164,6 +171,7 @@ export class WorkPackageEmbeddedTableComponent extends WorkPackageEmbeddedBaseCo .then((query:QueryResource) => { this.initializeStates(query); this.onQueryLoaded.emit(query); + this.cdRef.markForCheck(); return query; }) .catch((error) => { @@ -171,6 +179,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-table/wp-table.component.ts b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts index b370d26c6c2..311f9a11443 100644 --- a/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts +++ b/frontend/src/app/features/work-packages/components/wp-table/wp-table.component.ts @@ -34,7 +34,6 @@ import { EventEmitter, Injector, Input, - NgZone, OnInit, Output, ViewEncapsulation, OnDestroy, @@ -144,7 +143,6 @@ export class WorkPackagesTableComponent extends UntilDestroyedMixin implements O readonly querySpace:IsolatedQuerySpace, readonly I18n:I18nService, readonly cdRef:ChangeDetectorRef, - readonly zone:NgZone, readonly wpTableGroupBy:WorkPackageViewGroupByService, readonly wpTableTimeline:WorkPackageViewTimelineService, readonly wpTableColumns:WorkPackageViewColumnsService, 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/openproject-work-packages.module.ts b/frontend/src/app/features/work-packages/openproject-work-packages.module.ts index 021d9d994fd..490ca2e61c0 100644 --- a/frontend/src/app/features/work-packages/openproject-work-packages.module.ts +++ b/frontend/src/app/features/work-packages/openproject-work-packages.module.ts @@ -272,7 +272,6 @@ import { import { QuerySharingModalComponent } from 'core-app/shared/components/modals/share-modal/query-sharing.modal'; import { SaveQueryModalComponent } from 'core-app/shared/components/modals/save-modal/save-query.modal'; import { QuerySharingFormComponent } from 'core-app/shared/components/modals/share-modal/query-sharing-form.component'; -import { WpDestroyModalComponent } from 'core-app/shared/components/modals/wp-destroy-modal/wp-destroy.modal'; import { WorkPackageTypeStatusComponent, } from 'core-app/features/work-packages/components/wp-type-status/wp-type-status.component'; @@ -617,7 +616,6 @@ import { WorkPackageFullViewEntryComponent } from 'core-app/features/work-packag QuerySharingFormComponent, QuerySharingModalComponent, SaveQueryModalComponent, - WpDestroyModalComponent, WorkPackageShareModalComponent, WorkPackageReminderModalComponent, 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-list-view/wp-list-view.component.ts b/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts index 6f0bc5e1db0..913a7fa4b57 100644 --- a/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts +++ b/frontend/src/app/features/work-packages/routing/wp-list-view/wp-list-view.component.ts @@ -33,7 +33,6 @@ import { ElementRef, inject, Injector, - NgZone, OnInit, } from '@angular/core'; import { take } from 'rxjs/operators'; @@ -83,8 +82,7 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements readonly CurrentProject = inject(CurrentProjectService); readonly wpDisplayRepresentation = inject(WorkPackageViewDisplayRepresentationService); readonly cdRef = inject(ChangeDetectorRef); - readonly elementRef = inject(ElementRef); - readonly ngZone = inject(NgZone); + readonly elementRef = inject>(ElementRef); readonly wpTableBaseline = inject(WorkPackageViewBaselineService); readonly pathHelper = inject(PathHelperService); @@ -135,24 +133,21 @@ export class WorkPackageListViewComponent extends UntilDestroyedMixin implements // the 'back button', the last selected card is visible on this list. // ngAfterViewInit doesn't find the .-checked elements on components // that inherit from this class (BcfListContainerComponent) so - // opting for a timeout 'runOutsideAngular' to avoid running change - // detection on the entire app - this.ngZone.runOutsideAngular(() => { - setTimeout(() => { - const selectedRow = this.elementRef.nativeElement.querySelector('.wp-table--row.-checked'); - const selectedCard = this.elementRef.nativeElement.querySelector('[data-test-selector="op-wp-single-card"].-checked'); + // opting for a timeout to defer until the DOM is ready + setTimeout(() => { + const selectedRow = this.elementRef.nativeElement.querySelector('.wp-table--row.-checked'); + const selectedCard = this.elementRef.nativeElement.querySelector('[data-test-selector="op-wp-single-card"].-checked'); - // The header of the table hides the scrolledIntoView element - // so we scrollIntoView the previous element, if any - if (selectedRow?.previousSibling) { - selectedRow.previousSibling.scrollIntoView({ block: 'start' }); - } + // The header of the table hides the scrolledIntoView element + // so we scrollIntoView the previous element, if any + if (selectedRow?.previousElementSibling) { + selectedRow.previousElementSibling.scrollIntoView({ block: 'start' }); + } - if (selectedCard) { - selectedCard.scrollIntoView({ block: 'start' }); - } - }, 0); - }); + if (selectedCard) { + selectedCard.scrollIntoView({ block: 'start' }); + } + }, 0); } protected setupInformationLoadedListener() { 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.component.ts b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts index 8a9a0d59bde..f60b346e471 100644 --- a/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts +++ b/frontend/src/app/shared/components/autocompleter/op-autocompleter/op-autocompleter.component.ts @@ -12,7 +12,6 @@ import { HostBinding, Injector, Input, - NgZone, OnChanges, OnInit, Output, @@ -306,7 +305,6 @@ export class OpAutocompleterComponent { - setTimeout(() => { - this.ngSelectInstance.focus(); - }, 25); - }); + setTimeout(() => { + this.ngSelectInstance.focus(); + }, 25); } public closed():void { 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 bba4d452189..4ca97fd421a 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.'); @@ -340,4 +355,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/header-project-select/header-project-select.component.html b/frontend/src/app/shared/components/header-project-select/header-project-select.component.html index e361ba1c476..31bb8c79774 100644 --- a/frontend/src/app/shared/components/header-project-select/header-project-select.component.html +++ b/frontend/src/app/shared/components/header-project-select/header-project-select.component.html @@ -41,15 +41,16 @@ > @if (displayMode !== 'favorited' || (favorites$ | async)?.length > 0) { + > } diff --git a/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts b/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts index 7cea4a38f8b..ef6b27a2ff1 100644 --- a/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts +++ b/frontend/src/app/shared/components/header-project-select/header-project-select.component.ts @@ -28,7 +28,7 @@ import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { ChangeDetectionStrategy, Component, HostBinding, OnDestroy, OnInit, ViewEncapsulation } from '@angular/core'; +import { ChangeDetectionStrategy, Component, HostBinding, OnDestroy, OnInit, ViewEncapsulation, ElementRef, ViewChild, AfterViewInit } from '@angular/core'; import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; import { BehaviorSubject, Observable, ReplaySubject, Subscription } from 'rxjs'; import { map, shareReplay, take, tap } from 'rxjs/operators'; @@ -55,9 +55,17 @@ import { ConfigurationService } from 'core-app/core/config/configuration.service ], standalone: false, }) -export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implements OnInit, OnDestroy { +export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implements OnInit, OnDestroy, AfterViewInit { @HostBinding('class.op-project-select') className = true; + @ViewChild('projectSearchField', { read: ElementRef }) + + projectSearchField?:ElementRef; + + private activeProjectId:number|null = null; + + private readonly listboxId = 'op-header-project-select-listbox'; + public dropModalOpen = false; public textFieldFocused = false; @@ -193,6 +201,38 @@ export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implemen this.onTextInput.unsubscribe(); } + ngAfterViewInit():void { + this.searchableProjectListService.selectedItemID$ + .pipe(this.untilDestroyed()) + .subscribe((selectedItemID:number|null) => { + this.activeProjectId = selectedItemID; + this.syncSearchInputAccessibility(); + }); + } + + private syncSearchInputAccessibility():void { + requestAnimationFrame(() => { + const input = this.projectSearchField?.nativeElement.querySelector('input') as HTMLInputElement | null; + + if (!input) { + return; + } + + input.setAttribute('role', 'combobox'); + input.setAttribute('aria-autocomplete', 'list'); + input.setAttribute('aria-haspopup', 'listbox'); + input.setAttribute('aria-expanded', String(this.dropModalOpen)); + input.setAttribute('aria-controls', this.listboxId); + input.setAttribute('aria-label', this.searchPlaceHolder()); + + if (this.dropModalOpen && this.activeProjectId !== null) { + input.setAttribute('aria-activedescendant', `op-header-project-select-option-${this.activeProjectId}`); + } else { + input.removeAttribute('aria-activedescendant'); + } + }); + } + toggleDropModal():void { this.subscriptionComplete$.pipe(take(1)).subscribe(() => { this.dropModalOpen = !this.dropModalOpen; @@ -203,6 +243,7 @@ export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implemen } else { this.searchableProjectListService.disableLoading(); } + this.syncSearchInputAccessibility(); }); } @@ -219,6 +260,7 @@ export class OpHeaderProjectSelectComponent extends UntilDestroyedMixin implemen this.dropModalOpen = false; this.searchableProjectListService.disableLoading(); this.searchableProjectListService.searchText = ''; + this.syncSearchInputAccessibility(); } currentProjectName():string { diff --git a/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.html b/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.html index 894a6d74c7b..e9de11e22b7 100644 --- a/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.html +++ b/frontend/src/app/shared/components/header-project-select/list/header-project-select-list.component.html @@ -1,6 +1,7 @@ @for (project of filteredProjects; track project; let i = $index; let isFirst = $first; let isLast = $last) {
  • @@ -8,6 +9,9 @@ - + } @@ -62,6 +66,10 @@ @if (project.disabled) { (); @Input() @HostBinding('class.op-header-project-select-list--root') root = false; @@ -49,7 +59,7 @@ export class OpHeaderProjectSelectListComponent implements OnInit, OnChanges { public text = { does_not_match_search: this.I18n.t('js.include_projects.tooltip.does_not_match_search'), - include_all_selected: this.I18n.t('js.include_projects.tooltip.include_all_selected'), + include_all_selected: this.I18n.t('js.include_projects.tooltip.include_all_selected') }; public portfolioModelsEnabled = this.configuration.activeFeatureFlags.includes('portfolioModels'); @@ -118,4 +128,8 @@ export class OpHeaderProjectSelectListComponent implements OnInit, OnChanges { return `${url}?jump=${encodeURIComponent(currentMenuItem)}`; } + + optionId(project:IProjectData):string { + return `op-header-project-select-option-${project.id}`; + } } 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.html b/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.html deleted file mode 100644 index e8494a84166..00000000000 --- a/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.html +++ /dev/null @@ -1,89 +0,0 @@ -
    -
    {{text.title}}
    - -
    - @if (singleWorkPackage) { -

    - - {{ singleWorkPackage.subject }} #{{ singleWorkPackage.id }} - ? -

    - @if (singleWorkPackageChildren && singleWorkPackageChildren.length > 0) { -
    -

    - - : - -

    -
      - @for (child of singleWorkPackageChildren; track child) { -
    • -
      - # - -
      -
    • - } -
    -

    - -

    -
    - } - } - @if (workPackages.length > 1) { -

    - - -

    -
      - @for (wp of workPackages; track wp) { -
    • -
      - # - - @if (children(wp).length > 0) { - (+ {{ text.childCount(wp) }}) - } -
      -
    • - } -
    - } - @if (mustConfirmChildren) { -
    - -
    - } -
    - -
    -
    - - -
    -
    -
    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 deleted file mode 100644 index 1b83db1e8e7..00000000000 --- a/frontend/src/app/shared/components/modals/wp-destroy-modal/wp-destroy.modal.ts +++ /dev/null @@ -1,183 +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 { WorkPackagesListService } from 'core-app/features/work-packages/components/wp-list/wp-list.service'; -import { States } from 'core-app/core/states/states.service'; -import { - ChangeDetectorRef, - Component, - ElementRef, - Inject, - OnInit, -} from '@angular/core'; -import { OpModalComponent } from 'core-app/shared/components/modal/modal.component'; -import { OpModalLocalsToken } from 'core-app/shared/components/modal/modal.service'; -import { OpModalLocalsMap } from 'core-app/shared/components/modal/modal.types'; -import { WorkPackageResource } from 'core-app/features/hal/resources/work-package-resource'; -import { WorkPackageViewFocusService } from 'core-app/features/work-packages/routing/wp-view-base/view-services/wp-view-focus.service'; -import { StateService } from '@uirouter/core'; -import { I18nService } from 'core-app/core/i18n/i18n.service'; -import { WorkPackageNotificationService } from 'core-app/features/work-packages/services/notifications/work-package-notification.service'; -import { WorkPackageService } from 'core-app/features/work-packages/services/work-package.service'; -import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; -import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; -import { BackRoutingService } from 'core-app/features/work-packages/components/back-routing/back-routing.service'; - -@Component({ - templateUrl: './wp-destroy.modal.html', - standalone: false, -}) -export class WpDestroyModalComponent extends OpModalComponent implements OnInit { - // When deleting multiple - public workPackages:WorkPackageResource[]; - - public workPackageLabel:string; - - // Single work package - public singleWorkPackage:WorkPackageResource; - - public singleWorkPackageChildren:WorkPackageResource[]; - - public busy = false; - - // Need to confirm deletion when children are involved - public childrenDeletionConfirmed = false; - - public text = { - label_visibility_settings: this.I18n.t('js.label_visibility_settings'), - button_save: this.I18n.t('js.modals.button_save'), - confirm: this.I18n.t('js.modals.button_delete'), - warning: this.I18n.t('js.label_warning'), - cancel: this.I18n.t('js.button_cancel'), - close: this.I18n.t('js.close_popup_title'), - label_confirm_children_deletion: this.I18n.t('js.modals.destroy_work_package.confirm_deletion_children'), - title: '', - bulk_text: '', - single_text: this.I18n.t('js.modals.destroy_work_package.single_text'), - childCount: (_wp:WorkPackageResource):string => '', - hasChildren: (_wp:WorkPackageResource):string => '', - deletesChildren: '', - }; - - constructor( - readonly elementRef:ElementRef, - readonly workPackageService:WorkPackageService, - @Inject(OpModalLocalsToken) public locals:OpModalLocalsMap, - readonly I18n:I18nService, - readonly cdRef:ChangeDetectorRef, - readonly $state:StateService, - readonly states:States, - readonly wpTableFocus:WorkPackageViewFocusService, - readonly wpListService:WorkPackagesListService, - readonly notificationService:WorkPackageNotificationService, - readonly currentProject:CurrentProjectService, - readonly pathHelper:PathHelperService, - readonly backRoutingService:BackRoutingService, - ) { - super(locals, cdRef, elementRef); - } - - ngOnInit():void { - super.ngOnInit(); - - this.workPackages = this.locals.workPackages; - this.workPackageLabel = this.I18n.t('js.units.workPackage', { count: this.workPackages.length }); - - // Ugly way to provide the same view bindings as the ng-init in the previous template. - if (this.workPackages.length === 1) { - this.singleWorkPackage = this.workPackages[0]; - this.singleWorkPackageChildren = this.singleWorkPackage.children; - } - - this.text.title = this.I18n.t('js.modals.destroy_work_package.title', { label: this.workPackageLabel }); - this.text.bulk_text = this.I18n.t('js.modals.destroy_work_package.bulk_text', { - label: this.workPackageLabel, - count: this.workPackages.length, - }); - - this.text.childCount = (wp:WorkPackageResource) => { - const count = this.children(wp).length; - return this.I18n.t('js.units.child_work_packages', { count }); - }; - - this.text.hasChildren = (wp:WorkPackageResource) => { - const childUnits = this.text.childCount(wp); - return this.I18n.t('js.modals.destroy_work_package.has_children', { childUnits }); - }; - this.text.deletesChildren = this.I18n.t('js.modals.destroy_work_package.deletes_children'); - } - - public get blockedDueToUnconfirmedChildren():boolean { - return this.mustConfirmChildren && !this.childrenDeletionConfirmed; - } - - public get mustConfirmChildren():boolean { - let result = false; - - if (this.singleWorkPackage && this.singleWorkPackageChildren) { - result = this.singleWorkPackageChildren.length > 0; - } - - return result || !!_.find(this.workPackages, (wp) => wp.children && wp.children.length > 0); - } - - public confirmDeletion($event:Event):boolean { - if (this.busy || this.blockedDueToUnconfirmedChildren) { - return false; - } - - this.busy = true; - const ids = this.workPackages - .map((el) => el.id) - .filter((id) => id !== null); - this.workPackageService.performBulkDelete(ids, true) - .then(() => { - this.busy = false; - this.closeMe($event); - this.wpTableFocus.clear('Clearing after destroying work packages'); - if (this.$state.current.data?.baseRoute) { - this.backRoutingService.goBack(true); - } else { - const projectIdentifier = this.currentProject.identifier; - window.location.href = this.pathHelper.workPackagesPath(projectIdentifier) + window.location.search; - } - }) - .catch(() => { - this.busy = false; - }); - - return false; - } - - public children(workPackage:WorkPackageResource) { - if (workPackage.hasOwnProperty('children')) { - return workPackage.children; - } - return []; - } -} 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/op-context-menu/wp-context-menu/wp-single-context-menu.ts b/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-single-context-menu.ts index 04b2972400d..f9124b4486e 100644 --- a/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-single-context-menu.ts +++ b/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-single-context-menu.ts @@ -12,18 +12,17 @@ import { OpContextMenuItem } from 'core-app/shared/components/op-context-menu/op import { PERMITTED_CONTEXT_MENU_ACTIONS, } from 'core-app/shared/components/op-context-menu/wp-context-menu/wp-static-context-menu-actions'; -import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { CopyToClipboardService } from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.service'; import { WorkPackageAction, } from 'core-app/features/work-packages/components/wp-table/context-menu-helper/wp-context-menu-helper.service'; -import { WpDestroyModalComponent } from 'core-app/shared/components/modals/wp-destroy-modal/wp-destroy.modal'; import { WorkPackageAuthorization } from 'core-app/features/work-packages/services/work-package-authorization.service'; import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service'; import { ApiV3Service } from 'core-app/core/apiv3/api-v3.service'; import { TimeEntryTimerService } from 'core-app/shared/components/time_entries/services/time-entry-timer.service'; import { TimeEntryResource } from 'core-app/features/hal/resources/time-entry-resource'; import { DeviceService } from 'core-app/core/browser/device.service'; +import { CurrentProjectService } from 'core-app/core/current-project/current-project.service'; @Directive({ // eslint-disable-next-line @angular-eslint/directive-selector @@ -41,10 +40,10 @@ export class WorkPackageSingleContextMenuDirective extends OpContextMenuTrigger readonly injector = inject(Injector); readonly PathHelper = inject(PathHelperService); readonly elementRef = inject(ElementRef); - readonly opModalService = inject(OpModalService); readonly turboRequests = inject(TurboRequestsService); readonly apiV3Service = inject(ApiV3Service); readonly authorisationService = inject(AuthorisationService); + readonly currentProject = inject(CurrentProjectService); readonly timeEntryService = inject(TimeEntryTimerService); protected copyToClipboardService = inject(CopyToClipboardService); protected deviceService = inject(DeviceService); @@ -96,9 +95,18 @@ export class WorkPackageSingleContextMenuDirective extends OpContextMenuTrigger window.location.href = `${this.PathHelper.workPackageCopyPath(this.workPackage.project.identifier, this.workPackage.id)}`; } break; - case 'delete': - this.opModalService.show(WpDestroyModalComponent, this.injector, { workPackages: [this.workPackage] }); + case 'delete': { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + const currentBaseRoute = this.$state.current.data?.baseRoute as string | undefined; + const backUrl = currentBaseRoute + ? this.$state.href(currentBaseRoute) + : this.PathHelper.workPackagesPath(this.currentProject.identifier ?? null); + void this.turboRequests.request( + this.PathHelper.workPackagesBulkDeleteDialogPath([this.workPackage.id!], backUrl), + { method: 'GET' }, + ); break; + } case 'log_time': void this.turboRequests.request(this.PathHelper.timeEntryWorkPackageDialog(this.workPackage.id!), { method: 'GET' }); break; diff --git a/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive.ts b/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive.ts index f4e6953a8fe..e3cf08e3a50 100644 --- a/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive.ts +++ b/frontend/src/app/shared/components/op-context-menu/wp-context-menu/wp-view-context-menu.directive.ts @@ -20,12 +20,10 @@ import { import { PERMITTED_CONTEXT_MENU_ACTIONS, } from 'core-app/shared/components/op-context-menu/wp-context-menu/wp-static-context-menu-actions'; -import { OpModalService } from 'core-app/shared/components/modal/modal.service'; import { StateService } from '@uirouter/core'; import { InjectField } from 'core-app/shared/helpers/angular/inject-field.decorator'; import { CopyToClipboardService } from 'core-app/shared/components/copy-to-clipboard/copy-to-clipboard.service'; import { splitViewRoute } from 'core-app/features/work-packages/routing/split-view-routes.helper'; -import { WpDestroyModalComponent } from 'core-app/shared/components/modals/wp-destroy-modal/wp-destroy.modal'; import isNewResource from 'core-app/features/hal/helpers/is-new-resource'; import { PathHelperService } from 'core-app/core/path-helper/path-helper.service'; import { TurboRequestsService } from 'core-app/core/turbo/turbo-requests.service'; @@ -40,8 +38,6 @@ export class WorkPackageViewContextMenu extends OpContextMenuHandler { @InjectField() protected wpRelationsHierarchyService:WorkPackageRelationsHierarchyService; - @InjectField() protected opModalService:OpModalService; - @InjectField() protected $state!:StateService; @InjectField() protected wpTableSelection:WorkPackageViewSelectionService; @@ -152,7 +148,9 @@ export class WorkPackageViewContextMenu extends OpContextMenuHandler { private deleteSelectedWorkPackages() { const selected = this.getSelectedWorkPackages(); - this.opModalService.show(WpDestroyModalComponent, this.injector, { workPackages: selected }); + const ids = selected.map((wp) => wp.id).filter((id) => id !== null); + const backUrl = this.$state.href(this.baseRoute as string) || this.pathHelper.workPackagesPath(this.currentProject.identifier ?? null); + void this.turboRequests.request(this.pathHelper.workPackagesBulkDeleteDialogPath(ids, backUrl), { method: 'GET' }); } private editSelectedWorkPackages(link:any) { 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/_dialogues.sass b/frontend/src/assets/sass/backlogs/_dialogues.sass deleted file mode 100644 index 4d05872a31c..00000000000 --- a/frontend/src/assets/sass/backlogs/_dialogues.sass +++ /dev/null @@ -1,9 +0,0 @@ -/* Hide the close button since we do no longer include the necessary image for the close icon -.controller-rb_master_backlogs, -.controller-rb_taskboards - .ui-dialog-titlebar-close - display: none - - .ui-dialog - background: var(--body-background) - border: 1px solid var(--borderColor-default) diff --git a/frontend/src/assets/sass/backlogs/_global.css b/frontend/src/assets/sass/backlogs/_global.css deleted file mode 100644 index 5e3cc7d66ba..00000000000 --- a/frontend/src/assets/sass/backlogs/_global.css +++ /dev/null @@ -1,66 +0,0 @@ -/*-- copyright -OpenProject Backlogs Plugin - -Copyright (C) the OpenProject GmbH -Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda -Copyright (C)2010-2011 friflaj -Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns -Copyright (C)2009-2010 Mark Maglana -Copyright (C)2009 Joe Heck, Nate Lowrie - -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 Backlogs is a derivative work based on ChiliProject Backlogs. -The copyright follows: -Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj -Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany - -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. - -++*/ - -#rb .meta { - display:none; -} -#rb #helpers { - display:none; -} -/* - .editor is the classname for field editors of sprint, - story, task, impediment. These field editors get created - at runtime whenever any of the above models are edited. -*/ -#rb .editors { - display:none; -} -#rb .ui-dialog .editor { - display:block; -} - -/* dialog */ -.ui-dialog .ui-dialog-title { float:left; margin-right:0; } -.ui-dialog.ui-widget-content { border:none; } -.ui-dialog .ui-dialog-buttonpane.ui-widget-content { border:none; } - -.subject-input { - width: 99%; -} - -th { - font-weight: var(--base-text-weight-bold); -} diff --git a/frontend/src/assets/sass/backlogs/_global_print.css b/frontend/src/assets/sass/backlogs/_global_print.css deleted file mode 100644 index 20931d7bd72..00000000000 --- a/frontend/src/assets/sass/backlogs/_global_print.css +++ /dev/null @@ -1,39 +0,0 @@ -/*-- copyright -OpenProject Backlogs Plugin - -Copyright (C) the OpenProject GmbH -Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda -Copyright (C)2010-2011 friflaj -Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns -Copyright (C)2009-2010 Mark Maglana -Copyright (C)2009 Joe Heck, Nate Lowrie - -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 Backlogs is a derivative work based on ChiliProject Backlogs. -The copyright follows: -Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj -Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany - -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. - -++*/ - -#toolbar .links{ - display:none !important; -} diff --git a/frontend/src/assets/sass/backlogs/_index.sass b/frontend/src/assets/sass/backlogs/_index.sass index af0bd069623..bfd07ffb393 100644 --- a/frontend/src/assets/sass/backlogs/_index.sass +++ b/frontend/src/assets/sass/backlogs/_index.sass @@ -39,10 +39,4 @@ @import "../../../global_styles/openproject/_variables.sass" @import "../../../global_styles/openproject/_mixins.sass" -@import global -@import global_print -@import jqplot -@import statistics @import master_backlog -@import taskboard -@import dialogues diff --git a/frontend/src/assets/sass/backlogs/_jqplot.css b/frontend/src/assets/sass/backlogs/_jqplot.css deleted file mode 100644 index d9467564c3a..00000000000 --- a/frontend/src/assets/sass/backlogs/_jqplot.css +++ /dev/null @@ -1,185 +0,0 @@ -/*-- copyright -OpenProject Backlogs Plugin - -Copyright (C) the OpenProject GmbH -Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda -Copyright (C)2010-2011 friflaj -Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns -Copyright (C)2009-2010 Mark Maglana -Copyright (C)2009 Joe Heck, Nate Lowrie - -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 Backlogs is a derivative work based on ChiliProject Backlogs. -The copyright follows: -Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj -Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany - -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. - -++*/ - -.jqplot-target { - position: relative; - color: #666; - font-family: "Trebuchet MS", Arial, Helvetica, sans-serif; - font-size: 1em; -} - -.jqplot-axis { - font-size: .75em; -} - -.jqplot-xaxis { - margin-top: 10px; -} - -.jqplot-x2axis { - margin-bottom: 10px; -} - -.jqplot-yaxis { - margin-right: 10px; -} - -.jqplot-y2axis, .jqplot-y3axis, .jqplot-y4axis, .jqplot-y5axis, .jqplot-y6axis, .jqplot-y7axis, .jqplot-y8axis, .jqplot-y9axis { - margin-left: 10px; - margin-right: 10px; -} - -.jqplot-axis-tick, .jqplot-xaxis-tick, .jqplot-yaxis-tick, .jqplot-x2axis-tick, .jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { - position: absolute; -} - -.jqplot-xaxis-tick { - top: 0; - left: 15px; - vertical-align: top; -} - -.jqplot-x2axis-tick { - bottom: 0; - left: 15px; - vertical-align: bottom; -} - -.jqplot-yaxis-tick { - right: 0; - top: 15px; - text-align: right; -} - -.jqplot-y2axis-tick, .jqplot-y3axis-tick, .jqplot-y4axis-tick, .jqplot-y5axis-tick, .jqplot-y6axis-tick, .jqplot-y7axis-tick, .jqplot-y8axis-tick, .jqplot-y9axis-tick { - left: 0; - top: 15px; - text-align: left; -} - -.jqplot-xaxis-label { - margin-top: 10px; - font-size: 11pt; - position: absolute; -} - -.jqplot-x2axis-label { - margin-bottom: 10px; - font-size: 11pt; - position: absolute; -} - -.jqplot-yaxis-label { - margin-right: 10px; - font-size: 11pt; - position: absolute; -} - -.jqplot-y2axis-label, .jqplot-y3axis-label, .jqplot-y4axis-label, .jqplot-y5axis-label, .jqplot-y6axis-label, .jqplot-y7axis-label, .jqplot-y8axis-label, .jqplot-y9axis-label { - font-size: 11pt; - position: absolute; -} - -table.jqplot-table-legend, table.jqplot-cursor-legend { - background-color: rgba(255, 255, 255, 0.6); - border: 1px solid #ccc; - position: absolute; - font-size: .75em; -} - -td.jqplot-table-legend { - vertical-align: middle; -} - -td.jqplot-table-legend > div { - border: 1px solid #ccc; - padding: .2em; -} - -div.jqplot-table-legend-swatch { - width: 0; - height: 0; - border-top-width: .35em; - border-bottom-width: .35em; - border-left-width: .6em; - border-right-width: .6em; - border-top-style: solid; - border-bottom-style: solid; - border-left-style: solid; - border-right-style: solid; -} - -.jqplot-title { - top: 0; - left: 0; - padding-bottom: .5em; - font-size: 1.2em; -} - -table.jqplot-cursor-tooltip { - border: 1px solid #ccc; - font-size: .75em; -} - -.jqplot-cursor-tooltip { - border: 1px solid #ccc; - font-size: .75em; - white-space: nowrap; - background: rgba(208, 208, 208, 0.5); - padding: 1px; -} - -.jqplot-highlighter-tooltip { - border: 1px solid #ccc; - font-size: .75em; - white-space: nowrap; - background: rgba(208, 208, 208, 0.5); - padding: 1px; -} - -.jqplot-point-label { - font-size: .75em; -} - -td.jqplot-cursor-legend-swatch { - vertical-align: middle; - text-align: center; -} - -div.jqplot-cursor-legend-swatch { - width: 1.2em; - height: .7em; -} 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/assets/sass/backlogs/_statistics.css b/frontend/src/assets/sass/backlogs/_statistics.css deleted file mode 100644 index de64988edaf..00000000000 --- a/frontend/src/assets/sass/backlogs/_statistics.css +++ /dev/null @@ -1,48 +0,0 @@ -/*-- copyright -OpenProject Backlogs Plugin - -Copyright (C) the OpenProject GmbH -Copyright (C)2011 Stephan Eckardt, Tim Felgentreff, Marnen Laibow-Koser, Sandro Munda -Copyright (C)2010-2011 friflaj -Copyright (C)2010 Maxime Guilbot, Andrew Vit, Joakim Kolsjö, ibussieres, Daniel Passos, Jason Vasquez, jpic, Emiliano Heyns -Copyright (C)2009-2010 Mark Maglana -Copyright (C)2009 Joe Heck, Nate Lowrie - -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 Backlogs is a derivative work based on ChiliProject Backlogs. -The copyright follows: -Copyright (C) 2010-2011 - Emiliano Heyns, Mark Maglana, friflaj -Copyright (C) 2011 - Jens Ulferts, Gregor Schmidt - Finn GmbH - Berlin, Germany - -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. - -++*/ - -.score { text-align: center; width: 1.5em; font-size: large; display: inline-block; } -.score_0 { background-color: #FF0000; } -.score_1 { background-color: #FF5300; } -.score_2 { background-color: #FF8100; } -.score_3 { background-color: #FFA100; } -.score_4 { background-color: #FFBB00; } -.score_5 { background-color: #FFD300; } -.score_6 { background-color: #FFEC00; } -.score_7 { background-color: #E9FB00; } -.score_8 { background-color: #B1F100; } -.score_9 { background-color: #74E600; } -.score_10 { background-color: #00CC00; } diff --git a/frontend/src/assets/sass/backlogs/_taskboard.sass b/frontend/src/assets/sass/backlogs/_taskboard.sass deleted file mode 100644 index 733973ed520..00000000000 --- a/frontend/src/assets/sass/backlogs/_taskboard.sass +++ /dev/null @@ -1,329 +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. ++ - */ - -@mixin story-header - background-color: #FFFFFF - font-size: 1rem - rem-calc(5px) - opacity: 0.8 - filter: alpha(opacity = 80) - overflow: hidden - padding-bottom: 1px - padding-right: 3px - -@mixin story-footer - float: left - font-size: 1rem - rem-calc(5px) - width: 85% - margin-top: 4px - padding: 2px - padding-top: 0 - -@mixin ellipsis - overflow: hidden - white-space: nowrap - text-overflow: ellipsis - -#rb .task - color: #484848 - line-height: inherit - white-space: inherit - -#rb #taskboard - overflow-x: auto - #assigned_to_id_options - display: none - .swimlane - min-width: 107px - /* width + (2*margin) + (2*padding) of .work_package + (2*border) of cell */ - padding: 5px - width: 107px - /* Must be the same as min-width */ - #board_header - background-color: var(--body-background) - color: var(--body-font-color) - border: 1px solid var(--borderColor-default) - margin-bottom: 0 - margin-right: 10px - td - border-right: 1px dotted var(--borderColor-default) - font-weight: var(--base-text-weight-bold) - text-align: center - vertical-align: middle - padding-top: 0 - padding-bottom: 0 - line-height: 30px - &:first-child - min-width: 241px - width: 241px - - .board - background-color: var(--overlay-bgColor) - border: 1px solid var(--borderColor-default) - border-top: none - margin-right: 10px - /* IE7 table fix */ - table-layout: fixed - border-collapse: collapse - empty-cells: show - tr:hover - background-color: var(--control-transparent-bgColor-hover) - td - border-right: 1px dotted #CFCFCF - border-bottom: 1px dotted #CFCFCF - vertical-align: top - &:first-child - min-width: 210px - padding: 5px - width: 210px - tr:last-child td - border-bottom: none - .add_new - margin: 0 - min-width: 30px - padding: 0 - text-align: center - vertical-align: middle - width: 30px - &.clickable:hover - cursor: pointer - background-color: var(--highlight-neutral-bgColor) - .story, .label_sprint_impediments - background-color: var(--display-lemon-bgColor-muted) - color: var(--fgColor-muted) - border: none - display: block - min-height: 100px - margin: 5px - padding: 5px - position: relative - width: 190px - .story - .subject - height: 42px - line-height: 13px - margin-top: 0 - overflow: hidden - padding: 2px - width: 180px - &.closed .subject - text-decoration: line-through - .work_package, .placeholder - background-color: #AFAFAF - color: var(--color-ansi-black) - border: none - cursor: move - display: block - font-size: 10px - height: 85px - padding: 5px - margin: 5px 0px - position: relative - width: 85px - .work_package.prevent_edit - cursor: default - .placeholder - background-color: #FFFF00 - border: 1px dashed #333300 - height: 78px - width: 83px - .work_package - &.closed .subject.editable - text-decoration: line-through - .v - display: none - .remaining_hours.editable - border: 2px solid #FFFFFF - background-color: #EE0000 - bottom: -5px - color: #FFFFFF - font-size: 9px - height: 18px - padding-left: 5px - padding-right: 5px - position: absolute - right: -5px - .blocks, .remaining_hours.editable.empty - display: none - .indicator - display: none - - &.error .indicator - background: none - border: none - - &.error.icon-bug:before - position: absolute - top: 30px - left: 28px - color: red - - - .editors - display: none - -/* - * swimlane class is used by: - * - #board_header - * - .board - * - * Also use by the Column Width preference to determine the unit width of the - * swimlanes. See RB.Taskboard.initialize() - -/* status labels */ - -/* shared #impediments and #tasks */ - -/* item styles used by .task and .impediment */ - -/* dialog */ - -.task_editor_dialog.ui-dialog - .ui-widget-header - background-color: var(--bgColor-muted) - filter: alpha(opacity = 50) - .ui-dialog-title - float: right - margin-right: 0 - color: var(--body-font-color) - &.ui-widget-content - background: none - border: none - .ui-dialog-buttonpane.ui-widget-content - background: none - background-color: none - border: none - -.dark - #task_editor label, .subject, .assigned_to_id, div - color: #FFFFFF - option - color: var(--body-font-color) - -.light - #task_editor label, .subject, .assigned_to_id, div - color: var(--body-font-color) - -/* item editor */ - -#task_editor - label:first-letter - text-transform: capitalize - label - display: block - font-size: 11px - width: 100% - .editor - font-size: 11px - margin-bottom: 10px - width: 100% - .subject - height: 65px - width: 272px - .remaining_hours, .blocks - width: 268px - -/* compact view */ - -#rb - .compact - .story, .label_sprint_impediments - height: 15px - .story .subject - display: none - .work_package - height: 21px - padding: 0 - width: 21px - * - display: none - .placeholder - background-color: #FFFF00 - border: 1px dashed #333300 - height: 19px - width: 19px - #impediment_template, #task_template - display: none - -/* others */ - -.story - .story-bar - @include story-header - text-align: right - width: 180px - clear: both - .id - float: right - .status - float: left - -.story, -.label_sprint_impediments - font-size: 1rem - rem-calc(3px) - -.work_package - .id - @include story-header - text-align: right - width: 75px - a - opacity: 1.0 - filter: alpha(opacity = 100) - .editable:hover - background-color: transparent - .subject.editable - font-size: 1rem - rem-calc(3px) - height: 42px - line-height: 13px - margin-top: 0 - overflow: hidden - padding: 2px - width: 81px - -.story - .story-footer - .assigned_to_id - @include story-footer - @include ellipsis - .story-points - margin-top: 2px - float: right -.work_package - .assigned_to_id.editable - @include story-footer - .t - @include ellipsis - -/* Toolbar modifications (no support for labels form the component) */ - -#toolbar - label[for=col_width_input] - padding-top: rem-calc(20px) - - #col_width_input - max-width: 60px 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/stimulus/controllers/dynamic/backlogs/work_package.ts b/frontend/src/global_styles/content/_admin_groups_tree_layout.sass similarity index 66% rename from frontend/src/stimulus/controllers/dynamic/backlogs/work_package.ts rename to frontend/src/global_styles/content/_admin_groups_tree_layout.sass index f10d3b67a65..4cdd17399ac 100644 --- a/frontend/src/stimulus/controllers/dynamic/backlogs/work_package.ts +++ b/frontend/src/global_styles/content/_admin_groups_tree_layout.sass @@ -25,33 +25,19 @@ // // See COPYRIGHT and LICENSE files for more details. //++ +@import ../openproject/mixins -/************************************** - WORK PACKAGE -***************************************/ -// @ts-expect-error TS(2304): Cannot find name 'RB'. -RB.WorkPackage = (function ($) { - // @ts-expect-error TS(2304): Cannot find name 'RB'. - return RB.Object.create(RB.Model, { +body:has(.admin-groups-tree-page) + @include extended-content--bottom + #content-body + display: flex + flex-direction: column - initialize(el:any) { - this.$ = $(el); - this.el = el; - }, +.admin-groups-tree-page + &--sidebar + border-top-left-radius: var(--borderRadius-medium) + border-top-right-radius: var(--borderRadius-medium) - beforeSaveDragResult() { - // Do nothing - }, - - getType() { - return 'WorkPackage'; - }, - - saveDragResult() { - this.beforeSaveDragResult(); - if (!this.$.hasClass('editing')) { - this.saveEdits(); - } - }, - }); -}(jQuery)); + &--wrapper + height: 100% + overflow: hidden 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/content/_index.sass b/frontend/src/global_styles/content/_index.sass index 92e77dd3b79..5d7931874d8 100644 --- a/frontend/src/global_styles/content/_index.sass +++ b/frontend/src/global_styles/content/_index.sass @@ -76,6 +76,7 @@ @import activity_list @import activity_days @import hierachy_custom_field_layout +@import admin_groups_tree_layout @import text_utils @import menus/menu_blocks 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/openproject/_mixins.sass b/frontend/src/global_styles/openproject/_mixins.sass index 35c929d6139..8d088d8e981 100644 --- a/frontend/src/global_styles/openproject/_mixins.sass +++ b/frontend/src/global_styles/openproject/_mixins.sass @@ -275,18 +275,14 @@ $scrollbar-size: 10px @mixin macro--text-style @media screen display: inline - background: rgba(218,223,225,0.19) + background: var(--bgColor-muted) border: 1px solid transparent + border-radius: var(--borderRadius-medium) padding: 2px &:has(.-multiline) display: inline-flex - &:hover - cursor: default - border-color: var(--fgColor-muted) - background: rgba(218,223,225,0.75) - @mixin unset-button-styles padding: 0 margin: 0 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/backlogs-settings.controller.ts b/frontend/src/stimulus/controllers/dynamic/admin/backlogs-settings.controller.ts deleted file mode 100644 index 8482d0d0c32..00000000000 --- a/frontend/src/stimulus/controllers/dynamic/admin/backlogs-settings.controller.ts +++ /dev/null @@ -1,144 +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 { Controller } from '@hotwired/stimulus'; -import { - NgOption, - NgSelectComponent, -} from '@ng-select/ng-select'; - -/** - * Stimulus Controller adding behavior to Admin > Backlogs page. - * Ensures that story types and task types are mutually exclusive. - */ -export default class BacklogsSettings extends Controller { - static targets = ['storyTypes', 'taskType']; - - declare readonly storyTypesTarget:HTMLElement; - declare readonly taskTypeTarget:HTMLElement; - declare readonly hasStoryTypesTarget:boolean; - declare readonly hasTaskTypeTarget:boolean; - - private isUpdating = false; - - storyTypesTargetConnected(target:HTMLElement) { - target.addEventListener('change', this.onStoryTypesChanged); - } - - storyTypesTargetDisconnected(target:HTMLElement) { - target.removeEventListener('change', this.onStoryTypesChanged); - } - - taskTypeTargetConnected(target:HTMLElement) { - target.addEventListener('change', this.onTaskTypeChanged); - } - - taskTypeTargetDisconnected(target:HTMLElement) { - target.removeEventListener('change', this.onTaskTypeChanged); - } - - private onStoryTypesChanged = () => { - if (this.isUpdating || !this.hasTaskTypeTarget) return; - - this.syncDisabledOptions(this.storyTypesTarget, this.taskTypeTarget); - }; - - private onTaskTypeChanged = () => { - if (this.isUpdating || !this.hasStoryTypesTarget) return; - - this.syncDisabledOptions(this.taskTypeTarget, this.storyTypesTarget); - }; - - /** - * Syncs disabled options between two autocompleters. - * Selected values in the source autocompleter will be disabled in the target. - * - * @param sourceTarget The autocompleter whose selections should disable options in the target - * @param targetTarget The autocompleter whose options should be disabled - */ - private syncDisabledOptions(sourceTarget:HTMLElement, targetTarget:HTMLElement) { - this.isUpdating = true; - try { - const sourceNgSelect = this.getNgSelectComponent(sourceTarget); - const targetNgSelect = this.getNgSelectComponent(targetTarget); - - if (!sourceNgSelect || !targetNgSelect) { - return; - } - - this.syncAutocompleters(sourceNgSelect, targetNgSelect); - } finally { - this.isUpdating = false; - } - } - - /** - * Gets the NgSelectComponent instance from an op-autocompleter element. - */ - private getNgSelectComponent(target:HTMLElement):NgSelectComponent|null { - // Access the ng-select instance stored by op-autocompleter component - // eslint-disable-next-line @typescript-eslint/no-unsafe-return,@typescript-eslint/no-unsafe-member-access - return (target as any).ngSelectComponentInstance ?? null; - } - - /** - * Syncs two ng-select autocompleters - ensuring selections are mutually exclusive. - * - * @param source source autocompleter - * @param target target autocompleter - */ - private syncAutocompleters(source:NgSelectComponent, target:NgSelectComponent) { - const sourceSelectedIds = new Set( - source.selectedItems - .map((item) => item.value.id) - .filter((id) => id != null) - ); - - // Directly mutate the items array to ensure ng-select updates properly - let hasChanges = false; - target.itemsList.items.forEach((targetItem:NgOption) => { - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - const itemId = targetItem.value?.id; - - if (!itemId) return; - - const shouldBeDisabled = sourceSelectedIds.has(itemId); - if (targetItem.disabled !== shouldBeDisabled) { - targetItem.disabled = shouldBeDisabled; - hasChanges = true; - } - }); - - // Force ng-select to re-render if we made changes - if (hasChanges) { - target.detectChanges(); - } - } -} 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/backlogs/common.ts b/frontend/src/stimulus/controllers/dynamic/backlogs/common.ts deleted file mode 100644 index 12937fef187..00000000000 --- a/frontend/src/stimulus/controllers/dynamic/backlogs/common.ts +++ /dev/null @@ -1,140 +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 'jquery.cookie'; - -// @ts-expect-error TS(2339): Property 'RB' does not exist on type 'Window & typ... Remove this comment to see the full error message -if (window.RB === null || window.RB === undefined) { - // @ts-expect-error TS(2339): Property 'RB' does not exist on type 'Window & typ... Remove this comment to see the full error message - window.RB = {}; -} - -(function ($) { - let object:any; - let Factory; - let Dialog; - let UserPreferences; - - object = { - // Douglas Crockford's technique for object extension - // http://javascript.crockford.com/prototypal.html - create() { - let obj; - let i; - let methods; - let methodName; - - function F() { - } - - F.prototype = arguments[0]; - // @ts-expect-error TS(7009): 'new' expression, whose target lacks a construct s... Remove this comment to see the full error message - obj = new F(); - - // Add all the other arguments as mixins that - // 'write over' any existing methods - for (i = 1; i < arguments.length; i += 1) { - methods = arguments[i]; - if (typeof methods === 'object') { - for (methodName in methods) { - if (methods.hasOwnProperty(methodName)) { - obj[methodName] = methods[methodName]; - } - } - } - } - return obj; - }, - }; - - // Object factory for chiliproject_backlogs - Factory = object.create({ - - initialize(objType:any, el:any) { - let obj; - - obj = object.create(objType); - obj.initialize(el); - return obj; - }, - - }); - - // Utilities - Dialog = object.create({ - msg(msg:any) { - let dialog; - let baseClasses; - - baseClasses = 'ui-button ui-widget ui-state-default ui-corner-all'; - - if ($('#msgBox').length === 0) { - dialog = $('
    ').appendTo('body'); - } else { - dialog = $('#msgBox'); - } - - dialog.html(msg); - dialog.dialog({ - title: 'Backlogs Plugin', - buttons: [ - { - text: 'OK', - class: 'button -primary', - click() { - $(this).dialog('close'); - }, - }], - modal: true, - }); - $('.button').removeClass(baseClasses); - $('.ui-icon-closethick').prop('title', 'close'); - }, - }); - - // Abstract the user preference from the rest of the RB objects - // so that we can change the underlying implementation as needed - UserPreferences = object.create({ - get(key:any) { - return $.cookie(key); - }, - - set(key:any, value:any) { - $.cookie(key, value, { expires: 365 * 10 }); - }, - }); - - // @ts-expect-error TS(2304): Cannot find name 'RB'. - RB.Object = object; - // @ts-expect-error TS(2304): Cannot find name 'RB'. - RB.Factory = Factory; - // @ts-expect-error TS(2304): Cannot find name 'RB'. - RB.Dialog = Dialog; - // @ts-expect-error TS(2304): Cannot find name 'RB'. - RB.UserPreferences = UserPreferences; -}(jQuery)); diff --git a/frontend/src/stimulus/controllers/dynamic/backlogs/editable_inplace.ts b/frontend/src/stimulus/controllers/dynamic/backlogs/editable_inplace.ts deleted file mode 100644 index 22c947f995b..00000000000 --- a/frontend/src/stimulus/controllers/dynamic/backlogs/editable_inplace.ts +++ /dev/null @@ -1,66 +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. -//++ - -// @ts-expect-error TS(2304): Cannot find name 'RB'. -RB.EditableInplace = (function ($) { - // @ts-expect-error TS(2304): Cannot find name 'RB'. - return RB.Object.create(RB.Model, { - - displayEditor(editor:any) { - this.$.addClass('editing'); - editor.find('.editor').bind('keydown', this.handleKeydown); - }, - - getEditor() { - // Create the model editor container if it does not yet exist - let editor = this.$.children('.editors'); - - if (editor.length === 0) { - editor = $("
    ").appendTo(this.$); - } else if (!editor.hasClass('permanent')) { - editor.first().html(''); - } - return editor; - }, - - // For detecting Enter and ESC - handleKeydown(e:any) { - let j; - let that; - - j = $(this).parents('.model').first(); - that = j.data('this'); - - if (e.key === 'Enter') { - that.saveEdits(); - } else if (e.key === 'Escape') { - that.cancelEdit(); - } - }, - }); -}(jQuery)); diff --git a/frontend/src/stimulus/controllers/dynamic/backlogs/impediment.ts b/frontend/src/stimulus/controllers/dynamic/backlogs/impediment.ts deleted file mode 100644 index 9393425dafb..00000000000 --- a/frontend/src/stimulus/controllers/dynamic/backlogs/impediment.ts +++ /dev/null @@ -1,91 +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. -//++ - -/************************************** - IMPEDIMENT -***************************************/ - -// @ts-expect-error TS(2304): Cannot find name 'RB'. -RB.Impediment = (function ($) { - // @ts-expect-error TS(2304): Cannot find name 'RB'. - return RB.Object.create(RB.Task, { - - initialize(el:any) { - let j; // This ensures that we use a local 'j' variable, not a global one. - - this.$ = j = $(el); - this.el = el; - - j.addClass('impediment'); // If node is based on #task_template, it doesn't have the impediment class yet - - // Associate this object with the element for later retrieval - j.data('this', this); - - j.on('mouseup', '.editable', this.handleClick); - }, - - // Override saveDirectives of RB.Task - saveDirectives() { - let j; - let prev; - let statusID; - - let method; - let url; - let data; - - j = this.$; - prev = this.$.prev(); - statusID = j.parent('td').first().attr('id').split('_')[1]; - - data = `${j.find('.editor').serialize() - }&is_impediment=true` - // @ts-expect-error TS(2304): Cannot find name 'RB'. - + `&version_id=${RB.constants.sprint_id - }&status_id=${statusID - }&prev=${prev.length === 1 ? prev.data('this').getID() : '' - }${this.isNew() ? '' : `&id=${j.children('.id').text()}`}`; - - if (this.isNew()) { - // @ts-expect-error TS(2304): Cannot find name 'RB'. - url = RB.urlFor('create_impediment', { sprint_id: RB.constants.sprint_id }); - method = 'post'; - } else { - // @ts-expect-error TS(2304): Cannot find name 'RB'. - url = RB.urlFor('update_impediment', { id: this.getID(), sprint_id: RB.constants.sprint_id }); - method = 'put'; - } - - return { - url, - method, - data, - }; - }, - }); -}(jQuery)); diff --git a/frontend/src/stimulus/controllers/dynamic/backlogs/model.ts b/frontend/src/stimulus/controllers/dynamic/backlogs/model.ts deleted file mode 100644 index b77e59ad580..00000000000 --- a/frontend/src/stimulus/controllers/dynamic/backlogs/model.ts +++ /dev/null @@ -1,486 +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 { FetchRequest, FetchResponse } from '@rails/request.js'; - -/*************************************** - MODEL - Common methods for sprint, work_package, - story, task, and impediment -***************************************/ - -// @ts-expect-error TS(2304): Cannot find name 'RB'. -RB.Model = (function ($) { - // @ts-expect-error TS(2304): Cannot find name 'RB'. - return RB.Object.create({ - - initialize(el:any) { - this.$ = $(el); - this.el = el; - }, - - afterCreate(data:string, response:FetchResponse) { - // Do nothing. Child objects may optionally override this - }, - - afterSave(data:string, response:FetchResponse) { - let isNew; - let result; - - isNew = this.isNew(); - // @ts-expect-error TS(2304): Cannot find name 'RB'. - result = RB.Factory.initialize(RB.Model, data); - - this.unmarkSaving(); - this.refresh(result); - - if (isNew) { - const id = result.$.filter('.model').attr('id'); - this.$.attr('id', id); - - this.afterCreate(data, response); - } else { - this.afterUpdate(data, response); - } - }, - - afterUpdate(data:string, response:FetchResponse) { - // Do nothing. Child objects may optionally override this - }, - - beforeSave() { - // Do nothing. Child objects may or may not override this method - }, - - cancelEdit() { - this.endEdit(); - if (this.isNew()) { - this.$.hide('blind'); - } - }, - - close() { - this.$.addClass('closed'); - }, - - copyFromDialog() { - let editors; - - if (this.$.find('.editors').length === 0) { - editors = $("
    ").appendTo(this.$); - } else { - editors = this.$.find('.editors').first(); - } - editors.html(''); - editors.append($(`#${this.getType().toLowerCase()}_editor`).children('.editor')); - this.saveEdits(); - }, - - displayEditor(editor:any) { - const self = this; - let baseClasses; - - baseClasses = 'ui-button ui-widget ui-state-default ui-corner-all'; - - editor.dialog({ - buttons: [ - { - text: 'OK', - class: 'button -primary', - click() { - self.copyFromDialog(); - $(this).dialog('close'); - }, - }, - { - text: 'Cancel', - class: 'button', - click() { - self.cancelEdit(); - $(this).dialog('close'); - }, - }, - ], - close(e:any, ui:any) { - if (e.type === 'click' || (e.type === 'keydown' && e.key === 'Escape')) { - self.cancelEdit(); - } - }, - dialogClass: `${this.getType().toLowerCase()}_editor_dialog`, - modal: true, - position: { my: 'center', at: 'center', of: window }, - resizable: false, - title: (this.isNew() ? this.newDialogTitle() : this.editDialogTitle()), - }); - editor.find('.editor').first().focus(); - $('.button').removeClass(baseClasses); - $('.ui-icon-closethick').prop('title', 'close'); - }, - - edit() { - const editor = this.getEditor(); - const self = this; - let maxTabIndex = 0; - - $('.stories .editors .editor').each(function (index) { - let value; - - // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message - value = parseInt($(this).attr('tabindex'), 10); - - if (maxTabIndex < value) { - maxTabIndex = value; - } - }); - - if (!editor.hasClass('permanent')) { - this.$.find('.editable').each(function (this:any, index:any) { - const field = $(this); - const fieldId = field.attr('field_id'); - const fieldName = field.attr('fieldname'); - const fieldLabel = field.attr('fieldlabel'); - // @ts-expect-error TS(2345): Argument of type 'string | undefined' is not assig... Remove this comment to see the full error message - const fieldOrder = parseInt(field.attr('fieldorder'), 10); - const fieldEditable = field.attr('fieldeditable') || 'true'; - const fieldType = field.attr('fieldtype') || 'input'; - let typeId; - let statusId; - let input:any; - - if (fieldType === 'select') { - // Special handling for status_id => they are dependent of type_id - if (fieldName === 'status_id') { - typeId = $.trim(self.$.find('.type_id .v').html()); - // when creating stories we need to query the select directly - if (typeId === '') { - typeId = $('#type_id_options').val(); - } - statusId = $.trim(self.$.find('.status_id .v').html()); - input = self.findFactory(typeId, statusId, fieldName); - } else if (fieldName === 'type_id') { - input = $(`#${fieldName}_options`).clone(true); - // if the type changes the status dropdown has to be modified - input.change(function () { - // @ts-expect-error TS(2683): 'this' implicitly has type 'any' because it does n... Remove this comment to see the full error message - typeId = $(this).val(); - statusId = $.trim(self.$.find('.status_id .v').html()); - let newInput = self.findFactory(typeId, statusId, 'status_id'); - newInput = self.prepareInputFromFactory(newInput, fieldId, 'status_id', fieldOrder, maxTabIndex); - // @ts-expect-error TS(2683): 'this' implicitly has type 'any' because it does n... Remove this comment to see the full error message - newInput = self.replaceStatusForNewType(input, newInput, $(this).parent().find('.status_id').val(), editor); - }); - } else { - input = $(`#${fieldName}_options`).clone(true); - } - } else { - input = $(document.createElement(fieldType)); - } - - input = self.prepareInputFromFactory(input, fieldId, fieldName, fieldOrder, maxTabIndex, fieldEditable); - - // Copy the value in the field to the input element - input.val(fieldType === 'select' ? field.children('.v').first().text() : field.text()); - - // Record in the model's root element which input field had the last focus. We will - // use this information inside RB.Model.refresh() to determine where to return the - // focus after the element has been refreshed with info from the server. - input.focus(function (this:any) { - self.$.data('focus', $(this).attr('name')); - }); - - input.blur(() => { - self.$.data('focus', ''); - }); - - $('